如何在Pyspark中将列表拆分为多个列?

时间:2017-08-21 04:50:11

标签: pyspark apache-spark-sql

我有:

key   value
a    [1,2,3]
b    [2,3,4]

我想:

key value1 value2 value3
a     1      2      3
b     2      3      4

似乎在scala中我可以写:df.select($"value._1", $"value._2", $"value._3"),但在python中是不可能的。

那么有一个很好的方法吗?

6 个答案:

答案 0 :(得分:26)

这取决于您的"列表":

的类型
  • 如果类型为ArrayType()

    df = hc.createDataFrame(sc.parallelize([['a', [1,2,3]], ['b', [2,3,4]]]), ["key", "value"])
    df.printSchema()
    df.show()
    root
     |-- key: string (nullable = true)
     |-- value: array (nullable = true)
     |    |-- element: long (containsNull = true)
    

    您可以使用[]

    访问python,就像使用python一样
    df.select("key", df.value[0], df.value[1], df.value[2]).show()
    +---+--------+--------+--------+
    |key|value[0]|value[1]|value[2]|
    +---+--------+--------+--------+
    |  a|       1|       2|       3|
    |  b|       2|       3|       4|
    +---+--------+--------+--------+
    
    +---+-------+
    |key|  value|
    +---+-------+
    |  a|[1,2,3]|
    |  b|[2,3,4]|
    +---+-------+
    
  • 如果类型为StructType() :(也许您是通过阅读JSON构建数据框)

    df2 = df.select("key", psf.struct(
            df.value[0].alias("value1"), 
            df.value[1].alias("value2"), 
            df.value[2].alias("value3")
        ).alias("value"))
    df2.printSchema()
    df2.show()
    root
     |-- key: string (nullable = true)
     |-- value: struct (nullable = false)
     |    |-- value1: long (nullable = true)
     |    |-- value2: long (nullable = true)
     |    |-- value3: long (nullable = true)
    
    +---+-------+
    |key|  value|
    +---+-------+
    |  a|[1,2,3]|
    |  b|[2,3,4]|
    +---+-------+
    

    你可以直接分开'使用*

    的列
    df2.select('key', 'value.*').show()
    +---+------+------+------+
    |key|value1|value2|value3|
    +---+------+------+------+
    |  a|     1|     2|     3|
    |  b|     2|     3|     4|
    +---+------+------+------+
    

答案 1 :(得分:1)

@jordi Aceiton感谢您的解决方案。 我试图使其更加简洁,尝试删除用于重命名新创建的列名的循环,并在创建列时进行了此操作。 使用df.columns来获取所有列名,而不是手动创建。

    from pyspark.sql.types import *          
    from pyspark.sql.functions import * 
    from pyspark import Row

    df = spark.createDataFrame([Row(index=1, finalArray = [1.1,2.3,7.5], c =4),Row(index=2, finalArray = [9.6,4.1,5.4], c= 4)])
    #collecting all the column names as list
    dlist = df.columns
    #Appending new columns to the dataframe
    df.select(dlist+[(col("finalArray")[x]).alias("Value"+str(x+1)) for x in range(0, 3)]).show()

输出:

     +---------------+-----+------+------+------+
     |  finalArray   |index|Value1|Value2|Value3|
     +---------------+-----+------+------+------+
     |[1.1, 2.3, 7.5]|  1  |   1.1|   2.3|   7.5|
     |[9.6, 4.1, 5.4]|  2  |   9.6|   4.1|   5.4|
     +---------------+-----+------+------+------+

答案 2 :(得分:1)

我需要将一个 712 维数组取消列出到列中,以便将其写入 csv。我首先使用@MaFF 的解决方案来解决我的问题,但这似乎导致了很多错误和额外的计算时间。我不确定是什么原因造成的,但我使用了一种不同的方法,该方法大大减少了计算时间(22 分钟,而超过 4 小时)!

@MaFF 的方法:

length = len(dataset.head()["list_col"])
dataset = dataset.select(dataset.columns + [dataset["list_col"][k] for k in range(length)])

我用过的:

dataset = dataset.rdd.map(lambda x: (*x, *x["list_col"])).toDF()

如果有人知道导致这种计算时间差异的原因,请告诉我!我怀疑在我的情况下,瓶颈在于调用 head() 来获取列表长度(我希望它是自适应的)。因为 (i) 我的数据管道很长而且很详尽,而且 (ii) 我不得不取消列出多个列。此外,缓存整个数据集也不是一种选择。

答案 3 :(得分:0)

我想在列表答案中添加大小写列表(数组)的大小写。

如果我们的列包含中型数组(或大型数组),仍然可以将它们拆分为列。

from pyspark.sql.types import *          # Needed to define DataFrame Schema.
from pyspark.sql.functions import expr   

# Define schema to create DataFrame with an array typed column.
mySchema = StructType([StructField("V1", StringType(), True),
                       StructField("V2", ArrayType(IntegerType(),True))])

df = spark.createDataFrame([['A', [1, 2, 3, 4, 5, 6, 7]], 
                            ['B', [8, 7, 6, 5, 4, 3, 2]]], schema= mySchema)

# Split list into columns using 'expr()' in a comprehension list.
arr_size = 7
df = df.select(['V1', 'V2']+[expr('V2[' + str(x) + ']') for x in range(0, arr_size)])

# It is posible to define new column names.
new_colnames = ['V1', 'V2'] + ['val_' + str(i) for i in range(0, arr_size)] 
df = df.toDF(*new_colnames)

结果是:

df.show(truncate= False)

+---+---------------------+-----+-----+-----+-----+-----+-----+-----+
|V1 |V2                   |val_0|val_1|val_2|val_3|val_4|val_5|val_6|
+---+---------------------+-----+-----+-----+-----+-----+-----+-----+
|A  |[1, 2, 3, 4, 5, 6, 7]|1    |2    |3    |4    |5    |6    |7    |
|B  |[8, 7, 6, 5, 4, 3, 2]|8    |7    |6    |5    |4    |3    |2    |
+---+---------------------+-----+-----+-----+-----+-----+-----+-----+

答案 4 :(得分:0)

添加到@MaFF 的答案中,对于数组类型数据,要动态执行此操作,您可以执行以下操作 df2.select(['key'] + [df2.features[x] for x in range(0,3)])

答案 5 :(得分:-2)

哦,我知道。您可以使用typename Container

getItem

诀窍。