如何在 pyspark 中创建数据框的副本?

Posted

技术标签:

【中文标题】如何在 pyspark 中创建数据框的副本?【英文标题】:How to create a copy of a dataframe in pyspark? 【发布时间】:2018-09-12 04:35:09 【问题描述】:

我有一个数据框,我需要通过执行以下操作来创建一个新的数据框,并在架构中进行少量更改。

>>> X = spark.createDataFrame([[1,2], [3,4]], ['a', 'b'])
>>> schema_new = X.schema.add('id_col', LongType(), False)
>>> _X = X.rdd.zipWithIndex().map(lambda l: list(l[0]) + [l[1]]).toDF(schema_new)

问题是在上述操作中,X 的架构被原地改变了。所以当我打印 X.columns 我得到 ​​p>

>>> X.columns
['a', 'b', 'id_col']

X 中的值仍然相同

>>> X.show()
+---+---+
|  a|  b|
+---+---+
|  1|  2|
|  3|  4|
+---+---+

为避免更改X 的架构,我尝试使用三种方式创建X 的副本 - 使用来自copy 模块的copydeepcopy 方法 - 只需使用_X = X

copy 方法失败并返回一个

RecursionError: maximum recursion depth exceeded

赋值方法也不行

>>> _X = X
>>> id(_X) == id(X)
True

由于他们的id 是相同的,因此在这里创建一个重复的数据框并没有真正的帮助,并且在_X 上完成的操作反映在X 中。

所以我的问题确实是两个方面

如何更改架构替代位置(即不对X 进行任何更改)?

更重要的是,如何创建 pyspark 数据帧的副本?

注意:

这个问题是这个post的后续问题

【问题讨论】:

我想到的最简单的解决方案是使用df_copy = original_df.select("*") 的解决方法,并且可能添加一些 .cache() 。有意义吗? 【参考方案1】:

.alias() 常用于重命名列,但它也是a DataFrame method,它会给你你想要的:

df2 = df.alias('df2')
id(df2) == id(df)  # False

【讨论】:

@GuillaumeLabs 你能告诉你的火花版本和你得到了什么错误。 我正在使用 azure databricks 6.4 。数据帧的 ID 不同,但因为初始数据帧是一个增量表的选择,所以这个数据帧的副本和你的技巧仍然是这个增量表的一个选择;-)。【参考方案2】:

如另一个问题的答案中所述,您可以对初始架构进行深度复制。然后我们可以修改该副本并使用它来初始化新的DataFrame_X

import pyspark.sql.functions as F
from pyspark.sql.types import LongType
import copy

X = spark.createDataFrame([[1,2], [3,4]], ['a', 'b'])
_schema = copy.deepcopy(X.schema)
_schema.add('id_col', LongType(), False) # modified inplace
_X = X.rdd.zipWithIndex().map(lambda l: list(l[0]) + [l[1]]).toDF(_schema)

现在让我们检查一下:

print('Schema of X: ' + str(X.schema))
print('Schema of _X: ' + str(_X.schema))

输出:

Schema of X: StructType(List(StructField(a,LongType,true),StructField(b,LongType,true)))
Schema of _X: StructType(List(StructField(a,LongType,true),
                  StructField(b,LongType,true),StructField(id_col,LongType,false)))

请注意,要复制DataFrame,您只需使用_X = X。每当您添加新列时,例如withColumn,对象并没有就地改变,而是返回了一个新的副本。 希望这会有所帮助!

【讨论】:

【参考方案3】:

如果您需要创建 pyspark 数据帧的副本,您可以使用 Pandas。

schema = X.schema
X_pd = X.toPandas()
_X = spark.createDataFrame(X_pd,schema=schema)
del X_pd

【讨论】:

【参考方案4】:

在 Scala 中:

    使用“X.schema.copy”创建新架构实例,而无需修改旧架构; 在每个返回 Dataframe(“select”、“where”等)的 Dataframe 操作中,都会创建新的 Dataframe,而无需修改原始数据框。原件可以反复使用。猜猜,您的情况不需要重复。性能是单独的问题,可以使用“persist”。

【讨论】:

【参考方案5】:
df2 = df.select("*")
id(df2) = id(df)  # False

这与@SantiagoRodriguez 给出的答案相同,同样代表了与@tozCSS 共享的类似方法。我相信@tozCSS 建议使用.alias() 代替.select() 可能确实是最有效的。

【讨论】:

以上是关于如何在 pyspark 中创建数据框的副本?的主要内容,如果未能解决你的问题,请参考以下文章

从列表中创建一个 pyspark 数据框列,其中列表的长度与数据框的行数相同

如何在 PySpark ML 中创建自定义 SQLTransformer 以透视数据

如何在 Databricks 的 PySpark 中使用在 Scala 中创建的 DataFrame

如何在 Pyspark Dataframe 中创建多列的所有成对组合?

在 pyspark 中创建训练集和测试集时,如何跨不同组进行分层抽样? [关闭]

如何在 Python 中创建具有两列作为元组或 Pandas 数据框的单个变量?