【问题标题】:How to reduce multiple case when in scala-spark在scala-spark中如何减少多个案例
【发布时间】:2021-02-01 16:42:49
【问题描述】:

新手问题,你如何优化/减少这些表达式:

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 行纯 when case,并希望减少它,以便我的代码更清晰。

【问题讨论】:

    标签: scala apache-spark case-when


    【解决方案1】:

    您可以在地图列表中使用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语句,直到得到非null结果。

    val new_col = coalesce((maplist.map(x => when(x._1, x._2)) :+ z):_*)
    

    【讨论】:

    • 非常感谢,我下午去试试。
    • 更新:两种解决方案都有效,谢谢!我将尝试使用替代方法(coalesce)运行更多测试,但到目前为止一切都很好。
    【解决方案2】:

    您可以创建一个简单的递归方法来组装嵌套的-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 组装嵌套条件:

    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")
    

    【讨论】:

      【解决方案3】:

      另一种方式,将 else 作为 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-05
        • 1970-01-01
        • 2021-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        • 1970-01-01
        相关资源
        最近更新 更多