在Spark DataFrame中计算大于0的值的更快方法?

时间:2018-07-13 21:46:07

标签: apache-spark apache-spark-sql

我有一个Spark DataFrame,其中所有字段均为整数类型。我需要计算有多少个单个单元格大于0。

我在本地运行,并且有一个DataFrame,具有17,000行和450列。

我尝试了两种方法,但均会产生缓慢的结果:

版本1:

(for (c <- df.columns) yield df.where(s"$c > 0").count).sum

版本2:

df.columns.map(c => df.filter(df(c) > 0).count)

此计算需要80秒钟的挂钟时间。使用Python Pandas,只需几分之一秒。我知道对于小型数据集和本地操作Python may perform better,但这似乎很极端。

尝试进行火花火花比较,我发现对相同数据(转换为RowMatrix)运行MLlib的PCA算法需要不到2秒的时间!

我应该使用更有效的实现方式吗?

如果没有,那么看似复杂得多的PCA计算又如何这么快?

2 个答案:

答案 0 :(得分:1)

做什么

import org.apache.spark.sql.functions.{col, count, when}

df.select(df.columns map (c => count(when(col(c) > 0, 1)) as c): _*)

为什么

您的两次尝试都会创建与列数成比例的作业数。仅计算执行计划和安排作业的成本很高,并且根据数据量会增加大量开销。

此外,每次执行作业时都可能从磁盘加载数据和/或解析数据,除非完全缓存数据并具有显着的内存安全裕度,以确保不会驱逐缓存的数据。

这意味着,在最坏的情况下,您使用的类似嵌套循环的结构在列数方面可能大致呈二次方。

上面显示的代码可同时处理所有列,仅需进行一次数据扫描。

答案 1 :(得分:1)

您的方法存在的问题是,将扫描文件的每一列(除非您已将其缓存在内存中)。单个FileScan的最快方法应该是:

import org.apache.spark.sql.functions.{explode,array}

val cnt: Long = df
  .select(
    explode(
      array(df.columns.head,df.columns.tail:_*)
    ).as("cell")
  )
.where($"cell">0).count

我仍然认为它会比使用Pandas慢,因为Spark由于并行化引擎而具有一定的开销