我有这段代码,我正在使用ipython
在pyspark
中阅读文件。我要做的是添加一个片段,它根据从文件中读取的特定列形成一个列表但是当我尝试执行它时,列表变为空,并且没有任何内容被附加到它。我的代码是:
list1 = []
def file_read(line):
list1.append(line[10])
# bunch of other code which process other column indexes on `line`
inputData = sc.textFile(fileName).zipWithIndex().filter(lambda (line,rownum): rownum>0).map(lambda (line, rownum): line)
column_val = (inputData
.map(lambda line: line.split(","))
.filter(lambda line: len(line) >1 )
.map(file_read))
当我执行这部分代码时list1
仍然是空的,即使line[10]
中有数据,因为我在上面相同函数的代码的其他部分中使用它。好像它只是没有将它附加到列表中。我如何形成上面的列表?
答案 0 :(得分:6)
嗯,它实际上附加到list1
,问题不在于您正在考虑的问题。闭包中引用的每个变量都被序列化并发送给工作者。它也适用于list1
。
每个分区都会收到它自己的list1
副本,当调用file_read
时,数据会附加到此副本,当给定的映射阶段完成时,它会超出范围并被丢弃。< / p>
不是特别优雅的代码,但你应该看到它真的发生在这里:
rdd = sc.parallelize(range(100), 5)
line1 = []
def file_read(line):
list1.append(line)
print len(list1)
return line
xs = rdd.map(file_read).collect()
修改强>
Spark提供两种类型的共享变量。 Broadcast variables,只能从worker透视图中读取,而accumulators只能从驱动程序的角度编写。
默认情况下,accumulators仅支持数字变量,主要用作计数器。但是可以定义自定义累加器。为此,您必须扩展AccumulatorParam
课程并提供自定义zero
和addInPlace
实施:
class ListParam(AccumulatorParam):
def zero(self, v):
return []
def addInPlace(self, acc1, acc2):
acc1.extend(acc2)
return acc1
接下来,您可以按如下方式重新定义file_read
:
def file_read1(line):
global list1 # Required otherwise the next line will fail
list1 += [line]
return line
使用示例:
list1 = sc.accumulator([], ListParam())
rdd = sc.parallelize(range(10)).map(file_read1).collect()
list1.value
即使可以使用这样的累加器,在实践中使用也可能是昂贵的,并且在最坏的情况下它可能会使驱动器崩溃。相反,您可以简单地使用另一种转换:
tmp = (inputData
.map(lambda line: line.split(","))
.filter(lambda line: len(line) >1 ))
def line_read2(line): return ... # Just a core logic
line1 = tmp.map(lambda line: line[10])
column_val = tmp.map(line_read2)
旁注:
您提供的代码没有做任何事情。 Spark中的转换只是对必须完成的操作的描述,但在调用操作数据之前,实际上并没有执行任何操作。