如何在 scala-spark 中减少多个案例
How to reduce multiple case when in scala-spark
新手问题,你怎么optimize/reduce这样的表达:
when(x1._1,x1._2).when(x2._1,x2._2).when(x3._1,x3._2).when(x4._1,x4._2).when(x5._1,x5._2)....
.when(xX._1,xX._2).otherwise(z)
x1、x2、x3、xX 是映射,其中 x1._1 是条件,x._2 是“然后”。
我试图将地图保存在列表中,然后使用 map-reduce 但它生成了:
when(x1._1,x1._2).otherwise(z) && when(x2._1,x2._2).otherwise(z)...
这是错误的。我有 10 行 pure when case 并且想减少它以便我的代码更清晰。
您可以在地图列表中使用foldLeft
:
val maplist = List(x1, x2) // add more x if needed
val new_col = maplist.tail.foldLeft(when(maplist.head._1, maplist.head._2))((x,y) => x.when(y._1, y._2)).otherwise(z)
另一种方法是使用 coalesce
。如果不满足条件,则when
语句返回null,并会计算下一个when
语句,直到得到一个非空的结果。
val new_col = coalesce((maplist.map(x => when(x._1, x._2)) :+ z):_*)
另一种方法是将 otherwise 作为初始值传递给 foldLeft
:
val maplist = Seq(Map(col("c1") -> "value1"), Map(col("c2") -> "value2"))
val newCol = maplist.flatMap(_.toSeq).foldLeft(lit("z")) {
case (acc, (cond, value)) => when(cond, value).otherwise(acc)
}
// gives:
// newCol: org.apache.spark.sql.Column = CASE WHEN c2 THEN value2 ELSE CASE WHEN c1 THEN value1 ELSE z END END
您可以创建一个简单的递归方法来 assemble 嵌套-when/otherwise
条件:
import org.apache.spark.sql.Column
def nestedCond(cols: Array[String], default: String): Column = {
def loop(ls: List[String]): Column = ls match {
case Nil => col(default)
case c :: tail => when(col(s"$c._1"), col(s"$c._2")).otherwise(loop(tail))
}
loop(cols.toList).as("nested-cond")
}
测试方法:
val df = Seq(
((false, 1), (false, 2), (true, 3), 88),
((false, 4), (true, 5), (true, 6), 99)
).toDF("x1", "x2", "x3", "z")
val cols = df.columns.filter(_.startsWith("x"))
// cols: Array[String] = Array(x1, x2, x3)
df.select(nestedCond(cols, "z")).show
// +-----------+
// |nested-cond|
// +-----------+
// | 3|
// | 5|
// +-----------+
或者,使用 foldRight
到 assemble 嵌套条件:
def nestedCond(cols: Array[String], default: String): Column =
cols.foldRight(col(default)){ (c, acc) =>
when(col(s"$c._1"), col(s"$c._2")).otherwise(acc)
}.as("nested-cond")
新手问题,你怎么optimize/reduce这样的表达:
when(x1._1,x1._2).when(x2._1,x2._2).when(x3._1,x3._2).when(x4._1,x4._2).when(x5._1,x5._2)....
.when(xX._1,xX._2).otherwise(z)
x1、x2、x3、xX 是映射,其中 x1._1 是条件,x._2 是“然后”。
我试图将地图保存在列表中,然后使用 map-reduce 但它生成了:
when(x1._1,x1._2).otherwise(z) && when(x2._1,x2._2).otherwise(z)...
这是错误的。我有 10 行 pure when case 并且想减少它以便我的代码更清晰。
您可以在地图列表中使用foldLeft
:
val maplist = List(x1, x2) // add more x if needed
val new_col = maplist.tail.foldLeft(when(maplist.head._1, maplist.head._2))((x,y) => x.when(y._1, y._2)).otherwise(z)
另一种方法是使用 coalesce
。如果不满足条件,则when
语句返回null,并会计算下一个when
语句,直到得到一个非空的结果。
val new_col = coalesce((maplist.map(x => when(x._1, x._2)) :+ z):_*)
另一种方法是将 otherwise 作为初始值传递给 foldLeft
:
val maplist = Seq(Map(col("c1") -> "value1"), Map(col("c2") -> "value2"))
val newCol = maplist.flatMap(_.toSeq).foldLeft(lit("z")) {
case (acc, (cond, value)) => when(cond, value).otherwise(acc)
}
// gives:
// newCol: org.apache.spark.sql.Column = CASE WHEN c2 THEN value2 ELSE CASE WHEN c1 THEN value1 ELSE z END END
您可以创建一个简单的递归方法来 assemble 嵌套-when/otherwise
条件:
import org.apache.spark.sql.Column
def nestedCond(cols: Array[String], default: String): Column = {
def loop(ls: List[String]): Column = ls match {
case Nil => col(default)
case c :: tail => when(col(s"$c._1"), col(s"$c._2")).otherwise(loop(tail))
}
loop(cols.toList).as("nested-cond")
}
测试方法:
val df = Seq(
((false, 1), (false, 2), (true, 3), 88),
((false, 4), (true, 5), (true, 6), 99)
).toDF("x1", "x2", "x3", "z")
val cols = df.columns.filter(_.startsWith("x"))
// cols: Array[String] = Array(x1, x2, x3)
df.select(nestedCond(cols, "z")).show
// +-----------+
// |nested-cond|
// +-----------+
// | 3|
// | 5|
// +-----------+
或者,使用 foldRight
到 assemble 嵌套条件:
def nestedCond(cols: Array[String], default: String): Column =
cols.foldRight(col(default)){ (c, acc) =>
when(col(s"$c._1"), col(s"$c._2")).otherwise(acc)
}.as("nested-cond")