我有一个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计算又如何这么快?
答案 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由于并行化引擎而具有一定的开销