Python 由于Spark'导致结果不一致;s懒惰评估
我有一个简单的Python 由于Spark'导致结果不一致;s懒惰评估,python,apache-spark,pyspark,Python,Apache Spark,Pyspark,我有一个简单的pyspark代码: l = [ {'userId': 'u1', 'itemId': 'a1', 'click': 1}, {'userId': 'u1', 'itemId': 'a2', 'click': 0}, {'userId': 'u2', 'itemId': 'b1', 'click': 1}, {'userId': 'u2', 'itemId': 'b2', 'click': 1}, ] d = sc.parallelize(l)
pyspark
代码:
l = [
{'userId': 'u1', 'itemId': 'a1', 'click': 1},
{'userId': 'u1', 'itemId': 'a2', 'click': 0},
{'userId': 'u2', 'itemId': 'b1', 'click': 1},
{'userId': 'u2', 'itemId': 'b2', 'click': 1},
]
d = sc.parallelize(l)
基本上,第一个用户单击两个项目中的一个,而第二个用户同时单击两个项目
让我们按userId
对事件进行分组,并在函数中处理这些事件
def fun((user_id, events)):
events = list(events)
user_id = events[0]['userId']
clicked = set()
not_clicked = set()
for event in events:
item_id = event['itemId']
if event['click']==1:
clicked.add(item_id)
else:
not_clicked.add(item_id)
ret = {'userId': user_id, 'click': 1}
for item_id in clicked:
ret['itemId'] = item_id
yield ret
ret['click'] = 0
for item_id in not_clicked:
ret['itemId'] = item_id
yield ret
d1 = d\
.map(lambda obj: (obj['userId'], obj))\
.groupByKey()\
.flatMap(fun)
d1.collect()
这就是我得到的:
[{'click': 1, 'itemId': 'a1', 'userId': 'u1'},
{'click': 0, 'itemId': 'a2', 'userId': 'u1'},
{'click': 1, 'itemId': 'b1', 'userId': 'u2'},
{'click': 0, 'itemId': 'b2', 'userId': 'u2'}]
用户u2
的结果不正确
有人能解释为什么会发生这种情况,以及预防这种情况的最佳做法是什么
谢谢。您所看到的与Spark评估模型关系不大。你的代码有问题。在本地执行时,很容易看到这一点:
key = 'u2'
values = [
{'click': 1, 'itemId': 'b1', 'userId': 'u2'},
{'click': 1, 'itemId': 'b2', 'userId': 'u2'}
]
list(fun((key, values)))
[{'click':0,'itemId':'b2','userId':'u2},
{'click':0,'itemId':'b2','userId':'u2'}]
正如你们所看到的,这比你们从Spark得到的东西更有意义。问题是在不应该使用可变数据的情况下使用可变数据。由于修改相同的dict
后,所有生成的返回完全相同的对象:
(d1, d2) = list(fun((key, values)))
d1 is d2
True
我认为与Spark相比的差异与批处理序列化有关,在函数退出之前,第一项在不同批中序列化,有效顺序大致如下:
import pickle
from itertools import islice, chain
gen = fun((key, values))
# The first batch is serialized
b1 = [pickle.dumps(x) for x in list(islice(gen, 0, 1))]
# Window is adjusted and the second batch is serialized
# fun exits with StopIteration when we try to take
# the second element in the batch
# element so code proceeds to ret['click'] = 0
b2 = [
pickle.dumps(x) for x in
# Use list to eagerly take a whole batch before pickling
list(islice(gen, 0, 2))
]
[pickle.loads(x) for x in chain(*[b1, b2])]
[{'click':1,'itemId':'b1','userId':'u2},
{'click':0,'itemId':'b2','userId':'u2'}]
但如果您想要一个最终的确认,您必须自己检查它(用一个等待所有数据的序列化程序替换成批序列化程序)
如何解决?只是不要用同一本字典。而是在循环中初始化一个新的循环:
for item_id in clicked:
yield {'userId': user_id, 'click': 1, 'item_id': item_id}
for item_id in not_clicked:
yield {'userId': user_id, 'click': 0, 'item_id': item_id}