Apache spark 将具有结构数组的列展开为新列

Apache spark 将具有结构数组的列展开为新列,apache-spark,pyspark,Apache Spark,Pyspark,我有一个数据框架,其中有一列是结构数组 df.printSchema() root |-- dataCells: array (nullable = true) | |-- element: struct (containsNull = true) | | |-- label: string (nullable = true) | | |-- value: string (nullable = true) 一些示例数据可能如下所示: df.first()

我有一个数据框架,其中有一列是结构数组

df.printSchema()
root
 |-- dataCells: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- label: string (nullable = true)
 |    |    |-- value: string (nullable = true)
一些示例数据可能如下所示:

df.first()
Row(dataCells=[Row(label="firstName", value="John"), Row(label="lastName", value="Doe"), Row(label="Date", value="1/29/2018")])
------------------------------------
| firstName | lastName | Date      |
------------------------------------
| John      | Doe      | 1/29/2018 |
| ....      | ...      | ...       |
我正试图通过将每个结构转换成一个命名列来找出如何重新格式化这个数据帧。我想要一个这样的数据帧:

df.first()
Row(dataCells=[Row(label="firstName", value="John"), Row(label="lastName", value="Doe"), Row(label="Date", value="1/29/2018")])
------------------------------------
| firstName | lastName | Date      |
------------------------------------
| John      | Doe      | 1/29/2018 |
| ....      | ...      | ...       |
我已经尝试了我能想到的一切,但还没有找到答案。

只需爆炸并选择*

从pyspark.sql.functions导入explode,first,col,单调递增\u id df=spark.createDataFrame[ RowdataCells=[Rowlabel=firstName,value=John,Rowlabel=lastName,value=Doe,Rowlabel=Date,value=2018年1月29日] ] 长=df .withColumnid,单调递增 .selectid,explodesdatacells.aliascol .selectid,col* 和支点:

long.groupByid.pivotlabel.aggfirstvalue.show +------+-----+-----+----+ |id |日期|姓|姓| +------+-----+-----+----+ |25769803776 | 2018年1月29日|约翰|能源部| +------+-----+-----+----+ 您还可以:

从pyspark.sql.functions导入udf @udfmap def as_mapx: 返回dictx cols=[coldataCells[c]。在[Date,firstName,lastName]]中c的别名为c df.selectas\u mapdataCells.aliasdataCells.selectcols.show +-----+-----+----+ |日期|姓|姓| +-----+-----+----+ |2018年1月29日|约翰|能源部| +-----+-----+----+ 参考资料:


我在没有UDF的情况下尝试的另一种方法

>>> df.show()
+--------------------+
|           dataCells|
+--------------------+
|[[firstName,John]...|
+--------------------+

>>> from pyspark.sql import functions as F

## size of array with maximum length in column 
>>> arr_len = df.select(F.max(F.size('dataCells')).alias('len')).first().len

## get values from struct 
>>> df1 = df.select([df.dataCells[i].value for i in range(arr_len)])
>>> df1.show()
+------------------+------------------+------------------+
|dataCells[0].value|dataCells[1].value|dataCells[2].value|
+------------------+------------------+------------------+
|              John|               Doe|         1/29/2018|
+------------------+------------------+------------------+

>>> oldcols = df1.columns

## get the labels from struct
>>> cols = df.select([df.dataCells[i].label.alias('col_%s'%i) for i in range(arr_len)]).dropna().first()
>>> cols
Row(dataCells[0].label=u'firstName', dataCells[1].label=u'lastName', dataCells[2].label=u'Date')
>>> newcols = [cols[i] for i in range(arr_len)]
>>> newcols
[u'firstName', u'lastName', u'Date']

## use the labels to rename the columns
>>> df2 = reduce(lambda data, idx: data.withColumnRenamed(oldcols[idx], newcols[idx]), range(len(oldcols)), df1)
>>> df2.show()
+---------+--------+---------+
|firstName|lastName|     Date|
+---------+--------+---------+
|     John|     Doe|1/29/2018|
+---------+--------+---------+

回答得很好。我遇到了以下错误:pivot列标签有10000多个不同的值,这让我担心这种方法的长期性能。这是一个问题。Spark不能很好地处理宽数据。在这种情况下,我推荐第二种解决方案——explode和pivot都是昂贵的。请注意,只有当所有行都以相同的顺序包含相同的元组集时,这才有效。这是一个冒险的假设。