将映射函数附加到PySpark RDD内部for循环

时间:2017-06-26 15:39:53

标签: apache-spark pyspark

有人可以帮我理解在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编译器的闭包有关,但是我找不到任何关于这个的文档...

2 个答案:

答案 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参数:

  1. 用于循环打印:

    [2]
    [2, 2]
    [1]
    [3]
    [1, 2]
    [3, 2]
    
  2. 首先它从输入列表中获取第二个字段

    1. 显式写入映射器输出是:

      [1]
      [1, 1]
      [2]
      [2, 1]
      [3]
      [3, 1]