基于需要外部API调用的现有列创建新Spark星际数据列的最佳方法是什么?

时间:2018-04-05 15:00:15

标签: apache-spark pyspark apache-spark-sql user-defined-functions

我有一个数据帧,我正在使用基于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可能再次运行。 (?)

我的问题是

  1. 哪些Spark api调用实际上导致udf运行(或重新运行),以及如何构造调用以仅运行UDF一次,以便使用UDF的python函数输出文本来创建新列。 / LI>
  2. 我已经读过应该避免使用Python UDF,因为它们很慢(看起来是正确的)所以当我需要使用API​​调用来生成新列时,我还有哪些替代方法?

1 个答案:

答案 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,这种方法更有效,但仅支持有限的类型子集。