Python Pyspark从现有数组列创建特定长度的数组列
我有一个类似pyspark的数据帧Python Pyspark从现有数组列创建特定长度的数组列,python,apache-spark,pyspark,apache-spark-sql,Python,Apache Spark,Pyspark,Apache Spark Sql,我有一个类似pyspark的数据帧 +-----+----+------------+------------+-------------+------------+ | Name| Age| P_Attribute|S_Attributes|P_Values |S_values | +-----+----+------------+------------+-------------+------------+ | Bob1| 16 | [x1,x2] | [x1
+-----+----+------------+------------+-------------+------------+
| Name| Age| P_Attribute|S_Attributes|P_Values |S_values |
+-----+----+------------+------------+-------------+------------+
| Bob1| 16 | [x1,x2] | [x1,x3]|["ab",1] | [1,2] |
| Bob2| 16 |[x1,x2,x3] | [] |["a","b","c"]| [] |
+-----+----+------------+------------+-------------+------------+
我想最终创建df,如下所示
+-----+----+------------+------------+
| Name| Age| Attribute | Values|
+-----+----+------------+------------+
| Bob1| 16 | x1 | ab |
| Bob1| 16 | x2 | 1 |
| Bob1| 16 | x1 | 1 |
| Bob1| 16 | x3 | 2 |
| Bob2| 16 | x1 | a |
| Bob2| 16 | x2 | b |
| Bob2| 16 | x3 | c |
+-----+----+------------+------------+
基本上我想合并这两列并将它们分解成行。在pyspark数组函数的帮助下,我能够对数组进行concat和explode操作,但后来可以识别专业属性和运动属性之间的差异,因为它们可以有相同的名称。我还需要一个类型列
+-----+----+------------+------------+------------+
| Name| Age| Attribute| type |Value |
+-----+----+------------+------------+------------+
| Bob1| 16 | x1 | 1 | ab |
| Bob1| 16 | x2 | 1 | 1 |
| Bob1| 16 | x1 | 2 | 1 |
| Bob1| 16 | x3 | 2 | 2 |
| Bob2| 16 | x1 | 1 | a |
| Bob2| 16 | x2 | 1 | b |
| Bob2| 16 | x3 | 1 | c |
+-----+----+------------+------------+------------+
所以我想先创建一个单独的数组列
+-----+----+------------+------------+------------+------------+
| Name| Age| P_Attribute|S_Attributes|P_type |S_type |
+-----+----+------------+------------+------------+------------+
| Bob1| 16 | [x1,x2] | [x1,x3]| [1,1] | [2,2] |
| Bob2| 16 |[x1,x2,x3] | [] | [1,1,1] | [] |
+-----+----+------------+------------+------------+------------+
这样我就可以合并列并使用所需类型的列进行分解,如上面的df所示。
问题是我无法动态创建P_类型和S_类型列。
我试过下面的代码
new_df = df.withColumn("temp_P_type", F.lit(1))\
.withColumn("P_type", F.array_repeat("temp_P_type",F.size("P_Attribute")))
这将抛出TypeError:列不可编辑错误。
若列的长度已经被提取为另一列,那个么它也不起作用。
有人能帮我吗?有没有更好的解决办法?作为df级别,不使用RDD和python函数(不使用UDF)是否可以做到这一点
另外,我正在使用spark 2.4,您可以执行以下操作。首先将P\u属性
和S\u属性
收集到一个attributes
列中,然后在其上执行posexplode
,这将根据需要给出引用属性源(P
或S
)的类型
列。最后,分解
属性
列以展平所有属性
import pyspark.sql.functions as f
df = spark.createDataFrame([
['Bob1', 16, ['x1', 'x2'], ['x1', 'x3']],
['Bob2', 16, ['x1', 'x2', 'x3'], []]],
['Name', 'Age', 'P_Attribute', 'S_Attributes'])
df.withColumn('Attributes', f.array('P_Attribute', 'S_Attributes'))\
.select('Name', 'Age', f.posexplode('Attributes').alias('type', 'Attribute'))\
.withColumn('Attribute', f.explode('Attribute'))\
.show()
+----+---+----+---------+
|Name|Age|type|Attribute|
+----+---+----+---------+
|Bob1| 16| 0| x1|
|Bob1| 16| 0| x2|
|Bob1| 16| 1| x1|
|Bob1| 16| 1| x3|
|Bob2| 16| 0| x1|
|Bob2| 16| 0| x2|
|Bob2| 16| 0| x3|
+----+---+----+---------+
我建议使用高阶函数
,使用
和
,然后分解一次
,然后使用*扩展选择两者。
df.show()
#+----+---+------------+------------+
#|Name|Age| P_Attribute|S_Attributes|
#+----+---+------------+------------+
#|Bob1| 16| [x1, x2]| [x1, x3]|
#|Bob2| 16|[x1, x2, x3]| []|
#+----+---+------------+------------+
from pyspark.sql import functions as F
df.withColumn("Attributes", F.explode(F.array_union(F.expr("""transform(P_Attribute,x-> struct(x as Attribute,1 as Type))"""),\
F.expr("""transform(S_Attributes,x-> struct(x as Attribute,2 as Type))"""))))\
.select("Name", "Age", "Attributes.*").show()
#+----+---+---------+----+
#|Name|Age|Attribute|Type|
#+----+---+---------+----+
#|Bob1| 16| x1| 1|
#|Bob1| 16| x2| 1|
#|Bob1| 16| x1| 2|
#|Bob1| 16| x3| 2|
#|Bob2| 16| x1| 1|
#|Bob2| 16| x2| 1|
#|Bob2| 16| x3| 1|
#+----+---+---------+----+
更新:
df.show()
#+----+---+------------+------------+---------+--------+
#|Name|Age| P_Attribute|S_Attributes| P_Values|S_values|
#+----+---+------------+------------+---------+--------+
#|Bob1| 16| [x1, x2]| [x1, x3]| [ab, 1]| [1, 2]|
#|Bob2| 16|[x1, x2, x3]| []|[a, b, c]| []|
#+----+---+------------+------------+---------+--------+
from pyspark.sql import functions as F
df.withColumn("Attributes", F.explode(F.array_union\
(F.expr("""transform(arrays_zip(P_Attribute,P_Values),x->\
struct(x.P_Attribute as Attribute,1 as Type,string(x.P_Values) as Value))"""),\
F.expr("""transform(arrays_zip(S_Attributes,S_Values),x->\
struct(x.S_Attributes as Attribute,2 as Type,string(x.S_Values) as Value))"""))))\
.select("Name", "Age", "Attributes.*").show()
#+----+---+---------+----+-----+
#|Name|Age|Attribute|Type|Value|
#+----+---+---------+----+-----+
#|Bob1| 16| x1| 1| ab|
#|Bob1| 16| x2| 1| 1|
#|Bob1| 16| x1| 2| 1|
#|Bob1| 16| x3| 2| 2|
#|Bob2| 16| x1| 1| a|
#|Bob2| 16| x2| 1| b|
#|Bob2| 16| x3| 1| c|
#+----+---+---------+----+-----+
我想如下创建df,这个预期结果(第二个表)非常混乱。您是否只需要一个属性列,如下表3?感谢@mohamand Murtaza Hashmi的回答。它解决了这个问题。是否可以向F.expr添加多个列,如F.expr(““”转换(S_属性,S_值,x,y->struct(x为属性,y为变量,2为类型))”),以便转换只能接受一个参数,或者(x,i),其中i是x的索引。因此,为了实现这一点,u必须组合S_属性
和S_值
,然后使用transform遍历它。如果S_值
也是一个数组,那么u可以这样做F.expr(““”转换(数组(S_属性,S_值),x->struct(x.S_属性作为属性,x.S_值作为值,2作为类型))”
如果您可以提供S_值
列和P_值
列,我很乐意与他们一起更新解决方案@NachiketKateThanks,以获得答案@Psidom。如果P_值和S_值列作为这些属性的值,是否可以使用f.array()呢?不完全确定您需要什么。但是可能f.posexplode(f.explode(f.create_-map('P_-Attribute','P_-values')、f.create_-map('S_-Attributes','S_-values')))))。别名('type','Attribute')
如果属性列没有重复的元素。