Python 寻找pyspark的反比';s_-zip
我有以下格式化的输入数据帧:Python 寻找pyspark的反比';s_-zip,python,apache-spark,pyspark,pyspark-dataframes,Python,Apache Spark,Pyspark,Pyspark Dataframes,我有以下格式化的输入数据帧: 从pyspark.sql导入SparkSession 导入pyspark.sql.F函数 spark=SparkSession.builder.master(“本地”).getOrCreate() input_df=spark.createDataFrame( [ (‘爱丽丝;鲍勃;卡罗尔’,), ('12;13;14',), ('5;;7',), ('1;;3',), (';;3',) ], [“数据”] ) 输入_df.show() # +-----------
从pyspark.sql导入SparkSession
导入pyspark.sql.F函数
spark=SparkSession.builder.master(“本地”).getOrCreate()
input_df=spark.createDataFrame(
[
(‘爱丽丝;鲍勃;卡罗尔’,),
('12;13;14',),
('5;;7',),
('1;;3',),
(';;3',)
],
[“数据”]
)
输入_df.show()
# +---------------+
#|数据|
# +---------------+
#|爱丽丝;上下快速移动卡罗尔|
# | 12;13;14|
# | 5;;7|
# | 1;;3|
# | ;;3|
# +---------------+
实际输入是一个分号分隔的CSV文件,其中一列包含一个人的值。每个人可以有不同数量的值。在这里,Alice有3个值,Bob只有一个值,Carol有4个值
我想将其在PySpark中转换为一个输出数据帧,该数据帧保存每个人一个数组,在本例中,输出为:
result=spark.createDataFrame(
[
(“爱丽丝”[12,5,1]),
(“Bob”[13,]),
(“卡罗尔”[14,7,3,3])
],
['name','values']
)
result.show()
# +-----+-------------+
#|名称|值|
# +-----+-------------+
#|爱丽丝|[12,5,1]|
#|鲍勃|[13]|
#|卡罗尔|[14,7,3,3]|
# +-----+-------------+
我该怎么做?我想它将是F.arrays\u zip()
,F.split()
和/或F.explode()
的组合,但我想不出来
我现在被困在这里,这是我目前的尝试:
(输入
.withColumn('splits',F.split(F.col('data'),';'))
.drop('数据')
).show()
# +-------------------+
#|分裂|
# +-------------------+
#|[爱丽丝、鲍勃、卡罗尔]|
# | [12, 13, 14]|
# | [5, 7]|
# | [1, 3]|
# | [, 3]|
# +-------------------+
Spark-2.4+解决方案:
df.show()
#+---------------+
#| data|
#+---------------+
#|Alice;Bob;Carol|
#| 12;13;14|
#| 5;;7|
#| 1;;3|
#| ;;3|
#+---------------+
from pyspark.sql.functions import *
df.agg(split(concat_ws("|",collect_list(col("data"))),"\\|").alias("tmp")).\
withColumn("col1",split(element_at(col("tmp"),1),";")).\
withColumn("col2",split(element_at(col("tmp"),2),";")).\
withColumn("col3",split(element_at(col("tmp"),3),";")).\
withColumn("col4",split(element_at(col("tmp"),4),";")).\
withColumn("zip",arrays_zip(col("col1"),arrays_zip(col("col2"),col("col3"),col("col4")))).\
selectExpr("explode(zip)as tmp").\
selectExpr("tmp.*").\
toDF("name","values").\
show(10,False)
#+-----+----------+
#|name |values |
#+-----+----------+
#|Alice|[12, 5, 1]|
#|Bob |[13, , ] |
#|Carol|[14, 7, 3]|
#+-----+----------+
使用groupBy
使用收集列表将所有行合并为一行,然后拆分以创建新列
- 使用
数组\u zip
压缩数组并创建嵌套数组[键,[值]]
- 最后,
分解嵌套数组
示例:
df.show()
#+---------------+
#| data|
#+---------------+
#|Alice;Bob;Carol|
#| 12;13;14|
#| 5;;7|
#| 1;;3|
#| ;;3|
#+---------------+
from pyspark.sql.functions import *
df.agg(split(concat_ws("|",collect_list(col("data"))),"\\|").alias("tmp")).\
withColumn("col1",split(element_at(col("tmp"),1),";")).\
withColumn("col2",split(element_at(col("tmp"),2),";")).\
withColumn("col3",split(element_at(col("tmp"),3),";")).\
withColumn("col4",split(element_at(col("tmp"),4),";")).\
withColumn("zip",arrays_zip(col("col1"),arrays_zip(col("col2"),col("col3"),col("col4")))).\
selectExpr("explode(zip)as tmp").\
selectExpr("tmp.*").\
toDF("name","values").\
show(10,False)
#+-----+----------+
#|name |values |
#+-----+----------+
#|Alice|[12, 5, 1]|
#|Bob |[13, , ] |
#|Carol|[14, 7, 3]|
#+-----+----------+
对于spark<2.4
使用数组,使用getItem()
而不是函数中的元素。我建议将数据读取为代码>分离的csv,然后处理以获得名称
和值
列,如下所示-
请注意,这段代码是用scala编写的,但类似的代码可以在pyspark中实现,只需很少的更改
加载代码>分离csv
val数据=
"""
|爱丽丝;鲍勃;卡罗尔
| 12;13;14
| 5;;7
| 1;;3
| ;;3
“.stripMargin”
val stringDS=data.split(System.lineSeparator())
.map(\\;).map(\.split(“\\\”).map(\.replaceAll(“^[\t]+\\t]+$”,”).mkString(“;”)
.toSeq.toDS()
val df=spark.read
.期权(“sep”、“;”)
.选项(“推断模式”、“真”)
.选项(“标题”、“正确”)
.选项(“空值”、“空值”)
.csv(stringDS)
df.printSchema()
df.show(假)
/**
*根
*|--Alice:integer(nullable=true)
*|--Bob:integer(nullable=true)
*|--Carol:integer(nullable=true)
*
* +-----+----+-----+
*|爱丽丝|鲍勃|卡罗尔|
* +-----+----+-----+
* |12 |13 |14 |
*| 5 |空| 7|
*| 1 |空| 3|
*|空|空| 3|
* +-----+----+-----+
*/
派生名称
和值
列
val columns=df.columns.map(c=>expr(s“named_struct('name','$c','values',collect_list($c)))
选择(数组(列:*).as(“数组”))
.selectExpr(“内联\外部(数组)”)
.show(假)
/**
* +-----+-------------+
*|名称|值|
* +-----+-------------+
*|爱丽丝|[12,5,1]|
*|鲍勃|[13]|
*|卡罗尔|[14,7,3,3]|
* +-----+-------------+
*/
一种方法是读取第一行作为标题,然后取消打印数据
df1 = spark.createDataFrame([(12,13,14),(5,None,7),(1,None,3),(None,None,3)], ['Alice','Bob','Carol'])
df1.show()
+-----+----+-----+
|Alice| Bob|Carol|
+-----+----+-----+
| 12| 13| 14|
| 5|null| 7|
| 1|null| 3|
| null|null| 3|
+-----+----+-----+
df1.select(f.expr('''stack(3,'Alice',Alice,'Bob',Bob,'Carol',Carol) as (Name,Value)'''))\
.groupBy('Name').agg(f.collect_list('value').alias('Value')).orderBy('Name').show()
+-----+-------------+
| Name| Value|
+-----+-------------+
|Alice| [12, 5, 1]|
| Bob| [13]|
|Carol|[14, 7, 3, 3]|
+-----+-------------+
要动态传递列,请使用以下代码
cols = ','.join([f"'{i[0]}',{i[1]}" for i in zip(df1.columns,df1.columns)])
df1.select(f.expr(f'''stack(3,{cols}) as (Name,Value)''')).groupBy('Name').agg(f.collect_list('value').alias('Value')).orderBy('Name').show()
+-----+-------------+
| Name| Value|
+-----+-------------+
|Alice| [12, 5, 1]|
| Bob| [13]|
|Carol|[14, 7, 3, 3]|
+-----+-------------+
非常感谢!在我的原始数据集中,我有数百列,即数百个像Alice、Bob和Carol这样的名字。有没有办法在您的答案中循环所有创建“col1”、“col2”等的行?@AlexanderEngelhardt,对于在列中动态创建元素_,您可以使用这里提到的类似方法: