我正在使用
从某个数据框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)。
答案 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()
可能仍然是不确定的。
您有两种方法可以解决此问题:
df.orderBy('someCol', 'rowId').limit(n)
df.limit(n).cache()
的结果,以便至少该限制的结果不会由于连续的动作调用而改变,否则会重新计算limit
的结果并弄乱了结果。