Spark复制Python / PySpark中的数据框列最佳实践?

时间:2018-12-19 01:32:56

标签: python apache-spark pyspark

这是针对使用Spark 2.3.2的Python / PySpark。 我正在寻找最佳实践方法,使用Python / PySpark将一个数据框的列复制到另一个数据框,以处理10+十亿行(按年/月/日均匀划分)的非常大的数据集。每行有120列要转换/复制。输出数据帧将被写入日期分区,并写入另一个拼合的文件集中。

示例架构为: 输入DFinput(colA,colB,colC)和 输出DFoutput(X,Y,Z)

我要将DFInput复制到DFOutput,如下所示(colA => Z,colB => X,colC => Y)。

在Python Spark 2.3+中执行此操作的最佳实践是什么? 是否应该为每个列使用 DF.withColumn() 方法将源复制到目标列? 如果数十亿行每行有110列以上的列进行复制,这样的性能是否很好?

谢谢

5 个答案:

答案 0 :(得分:1)

使用dataframe.withColumn()通过添加列或替换具有相同名称的现有列来返回新的DataFrame。

答案 1 :(得分:0)

就我所知,使用Apache Spark的方法是将输入DataFrame转换为所需的输出DataFrame。您只需在输入DataFrame上使用selectExpr即可完成该任务:

outputDF = inputDF.selectExpr("colB as X", "colC as Y", "colA as Z")

此转换不会将数据从输入DataFrame“复制”到输出DataFrame。

答案 2 :(得分:0)

我遇到的这个有趣的示例显示了两种方法和更好的方法,并且同意其他答案。这是Scala,而不是pyspark,但适用原理相同,即使示例不同。

import org.apache.spark.sql.functions._
import spark.implicits._

val df = Seq(
             ("1","2", "3"),
             ("4", "5", "6"),
             ("100","101", "102")
            ).toDF("c1", "c2", "c3")

这很昂贵,因为withColumn会为每次迭代创建一个新的DF:

val df2 = df.columns.foldLeft(df) { case (df, col) =>
          df.withColumn(col, df(col).cast("int"))
          }
//df2.show(false)

这更快。

val df3 = df.select(df.columns.map { col =>
          df(col).cast("int")
          }: _*)
//df3.show(false)

答案 3 :(得分:0)

这个(python)上的菜鸟有点难,但是用SQL(或您拥有的任何原始资料)来做,然后将其读入新的/单独的数据帧中可能会更容易些?

答案 4 :(得分:0)

使用python处理列映射的另一个有用的结构是dictionary。字典可帮助您使用key/value结构将初始数据框的列映射到最终数据框的列,如下所示:

from pyspark.sql.functions import col

df = spark.createDataFrame([
  [1, "John", "2019-12-01 10:00:00"],
  [2, "Michael", "2019-12-01 11:00:00"],
  [2, "Michael", "2019-12-01 11:01:00"],
  [3, "Tom", "2019-11-13 20:00:00"],
  [3, "Tom", "2019-11-14 00:00:00"],
  [4, "Sofy", "2019-10-01 01:00:00"]
], ["A", "B", "C"])


col_map = {"A":"Z", "B":"X", "C":"Y"}

df.select(*[col(k).alias(col_map[k]) for k in col_map]).show()

# +---+-------+-------------------+
# |  Z|      X|                  Y|
# +---+-------+-------------------+
# |  1|   John|2019-12-01 10:00:00|
# |  2|Michael|2019-12-01 11:00:00|
# |  2|Michael|2019-12-01 11:01:00|
# |  3|    Tom|2019-11-13 20:00:00|
# |  3|    Tom|2019-11-14 00:00:00|
# |  4|   Sofy|2019-10-01 01:00:00|
# +---+-------+-------------------+

在这里,我们分别将A,B,C映射到Z,Y,X。

如果需要模块化解决方案,还可以将所有内容都放在函数中:

def tranform_cols(mappings, df):
  return df.select(*[col(k).alias(mappings[k]) for k in mappings])

或者通过使用monkey patching来扩展DataFrame类的现有功能,甚至进行模块化。将下一个代码放在其他任何东西之前的PySpark代码顶部:

from pyspark.sql import DataFrame

def transform_cols(self, mappings):
  return self.select(*[col(k).alias(mappings[k]) for k in mappings])

DataFrame.transform = transform_cols

然后通过以下方式调用它:

df.tranform(col_map).show()

PS:这可能是通过创建自己的库并通过DataFrame和Monkey patching(熟悉C#的扩展方法)公开它们来扩展DataFrame功能的便捷方法。