我的Python 3程序读取CSV文件,对其执行一些处理,然后将结果转换为DataFrame。 因为CSV文件可能有数百万行,所以我通过驻留在内存中的10,000行批处理,然后将结果转换为DataFrame。
因为我想在Hadoop中形成一个单一的镶木地板文件,我想编写一个大的DataFrame(self.df),它是所有小型数据帧的串联,如下所示:
def parallel_process(self, batch, processor):
transformed = Pool().map(processor.transform, batch)
return self.spark.sparkContext.parallelize(transformed).toDF(self.schema)
def process_rows(self, reader, processor):
line_num = 0
batch = []
for row in reader:
line_num += 1
# parallel process a batch of 10000 rows
if line_num % 10000 == 0:
df = processor.parallel_process(batch)
if self.df is None:
self.df = df
else:
self.df = self.df.union(df)
batch.clear()
print(line_num)
else:
batch.append(row)
# last (incomplete) batch
df = processor.parallel_process(batch)
if self.df is None:
self.df = df
else:
self.df = self.df.union(df)
# write the dataframe
self.df.write.mode('append').format('parquet').save('table.parquet')
我的问题是,鉴于DataFrames是不可变的,它是内存和计算效率这么做还是应该谨慎使用联合? 如果不是将10,000行的常量大小的批量DF附加到增长较大的DF中,而是将所有批量DataFrame存储在一个数组中(例如),并在最后将它们连接起来,这有可能吗?什么是内存和CPU使用含义?
答案 0 :(得分:1)
因为CSV文件可能有数百万行,所以我会通过驻留在内存中的10,000行来处理,然后将结果转换为DataFrame。
这没有任何意义,并且无法按预期工作。事实上,通过.parallelize
传递的所有数据都将存储在Python解释器内存中,并在JVM上进一步复制。
因为我想在Hadoop中形成一个单一的镶木地板文件,我想编写一个大的DataFrame(self.df),这是所有小型数据帧的串联,
一般来说,它也不会像这样工作。输出文件的数量不依赖于DataFrames
的数量 - 它取决于分区的数量。除非您coalesce(1)
/ repartition(1)
(在任何现实生活场景中很难推荐),否则您将获得多个输出文件。
这样做是记忆和计算效率还是应该谨慎使用联合?
本地复杂性在合并DataFrames
的数量上是非线性的(过去是指数级的,但在最新版本中实现了改进),因此效率不高。好消息是它不影响任务执行时间,只影响驱动程序代码。
你真正应该做的是,与csv
读者一起阅读数据,与parquet
作者一起写:
(spark
.read.format("csv").load(inpath)
... # Apply whatever transformations you neeed
.write.format("parquet").save(outpath))
(根据您的情况适用选项进行调整)。