我在Spark 2.1.0(pySpark)中有一个带有嵌套结构的大而复杂的DataFrame,我想为它添加一个ID列。我这样做的方法是添加一个这样的列:
df= df.selectExpr('*','row_number() OVER (PARTITION BY File ORDER BY NULL) AS ID')
所以它就是例如从这个:
File A B
a.txt valA1 [valB11,valB12]
a.txt valA2 [valB21,valB22]
到此:
File A B ID
a.txt valA1 [valB11,valB12] 1
a.txt valA2 [valB21,valB22] 2
添加此列后,我不会立即触发Spark中的实现,但我首先将DataFrame分支到一个新变量:
dfOutput = df.select('A','ID')
只有A列和ID列,我将dfOutput
写入Hive,所以我得到了的表1 :
A ID
valA1 1
valA2 2
到目前为止一切顺利。然后我继续使用df
进行进一步的转换,即我在列中展开一些嵌套数组并删除原始数据,如下所示:
df = df.withColumn('Bexpl',explode('B')).drop('B')
我得到了这个:
File A Bexpl ID
a.txt valA1 valB11 1
a.txt valA1 valB12 1
a.txt valA2 valB21 2
a.txt valA2 valB22 2
并从中输出其他表,有时在创建第二个ID列之后,因为爆炸数组中有更多行。例如。我创建了 Table2 :
df= df.selectExpr('*','row_number() OVER (PARTITION BY File ORDER BY NULL) AS ID2')
得到:
File A Bexpl ID ID2
a.txt valA1 valB11 1 1
a.txt valA1 valB12 1 2
a.txt valA2 valB21 2 3
a.txt valA2 valB22 2 4
并输出如前所述:
dfOutput2 = df.select('Bexpl','ID','ID2')
得到:
Bexpl ID ID2
valB11 1 1
valB12 1 2
valB21 2 3
valB22 2 4
我希望第一个ID列的值保持不变,并且与创建此列的每一行的数据相匹配。这样我就可以保持dfOutput
创建的 Table1 与df
后续表格之间的关系,如dfOutput2
,结果 Table2
问题是ID和ID2不像上面的例子那样,但是混淆了,我试图找出原因。我的猜测是第一个ID列的值不是确定性的,因为df
在分支到dfOutput
之前没有实现。因此,当从dfOutput
保存表时实际实现数据时,行会被洗牌,ID与df
中稍后点上的数据不同,如dfOutput2
中所示。 。但我不确定,所以我的问题是:
dfOutput
之前实现DataFrame,例如通过df.cache().count()
,确保一个固定的ID列,我可以稍后从df
分支,以便我可以将其用作检查点?我会感谢任何帮助或至少快速确认,因为我无法正确测试。 Spark只有在没有足够的内存时才会对数据进行洗牌,达到这一点意味着需要加载大量数据,反过来需要很长时间,并且仍然可以提供巧合的好结果(已经尝试过使用较小的数据集)。 / p>
答案 0 :(得分:0)
我没有完全理解这个问题,但地址有些重点:
窗口定义为:
(PARTITION BY ... ORDER BY NULL)
窗口中的值的顺序实际上是随机的。它没有理由保持稳定。
只有在没有足够的内存时,Spark才会对数据进行洗牌,
每次执行计划(分组,窗口函数)需要时,Spark都会随机播放。您将此与磁盘溢出混淆,后者不会导致随机性。
在分支到dfOutput之前实现DataFrame,例如通过df.cache()。count()
没有。您不能依赖缓存的正确性。