从Python列表向PySpark数据帧添加新列

从Python列表向PySpark数据帧添加新列,python,apache-spark,pyspark,apache-spark-sql,Python,Apache Spark,Pyspark,Apache Spark Sql,我有一份清单: dates = [2017, 2018, 2018, 2018, 2019, 2019, 2019, 2020, 2020, 2020] 我尝试添加到的数据帧长度相同,没有问题 我试过: df = df.withColumn("YEARS", dates) Error: Column needs to be col 我也试过: df = df.withColumn("YEARS", f.lit(dates)) 但这并不奏效 我看到这个

我有一份清单:

dates = [2017, 2018, 2018, 2018, 2019, 2019, 2019, 2020, 2020, 2020]
我尝试添加到的数据帧长度相同,没有问题

我试过:

df = df.withColumn("YEARS", dates)
Error: Column needs to be col
我也试过:

df = df.withColumn("YEARS", f.lit(dates))
但这并不奏效

我看到这个问题:

但在这种情况下没有什么有用的

更新: 预期结果是:

df_columns...   | dates_from_list
---------------------------------
original_df_data| 2017
original_df_data| 2018
original_df_data| 2018
original_df_data| 2018
original_df_data| 2019
original_df_data| 2019
original_df_data| 2019
original_df_data| 2020
original_df_data| 2020
original_df_data| 2020

您的错误是因为您需要将列对象传递给withColumn

根据日期数据的大小,有两种方法可以将日期作为新列添加到Spark数据框联接中,该联接使用每个数据框中记录的顺序进行

1如果您操作一个小数据集 实现这一点的一种简洁方法是将UDF应用于单增长id:

从pyspark.sql.functions导入udf,单调地增加id df=[…]10条记录 日期=[2017、2018、2018、2018、2019、2019、2019、2020、2020、2020] df=df.repartition1.withColumn 年, udflambda id:dates[id]单调递增 df.show 产出:

+---+-----+
|...|YEARS|
+---+-----+
|...| 2017|
|...| 2018|
|...| 2018|
|...| 2018|
|...| 2019|
|...| 2019|
|...| 2019|
|...| 2020|
|...| 2020|
|...| 2020|
+---+-----+
注意:.repartition1确保生成的ID是连续的。如果您有另一种方法将每个记录映射到日期中的值(如以前构建的id列),则可以避免将其重新分区到单个分区。 在这个用例中,正如我们所期望的那样,pythonlist对象非常小,这意味着您的数据帧也非常小,因此这种重新分区不是什么大问题

/!\如果dataframe和python列表太大,为什么它不能扩展:

需要对数据帧进行重新分区,从而导致代价高昂的洗牌/交换 .repartition1可能导致生成一个非常大的分区,该分区的处理速度非常慢,因为它非常大,而且如果它不适合执行内存,则可能意味着会有许多额外的磁盘I/O将RDD块溢出到磁盘,或者导致作业因OutOfMemory错误而崩溃。 python列表由udf通过lambda闭包捕获,这意味着它将广播给集群的每个执行者 2如果操作数据集大小>百万行 这里是另一种方法,通过使用pandas操作id和dates列并避免对Spark数据帧进行任何重新分区,可以更好地处理数百万行

可以这样做:

作为pd进口熊猫 从pyspark.sql.functions导入单调递增的\u id 从pyspark.sql.session导入SparkSession spark=SparkSession.builder.getOrCreate 一些长度为N的数据帧 df=[…] 在不重新划分Spark数据帧的情况下,生成不连续的单增长ID。 df=df.withColumnid,单调递增 获取生成的ID作为单列数据帧不连续 spark_df_ids=df.selectid.toPandas 长度为N的一些python列表 日期=[2017、2018、2018、2018、2019、…、2019、2019、2020、2020、2020] 从日期构建数据框架 dates\u pandas\u df=pd.DataFramedates,columns=[年] 将id列附加到表中的日期 dates_和_id_pandas_df=dates_pandas_df.joinspark_df_id 从pandas数据帧转换为spark数据帧 dates_和_id_spark_df=spark.createDataFramedates_和_id_pandas_df 使用Spark中的联接将dates列最后添加到Spark数据框 df.joindates_和_id_spark_df[id]。显示 重要提示:可以进行熊猫和熊猫的转换

您可以尝试以下方法:

dates = [2017, 2018, 2018, 2018, 2019, 2019, 2019, 2020, 2020, 2020]
df = spark.createDataFrame([Row(a=1)])
df = df.withColumn("YEARS",  array( [lit(x) for x in dates]  ))


df.show(truncate=False)
+---+------------------------------------------------------------+
|a  |YEARS                                                       |
+---+------------------------------------------------------------+
|1  |[2017, 2018, 2018, 2018, 2019, 2019, 2019, 2020, 2020, 2020]|
+---+------------------------------------------------------------+

df.select("a", explode("YEARS")).show()
+---+----+
|  a| col|
+---+----+
|  1|2017|
|  1|2018|
|  1|2018|
|  1|2018|
|  1|2019|
|  1|2019|
|  1|2019|
|  1|2020|
|  1|2020|
|  1|2020|
+---+----+

为了举例,我在示例中使用了10行。如果数据集为100-200万行,使用此UDF是否会降低效率?我通常使用Pandas编写代码,但由于我正在处理的数据集的大小,我正在转换为PySpark,即使您通过使用另一种方法将数据帧记录映射到python列表的元素来避免.repartition1,还有一个潜在的巨大成本,对于数百万行来说显然并不便宜:python列表由udf通过lambda闭包捕获,这意味着它将被广播。因此,在这种规模下,最好直接使用pandas,然后将pandas数据帧转换为spark数据帧:spark.createDataFramepandas_df。启用Apache Arrow后,可以加快速度。如果这个答案有帮助的话,你可能会想接受它,并且可能会提出另一个问题。谢谢,你可以查看我的编辑以获得更具可扩展性的答案alternative@EnzoBnl…我喜欢上面的方法。这里有另一种类似的方法,但列表元素和数据帧行数必须相同。