有人可以帮我理解在python for循环中将地图函数附加到RDD的行为吗?
以下代码:
rdd = spark.sparkContext.parallelize([[1], [2], [3]])
def appender(l, i):
return l + [i]
for i in range(3):
rdd = rdd.map(lambda x: appender(x, i))
rdd.collect()
我得到了输出:
[[1, 2, 2, 2], [2, 2, 2, 2], [3, 2, 2, 2]]
使用以下代码:
rdd = spark.sparkContext.parallelize([[1], [2], [3]])
def appender(l, i):
return l + [i]
rdd = rdd.map(lambda x: appender(x, 1))
rdd = rdd.map(lambda x: appender(x, 2))
rdd = rdd.map(lambda x: appender(x, 3))
rdd.collect()
我得到了预期的输出:
[[1, 1, 2, 3], [2, 1, 2, 3], [3, 1, 2, 3]]
我想这与传递给PySpark编译器的闭包有关,但是我找不到任何关于这个的文档...
答案 0 :(得分:2)
解决方案是将所有全局变量(在本例中为i
)存储在lambda函数中以确保正确关闭。这可以通过
for i in range(3):
rdd = rdd.map(lambda x, i=i: appender(x, i))
有关此内容的更多信息,请访问lambda function accessing outside variable。
有趣的是,至少在本地集群上(尚未在分布式集群上进行测试),问题也可以通过持久化中间的rdd来解决:
for i in range(3):
rdd = rdd.map(lambda x: appender(x, i))
rdd.persist()
两种解决方案都产生
[[1, 0, 1, 2], [2, 0, 1, 2], [3, 0, 1, 2]]
答案 1 :(得分:1)
我最好的猜测是因为懒惰的评价: 你也有一个糟糕的范围。
这两个代码片段会产生相同的输出:
rdd = spark.sparkContext.parallelize([[1], [2], [3]])
def appender(l, i):
return l + [i]
for i in range(1,4):
rdd = spark.sparkContext.parallelize(rdd.map(lambda x: appender(x, i)).collect())
rdd.collect()
输出:
[[1, 1, 2, 3], [2, 1, 2, 3], [3, 1, 2, 3]]
和第二个:
rdd = spark.sparkContext.parallelize([[1], [2], [3]])
rdd = rdd.map(lambda x: appender(x, 1))
rdd = rdd.map(lambda x: appender(x, 2))
rdd = rdd.map(lambda x: appender(x, 3))
rdd.collect()
输出:
[[1, 1, 2, 3], [2, 1, 2, 3], [3, 1, 2, 3]]
另外,为了显示在简化示例中的for循环中发生了什么(仅输入1和2),使用修改后的appender函数来打印l参数:
用于循环打印:
[2]
[2, 2]
[1]
[3]
[1, 2]
[3, 2]
首先它从输入列表中获取第二个字段
显式写入映射器输出是:
[1]
[1, 1]
[2]
[2, 1]
[3]
[3, 1]