为什么df.limit在Pyspark中不断变化?

时间:2016-05-10 19:10:57

标签: apache-spark pyspark spark-dataframe

我正在使用

从某个数据框df创建数据样本
rdd = df.limit(10000).rdd

此操作需要相当长的时间(实际上为什么?在10000行之后它不能短路?),所以我假设我现在有一个新的RDD。

但是,当我现在处理rdd时,每次访问它时都会有不同的行。好像它重新重新采样一样。缓存RDD有点帮助,但肯定不是保存?

背后的原因是什么?

更新:这是Spark 1.5.2的复制品

from operator import add
from pyspark.sql import Row
rdd=sc.parallelize([Row(i=i) for i in range(1000000)],100)
rdd1=rdd.toDF().limit(1000).rdd
for _ in range(3):
    print(rdd1.map(lambda row:row.i).reduce(add))

输出

499500
19955500
49651500

我很惊讶.rdd无法修复数据。

编辑: 为了表明它比重​​新执行问题更棘手,这里是一个单一的动作,它在Spark 2.0.0.2.5.0上产生不正确的结果

from pyspark.sql import Row
rdd=sc.parallelize([Row(i=i) for i in range(1000000)],200)
rdd1=rdd.toDF().limit(12345).rdd
rdd2=rdd1.map(lambda x:(x,x))
rdd2.join(rdd2).count()
# result is 10240 despite doing a self-join

基本上,每当您使用limit时,您的结果可能会出错。我的意思并不是“只是众多样本中的一个”,而是非常不正确(因为在这种情况下,结果应始终为12345)。

4 个答案:

答案 0 :(得分:0)

设置rdd后,它不会重新采样。如果没有看到很多代码或数据,很难在这里给你任何具体的反馈,但你可以通过进入pyspark shell并执行以下操作来轻松证明rdds不会重新采样:

>>> d = [{'name': 'Alice', 'age': 1, 'pet': 'cat'}, {'name': 'Bob', 'age': 2, 'pet': 'dog'}]
>>> df = sqlContext.createDataFrame(d)
>>> rdd = df.limit(1).rdd

现在你可以用一些打印功能重复打印出rdd的内容

>>> def p(x):
...    print x
...

您的输出将始终包含相同的值

>>> rdd.foreach(p)
Row(age=1, name=u'Alice', pet=u'cat')
>>> rdd.foreach(p)
Row(age=1, name=u'Alice', pet=u'cat')
>>> rdd.foreach(p)
Row(age=1, name=u'Alice', pet=u'cat')

我建议您检查代码或数据

答案 1 :(得分:0)

因为Spark是分布式的,所以通常假定确定性结果是不安全的。你的例子是第一个"第一个" 10,000行的DataFrame。在这里,首先是什么"首先"手段。这将取决于Spark的内部。例如,它可能是响应驱动程序的第一个分区。该分区可能会随着网络,数据位置等而改变。

即使您缓存数据,我仍然不会依赖每次都获取相同的数据,但我当然希望它比从磁盘读取更加一致。

答案 2 :(得分:0)

Spark是惰性的,因此您获取的每个action都会重新计算limit()返回的数据。如果基础数据分散在多个分区中,则每次评估时,限制都可能从另一个分区中拉出(即,如果数据存储在10个Parquet文件中,则第一个限制调用可能从文件1中拉出,第二次从文件中拉出文件7,依此类推)。

答案 3 :(得分:0)

来自Spark docs

<块引用>

LIMIT 子句用于限制 SELECT 语句返回的行数。通常,该子句与 ORDER BY 结合使用以确保结果是确定性的。

因此,如果您希望对 .limit() 的调用具有确定性,则需要事先对行进行排序。但是有一个问题!如果您按每行没有唯一值的列进行排序,则所谓的“绑定”行(具有相同排序键值的行)将不会确定性排序,因此 .limit() 可能仍然是不确定的。

您有两种方法可以解决此问题:

  • 确保在排序调用中包含唯一的行 ID。
    例如df.orderBy('someCol', 'rowId').limit(n)
  • 如果您只需要单次运行中的确定性结果,您可以简单地缓存 limit df.limit(n).cache() 的结果,以便至少该限制的结果不会由于连续的动作调用而改变,否则会重新计算limit 的结果并弄乱了结果。