我有一个数据帧,我正在使用基于Python的Jupyter笔记本。我想基于现有列的内容添加其他列,其中新列的内容源自在原始列上运行外部API调用。
我尝试的解决方案是使用基于Python的UDF。第一个单元格包含这样的内容:
def analysis(old_column):
new_column = myapi.analyze(text=old_column)
return(new_column)
analysis_udf = udf(analysis)
和第二个单元格:
df2 = df1.withColumn("col2",analysis_udf('col1'))
df2.select('col2').show(n=5)
我的数据帧相对较大,有大约70000行,而col1可以有100到10000多个字符的文本。当我在单元格2中运行上面的代码时,它实际上似乎运行得相当快(几分钟),并且丢弃了df2数据帧的5行。所以我以为我在做生意。但是,我的下一个单元格有以下代码:
df2.cache()
df2.filter(col('col2').isNull()).count()
此代码的目的是缓存新数据帧的内容以改善对DF的访问时间,然后计算数据帧中有多少条目具有UDF生成的空值。令人惊讶的是(对我而言)需要花费数小时才能运行,最终提供了6的输出。我不清楚为什么第二个细胞快速运转而第三个细胞运行缓慢。虽然df2.select('col2')。show(n = 5)调用会导致UDF在所有行上运行,并且那个会很慢,然后是后续调用以访问数据框的新列将很快。但事实并非如此,所以我认为缓存调用实际上导致UDF在所有行上运行,因此任何后续调用现在都应该很快。所以添加了另一个单元格:
df2.show(n=5)
假设它会快速运行,但是再次,它花费的时间比我预期的要长得多,似乎UDF可能再次运行。 (?)
我的问题是
答案 0 :(得分:1)
我会认为df2.select('col2')。show(n = 5)调用会导致UDF在
上运行
这不是一个正确的假设。鉴于API的限制,Spark将尽可能少地评估数据。因为您使用Python udf
,所以它将评估收集5行所需的最小分区数。
哪些spark api调用实际上导致udf运行(或重新运行),以及如何构造调用以仅运行UDF一次,以便使用UDF的python函数输出文本来创建新列。 / p>
udf
被标记为不确定。我已经读过应该避免使用Python UDF,因为它们很慢(看起来是正确的)所以当我需要使用API调用来生成新列时,我还有哪些替代方法?
除非您想切换到Scala或RDD API,否则唯一的选择是pandas_udf
,这种方法更有效,但仅支持有限的类型子集。