Scala 将spark应用功能并行应用于列
Spark将并行处理数据,但不处理操作。在我的DAG中,我想对每列调用一个函数,如 可以独立于其他列计算每列的值。有没有办法通过spark SQL API实现这种并行性?利用窗口函数有助于大量优化DAG,但仅以串行方式执行 可以找到一个包含更多信息的示例 以下是最起码的例子:Scala 将spark应用功能并行应用于列,scala,apache-spark,parallel-processing,apache-spark-sql,Scala,Apache Spark,Parallel Processing,Apache Spark Sql,Spark将并行处理数据,但不处理操作。在我的DAG中,我想对每列调用一个函数,如 可以独立于其他列计算每列的值。有没有办法通过spark SQL API实现这种并行性?利用窗口函数有助于大量优化DAG,但仅以串行方式执行 可以找到一个包含更多信息的示例 以下是最起码的例子: val df = Seq( (0, "A", "B", "C", "D"), (1, "A", "B", "C", "D"), (0, "d", "a", "jkl", "d"), (0,
val df = Seq(
(0, "A", "B", "C", "D"),
(1, "A", "B", "C", "D"),
(0, "d", "a", "jkl", "d"),
(0, "d", "g", "C", "D"),
(1, "A", "d", "t", "k"),
(1, "d", "c", "C", "D"),
(1, "c", "B", "C", "D")
).toDF("TARGET", "col1", "col2", "col3TooMany", "col4")
val inputToDrop = Seq("col3TooMany")
val inputToBias = Seq("col1", "col2")
val targetCounts = df.filter(df("TARGET") === 1).groupBy("TARGET").agg(count("TARGET").as("cnt_foo_eq_1"))
val newDF = df.toDF.join(broadcast(targetCounts), Seq("TARGET"), "left")
newDF.cache
def handleBias(df: DataFrame, colName: String, target: String = target) = {
val w1 = Window.partitionBy(colName)
val w2 = Window.partitionBy(colName, target)
df.withColumn("cnt_group", count("*").over(w2))
.withColumn("pre2_" + colName, mean(target).over(w1))
.withColumn("pre_" + colName, coalesce(min(col("cnt_group") / col("cnt_foo_eq_1")).over(w1), lit(0D)))
.drop("cnt_group")
}
val joinUDF = udf((newColumn: String, newValue: String, codingVariant: Int, results: Map[String, Map[String, Seq[Double]]]) => {
results.get(newColumn) match {
case Some(tt) => {
val nestedArray = tt.getOrElse(newValue, Seq(0.0))
if (codingVariant == 0) {
nestedArray.head
} else {
nestedArray.last
}
}
case None => throw new Exception("Column not contained in initial data frame")
}
})
现在我想将我的handleBias
函数应用于所有列,不幸的是,这不是并行执行的
val res = (inputToDrop ++ inputToBias).toSet.foldLeft(newDF) {
(currentDF, colName) =>
{
logger.info("using col " + colName)
handleBias(currentDF, colName)
}
}
.drop("cnt_foo_eq_1")
val combined = ((inputToDrop ++ inputToBias).toSet).foldLeft(res) {
(currentDF, colName) =>
{
currentDF
.withColumn("combined_" + colName, map(col(colName), array(col("pre_" + colName), col("pre2_" + colName))))
}
}
val columnsToUse = combined
.select(combined.columns
.filter(_.startsWith("combined_"))
map (combined(_)): _*)
val newNames = columnsToUse.columns.map(_.split("combined_").last)
val renamed = columnsToUse.toDF(newNames: _*)
val cols = renamed.columns
val localData = renamed.collect
val columnsMap = cols.map { colName =>
colName -> localData.flatMap(_.getAs[Map[String, Seq[Double]]](colName)).toMap
}.toMap
可以独立于其他列计算每列的值
虽然这是真的,但对你的案子没什么帮助。您可以生成许多独立的数据帧
,每个数据帧都有自己的附加内容,但这并不意味着您可以自动将其合并到单个执行计划中
handleBias
的每个应用程序都会将数据洗牌两次,并输出DataFrames
与父DataFrames
的数据分布不同。这就是为什么在对列列表进行折叠时,每次添加都必须单独执行的原因
理论上您可以设计一个管道,它可以(用伪代码)这样表示:
- 添加唯一id:
df_with_id = df.withColumn("id", unique_id())
- 独立计算每个df并转换为宽格式:
combined.groupBy("id").pivot("pres._1").agg(first("pres._2"))
- 合并所有部分结果:
combined = dfs.reduce(union)
- 要从长格式转换为宽格式的轴:
combined.groupBy("id").pivot("pres._1").agg(first("pres._2"))
但我怀疑这是否值得大惊小怪。您使用的过程非常繁重,需要大量的网络和磁盘IO
如果列中的x(<代码>总数(x))的数量相对较低,则可以尝试用一个通行证来计算所有统计数据,例如,使用代码< > CuthGueByKy[/COD] >用<代码> MAP[tup2],[StAd],/Cord> >否则考虑向下采样到可以在本地计算统计的水平。< /P>