[英]How can I take a dataframe containing lists of strings and create another dataframe from these lists in Pyspark?
假設我有一個看起來像這樣的 dataframe
+--------------------+
| ColA |
+--------------------+
| [val1, val2, val3] |
+--------------------+
| [val4, val5, val6] |
+--------------------+
| [val7, val8, val9] |
+--------------------+
如何創建一個看起來像這樣的新 dataframe?
+------+------+------+
| Col1 | Col2 | Col3 |
+------+------+------+
| val1 | val2 | val3 |
+------+------+------+
| val4 | val5 | val6 |
+------+------+------+
| val7 | val8 | val9 |
+------+------+------+
此代碼足夠健壯,可以采用 arrays 中的任意數量的元素。 雖然OP
在每個數組中有 3 個元素。 我們首先創建上述DataFrame
。
# Loading requisite packages.
from pyspark.sql.functions import col, explode, first, udf
df = sqlContext.createDataFrame([(['val1', 'val2', 'val3'],),
(['val4', 'val5', 'val6'],),
(['val7', 'val8', 'val9'],)],['ColA',])
df.show()
+------------------+
| ColA|
+------------------+
|[val1, val2, val3]|
|[val4, val5, val6]|
|[val7, val8, val9]|
+------------------+
由於我們希望將單個數組的每個元素標記為相應的列,因此第一步我們嘗試在列名和值之間進行映射。 我們創建一個user defined function
- ( UDF
)來實現這一點。
def func(c):
return [['Col'+str(i+1),c[i]] for i in range(len(c))]
func_udf = udf(func,ArrayType(StructType([
StructField('a', StringType()),
StructField('b', StringType())
])))
df = df.withColumn('ColA_new',func_udf(col('ColA')))
df.show(truncate=False)
+------------------+---------------------------------------+
|ColA |ColA_new |
+------------------+---------------------------------------+
|[val1, val2, val3]|[[Col1,val1], [Col2,val2], [Col3,val3]]|
|[val4, val5, val6]|[[Col1,val4], [Col2,val5], [Col3,val6]]|
|[val7, val8, val9]|[[Col1,val7], [Col2,val8], [Col3,val9]]|
+------------------+---------------------------------------+
完成此操作后,我們將分解explode
。
# Step 1: Explode the DataFrame
df=df.withColumn('vals', explode('ColA_new')).drop('ColA_new')
df.show()
+------------------+-----------+
| ColA| vals|
+------------------+-----------+
|[val1, val2, val3]|[Col1,val1]|
|[val1, val2, val3]|[Col2,val2]|
|[val1, val2, val3]|[Col3,val3]|
|[val4, val5, val6]|[Col1,val4]|
|[val4, val5, val6]|[Col2,val5]|
|[val4, val5, val6]|[Col3,val6]|
|[val7, val8, val9]|[Col1,val7]|
|[val7, val8, val9]|[Col2,val8]|
|[val7, val8, val9]|[Col3,val9]|
+------------------+-----------+
展開后,我們提取第一個和第二個元素,在UDF
中分別命名為a
和b
。
df=df.withColumn('column_name', col('vals').getItem('a'))
df=df.withColumn('value', col('vals').getItem('b')).drop('vals')
df.show()
+------------------+-----------+-----+
| ColA|column_name|value|
+------------------+-----------+-----+
|[val1, val2, val3]| Col1| val1|
|[val1, val2, val3]| Col2| val2|
|[val1, val2, val3]| Col3| val3|
|[val4, val5, val6]| Col1| val4|
|[val4, val5, val6]| Col2| val5|
|[val4, val5, val6]| Col3| val6|
|[val7, val8, val9]| Col1| val7|
|[val7, val8, val9]| Col2| val8|
|[val7, val8, val9]| Col3| val9|
+------------------+-----------+-----+
作為最后一步,我們將pivot
和 DataFrame 返回,得到最終的 DataFrame。 由於在旋轉中我們進行了aggregation
,所以我們在first()
的基礎上進行聚合,它采用組的第一個元素。
# Step 2: Pivot it back.
df = df.groupby('ColA').pivot('column_name').agg(first('value')).drop('ColA')
df.show()
+----+----+----+
|Col1|Col2|Col3|
+----+----+----+
|val1|val2|val3|
|val4|val5|val6|
|val7|val8|val9|
+----+----+----+
以下是一些使用map
到 RDD API 或單個 select 表達式的選項。
首先讓我們創建一些示例數據並從數據集中的任意行中提取列名。 這里的前提是數據集中的所有項必須具有相同的長度:
from pyspark.sql import Row
df = spark.createDataFrame(
[[["val1", "val2", "val3"]],
[["val4", "val5", "val6"]],
[["val7", "val8", "val9"]]], ["ColA"])
# get the len of the 1st item, the length should be the same for all the items in the dataset
ar_len = len(df.first()["ColA"])
# generate col names
col_names = ["col" + str(i + 1) for i in range(0, ar_len)]
col_names
# ['col1', 'col2', 'col3']
選項1:Map + 排
import pyspark.sql.functions as f
cols = [f.col('ColA').getItem(i).alias(c) for i,c in enumerate(col_names)]
def to_row(l):
# set the columns of the Row
r = Row(*cols)
# set the values of the row that we defined above
r = r(*l[0])
return r
df.rdd.map(to_row).toDF().show()
您應該首先聲明與數組項大小相同的cols
列表。 然后使用Row(*cols)
創建所需的Row
模式。 最后,我們使用r(*l[0])
設置先前創建的Row
項目的值。
選項2:Map + 元組
df.rdd.map(lambda l: (*l[0],)).toDF(col_names).show()
在這里,我們簡單地將列表中的所有項目解包到一個新的元組中。
選項3:select 語句
import pyspark.sql.functions as f
cols = [f.col('ColA').getItem(i).alias(c) for i,c in enumerate(col_names)]
df.select(*cols).show()
Output:
+----+----+----+
|col1|col2|col3|
+----+----+----+
|val1|val2|val3|
|val4|val5|val6|
|val7|val8|val9|
+----+----+----+
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.