如何避免在Spark Scala中迭代使用withColumn?

如何避免在Spark Scala中迭代使用withColumn?,scala,dataframe,apache-spark,apache-spark-sql,Scala,Dataframe,Apache Spark,Apache Spark Sql,目前,我们有一个迭代使用withColumn的代码。如果不是条件检查,我们会在此基础上进行算术计算 示例代码: df.withColumn("col4", when(col("col1")>10, col("col2").+col("col3")).otherwise(col("col2"))) 其他算术计算会对另外40列进行迭代。 总记录数-200万 重复的代码: df.withColumn(colName1, when((col(amountToSubtract).<("0

目前,我们有一个迭代使用withColumn的代码。如果不是条件检查,我们会在此基础上进行算术计算

示例代码:

df.withColumn("col4", when(col("col1")>10, col("col2").+col("col3")).otherwise(col("col2")))
其他算术计算会对另外40列进行迭代。 总记录数-200万

重复的代码:

  df.withColumn(colName1, when((col(amountToSubtract).<("0")) && (col(colName1).===("0")) && (col(amountToSubtract).<=(col(colName2))) && (col(colName2).!==("0")), col(colName2)).otherwise(col(colName1))).
      withColumn(amountToSubtract, when(col(amountToSubtract).!==("0"), col(amountToSubtract).-(col(colName2))).otherwise(col(amountToSubtract))).
      withColumn(colName1, when((col(amountToSubtract).>("0")) && (col(colName1).===("0")), col(colName2).+(col(amountToSubtract))).otherwise(col(colName1))).
      withColumn(amountToSubtract, when((col(amountToSubtract).>("0")) && (col(colName1).===("0")), "0").otherwise(col(amountToSubtract)))
df.with column(colName1,when((col(amountToSubtract)。(“0”)&&(col(colName1)。==(0”),“0”)。否则(col(amountToSubtract)))
接下来是7或8组其他计算

而这项工作在这一点上搁置的时间更长。有时它会抛出GC开销错误。
意识到迭代使用.withColumn对性能没有好处,我找不到替代方法来实现上述条件检查。友好的协助。

< P>您可以尝试将当前执行的所有逻辑封装成“一个/另一个”,作为一个UDF,输入一个数组,作为生成新列值时需要考虑的所有列值的数组,并作为输出返回所有生成的列值的数组。UDF有时也有自己的性能问题,但这可能值得一试。下面是我想到的技术的简单说明:

object SO extends App {

  val sparkSession = SparkSession.builder().appName("simple").master("local[*]").getOrCreate()
  sparkSession.sparkContext.setLogLevel("ERROR")

  import sparkSession.implicits._

  case class Record(col1: Int, col2: Int, amtToSubtract: Int)

  val recs = Seq(
    Record(1, 2, 3),
    Record(11, 2, 3)
  ).toDS()


  val colGenerator : Seq[Int] => Seq[Int] =
    (arr: Seq[Int]) =>  {
      val (in_c1, in_c2, in_amt_sub) = (arr(0), arr(1), arr(2))

      val newColName1_a = if (in_amt_sub < 0 && in_c1 == 0 && in_amt_sub < in_c2 &&  in_c2 != 0) {
        in_c2
      }
      else {
        in_c1
      }
      val newAmtSub_a = if (in_amt_sub != 0) {
        in_amt_sub - in_c2
      } else {
        in_amt_sub
      }

      val newColName1_b  = if (  newAmtSub_a  > 0 &&  newColName1_a  == 0 ) {
        in_c2  + newAmtSub_a
      } else {
        newColName1_a
      }

      val newAmtSub_b = if (newAmtSub_a  > 0  && newColName1_b   == 0) {
        0
      } else {
        newAmtSub_a
      }

      Seq(newColName1_b,  newAmtSub_b)
    }

  val colGeneratorUdf = udf(colGenerator)

  // Here the first column in the generated array is 'col4', the UDF could equivalently generate as many
  // other values as you want from the input array of column values.
  //
  val afterUdf = recs.withColumn("colsInStruct",   colGeneratorUdf (array($"col1", $"col2", $"amtToSubtract")))
  afterUdf.show()
  // RESULT
  //+----+----+-------------+------------+
  //|col1|col2|amtToSubtract|colsInStruct|
  //+----+----+-------------+------------+
  //|   1|   2|            3|      [1, 1]|
  //|  11|   2|            3|     [11, 1]|
  //+----+----+-------------+------------+


}
对象如此扩展应用程序{
val sparkSession=sparkSession.builder().appName(“simple”).master(“local[*]).getOrCreate()
sparkSession.sparkContext.setLogLevel(“错误”)
导入sparkSession.implicits_
案例类记录(col1:Int、col2:Int、amtToSubtract:Int)
val recs=序列(
记录(1,2,3),
记录(11,2,3)
).toDS()
val colGenerator:Seq[Int]=>Seq[Int]=
(arr:Seq[Int])=>{
val(在c1中,在c2中,在金额中)=(arr(0),arr(1),arr(2))
val newColName1\u a=if(单位金额<0&&in\u c1==0&&in\u金额0&&newColName1_a==0){
in_c2+newAmtSub_a
}否则{
newColName1_a
}
val newAmtSub_b=if(newAmtSub_a>0&&newColName1_b==0){
0
}否则{
新罕布什尔州
}
序号(新名称1\u b、新名称1\u b)
}
val colGeneratorUdf=udf(colGenerator)
//这里生成的数组中的第一列是“col4”,UDF可以等效地生成尽可能多的列
//从列值的输入数组中选择所需的其他值。
//
val afterUdf=recs.withColumn(“colsInStruct”、colGeneratorUdf(数组($“col1”、“col2”、“amtToSubtract”))
afterUdf.show()
//结果
//+----+----+-------------+------------+
//|col1 | col2 | amtt减去| colsInStruct|
//+----+----+-------------+------------+
//|   1|   2|            3|      [1, 1]|
//|  11|   2|            3|     [11, 1]|
//+----+----+-------------+------------+
}

我不确定您试图应用的逻辑。但这里是foldleft的想法:

  val colList = List("col1", "col2", "col3")
  val df: DataFrame = ???
  colList.foldLeft(df){case(df, colName1) => df
     .withColumn(colName1, when((col(amountToSubtract).<("0")) && (col(colName1).=== ("0")) && (col(amountToSubtract).<=(col(colName2))) && (col(colName2).!==("0")), col(colName2)).otherwise(col(colName1))).
     .withColumn(amountToSubtract, when(col(amountToSubtract).!==("0"), col(amountToSubtract).-(col(colName2))).otherwise(col(amountToSubtract))).
     .withColumn(colName1, when((col(amountToSubtract).>("0")) && (col(colName1).===("0")), col(colName2).+(col(amountToSubtract))).otherwise(col(colName1))).
     .withColumn(amountToSubtract, when((col(amountToSubtract).>("0")) && (col(colName1).===("0")), "0").otherwise(col(amountToSubtract)))
val colList=List(“col1”、“col2”、“col3”)
val df:DataFrame=???
colList.foldLeft(df){case(df,colName1)=>df
.带列(colName1,when((col(amountToSubtract)(“0”))和&(col(colName1)(=“0”),“0”)。否则(col(amountToSubtract)))

}

感谢您的帮助。这里的挑战是,1。否则,所有40列的逻辑都不相同。2.我在这里看到的主要问题是——迭代withColumns的使用。所以,我的理解是,将UDF集成到这个性能已经下降的迭代列代码中只会使情况变得更糟。如果我的理解有误,请纠正。尽管如此,我们已经开始尝试UDF选项。我会让你保持在同一条线上,明白了。我认为您可以做的是移动UDF中所有40列的逻辑。这有意义吗?如果不是。。。请随意更新您的示例,增加更多列的逻辑,如果不清楚,我可以向您展示如何将其移动到UDF。嗨,Chris,根据要求-使用多次重复的代码更新了问题。我更新了答案。。我不确定你的商业逻辑是否100%正确。。但希望能够传达总体想法;^)@DasarathyDR-进展如何?我很好奇你是否尝试过,表现是否有所改善。希望成功!如果列具有相同的逻辑,则可以使用foldleft并添加列。获取列列表并应用一个foldleft,其中dataframe作为零元素。在foldleft中应用withColumn方法。@firas感谢您的输入!我刚才提到的代码片段是一个块。同一块4个withColumns被再使用8次。因此,我发现使用FoldLeft很难实现它。如果你能为我的示例代码分享一个示例foldleft,我可以进一步扩展它。我发布了答案。如果有帮助,你能把它标记为好的吗。如果没有,请告诉我,我将尝试更新。@firas尚未尝试您的解决方案。因为我已经尝试了这里提供的其他解决方案,因为UDF对我来说更熟悉。下周我将尝试你的建议。会让你保持在同一条线上。