PySpark:向DataFrame添加更多列的最佳实践

时间:2018-04-04 12:28:34

标签: apache-spark pyspark apache-spark-sql pyspark-sql

Spark Dataframes有一个方法withColumn,一次添加一个新列。要添加多个列,需要withColumn个链。这是最好的做法吗?

我觉得使用mapPartitions有更多优点。我们假设我有三个withColumn s链,然后根据某些条件删除Row个过滤器。这是四种不同的操作(我不确定这些操作中是否有任何广泛的转换)。但如果我做mapPartitions,我可以一气呵成。如果我有一个数据库连接,我也希望每个RDD分区打开一次。

我的问题分为两部分。

第一部分,这是我对mapPartitions的实现。这种方法有任何不可预见的问题吗?还有更优雅的方法吗?

df2 = df.rdd.mapPartitions(add_new_cols).toDF()

def add_new_cols(rows):
    db = open_db_connection()
    new_rows = []
    new_row_1 = Row("existing_col_1", "existing_col_2", "new_col_1", "new_col_2")
    i = 0
    for each_row in rows:
        i += 1
        # conditionally omit rows
        if i % 3 == 0:
            continue
        db_result = db.get_some_result(each_row.existing_col_2)
        new_col_1 = ''.join([db_result, "_NEW"])
        new_col_2 = db_result
        new_f_row = new_row_1(each_row.existing_col_1, each_row.existing_col_2, new_col_1, new_col_2)
        new_rows.append(new_f_row)

    db.close()
    return iter(new_rows)

第二部分,在mapPartitionswithColumn链上使用filter的权衡是什么?

我在某处读过,使用Spark DFs的可用方法总是比推出自己的实现更好。如果我的论点是错误的,请告诉我。谢谢!欢迎所有的想法。

2 个答案:

答案 0 :(得分:4)

  

这种方法有任何不可预见的问题吗?

多个。最严重的影响是:

  • 与普通DataFrame代码和显着的垃圾收集开销相比,内存占用量增加了几倍。
  • 在执行上下文之间移动数据所需的序列化和反序列化成本很高。
  • 在查询规划器中引入断点。
  • 原样,toDF调用的模式推断成本(如果提供了正确的模式,则可以避免)以及可能重新执行所有前面的步骤。
  • 依旧......

udfselect / withColumn可以避免其中一些,其他则不能。

  

假设我有一个带有三个withColumns的链,然后有一个过滤器可以根据某些条件删除Rows。这是四种不同的操作(我不确定这些操作中是否有任何广泛的转换)。但如果我做mapPartitions

,我可以一气呵成

您的mapPartitions不会删除Spark计划程序无法排除的任何操作,也不会提供任何优化。它唯一的优点是它为昂贵的连接对象提供了一个很好的范围。

  

我在某处读到使用Spark DFs的可用方法总是比推出自己的实现更好

当您开始使用执行程序端Python逻辑时,您已经脱离了Spark SQL。如果您使用udfRDD或新添加的矢量化udf,则无关紧要。在一天结束时,您应该根据代码的整体结构做出决定 - 如果主要是直接在数据上执行的Python逻辑,最好坚持使用RDD或完全跳过Spark。

如果它只是逻辑的一小部分,并且不会导致严重的性能问题,请不要为此烦恼。

答案 1 :(得分:-1)

使用df.withColumn()是添加列的最佳方式。他们都懒得加入