当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Python PySpark DataFrame randomSplit方法用法及代码示例


PySpark DataFrame 的 randomSplit(~) 方法使用伯努利采样将 PySpark DataFrame 随机拆分为更小的 DataFrames 列表。

randomSplit的参数

1.weights | listnumbers

指定分割分布的权重列表。例如,设置[0.8,0.2]将使用以下逻辑将PySpark DataFrame拆分为2个较小的DataFrames:

  • 为原始 DataFrame 的每一行生成一个 0 到 1 之间的随机数。

  • 我们设定了 2 个接受范围:

    • 如果随机数在0到0.8之间,那么该行将被放置在第一个sub-DataFrame

    • 如果随机数在0.8和1.0之间,则该行将被放置在第二个sub-DataFrame中

下图显示了如何执行拆分:

在此,请注意以下事项:

  • 我们假设 PySpark DataFrame 有两个分区(蓝色和绿色)。

  • 首先根据每个分区中的某些列值对行进行本地排序。这种排序保证只要每个分区中存在相同的行(无论它们的顺序如何),我们总是会得到相同的确定性顺序。

  • 对于每一行,都会生成一个 0 到 1 之间的随机数。

  • 第一次分割的接受范围是 00.8 。生成的随机数在 00.8 之间的任何行都将被放置在第一个分割中。

  • 第二次分割的接受范围是 0.81.0 。生成的随机数在 0.81.0 之间的任何行都将被放置在第二个分割中。

这里重要的是,永远不能保证第一个 DataFrame 将拥有 80% 的行,而第二个 DataFrame 将拥有 20%。例如,假设为每行生成的随机数落在 00.8 之间 - 这意味着没有任何行最终会出现在第二个 DataFrame 分割中:

平均而言,我们应该预期第一个 DataFrame 将拥有 80% 的行,而第二个 DataFrame 将拥有 20% 的行,但实际的分割可能会非常不同。

注意

如果这些值加起来不等于 1,那么它们将被标准化。

2. seed | int | optional

使用相同的种子调用该函数将始终生成相同的结果。对此有一个警告,我们稍后会看到。

randomSplit的返回值

PySpark 数据帧的列表。

例子

考虑以下PySpark DataFrame:

df = spark.createDataFrame([["Alex", 20], ["Bob", 30], ["Cathy", 40], ["Dave", 40]], ["name", "age"])
df.show()



+-----+---+
| name|age|
+-----+---+
| Alex| 20|
|  Bob| 30|
|Cathy| 40|
| Dave| 40|
+-----+---+

将 PySpark 数据帧随机拆分为更小的 DataFrames

将此 PySpark DataFrame 随机拆分为 2 sub-DataFrames,行拆分为 75-25:

list_dfs = df.randomSplit([0.75,0.25], seed=24)
for _df in list_dfs:
    _df.show()



+-----+---+
| name|age|
+-----+---+
| Alex| 20|
|Cathy| 40|
+-----+---+

+----+---+
|name|age|
+----+---+
| Bob| 30|
|Dave| 40|
+----+---+

尽管我们预计第一个 DataFrame 包含 3 行,而第二个 DataFrame 包含 1 行,但我们看到分割是 50-50。这是因为,如上所述,randomSplit(~) 基于伯努利采样。

种子参数的怪癖

seed 参数用于再现性。例如,考虑以下PySpark DataFrame:

df = spark.createDataFrame([["Alex", 20], ["Bob", 30], ["Cathy", 40], ["Dave", 40]], ["name", "age"])
df



+-----+---+
| name|age|
+-----+---+
| Alex| 20|
|  Bob| 30|
|Cathy| 40|
| Dave| 40|
+-----+---+

鉴于 PySpark DataFrame 以完全相同的方式分区,使用相同的种子运行 randomSplit(~) 方法将保证相同的分割:

list_dfs = df.randomSplit([0.75,0.25], seed=24)
for _df in list_dfs:
    _df.show()



+-----+---+
| name|age|
+-----+---+
| Alex| 20|
|Cathy| 40|
+-----+---+

+----+---+
|name|age|
+----+---+
| Bob| 30|
|Dave| 40|
+----+---+

多次运行上述操作将始终产生相同的分割,因为 PySpark DataFrame 的分区是相同的。

我们可以通过将 DataFrame 转换为 RDD,然后使用 glom() 方法来查看 PySpark DataFrame 的行是如何分区的:

df = spark.createDataFrame([["Alex", 20], ["Bob", 30], ["Cathy", 40], ["Dave", 40]], ["name", "age"])
df.rdd.glom().collect()



[[],
 [Row(name='Alex', age=20)],
 [],
 [Row(name='Bob', age=30)],
 [],
 [Row(name='Cathy', age=40)],
 [],
 [Row(name='Dave', age=40)]]

在这里,我们看到 PySpark DataFrame 被分为 8 个分区,但其中一半是空的。

让我们使用 repartition(~) 更改分区:

df = df.repartition(2)
df.rdd.glom().collect()



[[Row(name='Alex', age=20),
  Row(name='Bob', age=30),
  Row(name='Cathy', age=40),
  Row(name='Dave', age=40)],
 []]

尽管 DataFrame 的内容相同,但我们现在只有 2 个分区,而不是 8 个分区。

让我们使用与之前相同的种子 ( 24 ) 来调用 randomSplit(~)

list_dfs = df.randomSplit([0.75,0.25], seed=24)
for _df in list_dfs:
    _df.show()



+-----+---+
| name|age|
+-----+---+
| Alex| 20|
|  Bob| 30|
|Cathy| 40|
| Dave| 40|
+-----+---+

+----+---+
|name|age|
+----+---+
+----+---+

请注意,即使我们使用相同的种子,我们最终也会得到不同的分裂。这证实了 seed 参数仅在底层分区相同时才保证一致的分割。您应该谨慎对待这种行为,因为分区在洗牌操作后可能会发生变化(例如 join(~) groupBy(~) )。

相关用法


注:本文由纯净天空筛选整理自Isshin Inada大神的英文原创作品 PySpark DataFrame | randomSplit method。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。