isin在spark中的withcolumn函数中引发stackoverflow错误

时间:2019-07-16 12:16:27

标签: scala apache-spark apache-spark-sql

我在我的Scala应用程序中使用spark 2.3。我有一个从spark sql创建的数据框,该数据框在我共享的示例代码中名为sqlDF。我有一个包含以下项目的字符串列表

列表[]字符串列表项

  

-9,-8,-7,-6

我想将数据框中所有列中与此列表项匹配的所有值替换为0。

初始数据框

column1 | column2 | column3
1       |1        |1       
2       |-5       |1       
6       |-6       |1       
-7      |-8       |-7       

它必须返回到

column1 | column2 | column3
1       |1        |1       
2       |-5       |1       
6       |0        |1       
0       |0        |0

为此,我为sqlDF中的所有列(超过500个)设置了以下查询。

sqlDF = sqlDF.withColumn(currColumnName, when(col(currColumnName).isin(stringList:_*), 0).otherwise(col(currColumnName)))

但是,如果我只选择一列进行迭代,则会收到以下错误,但是如果我在上面的代码中进行500列迭代,它将失败

  

线程“ streaming-job-executor-0”中的异常   java.lang.StackOverflowError在   scala.collection.generic.GenTraversableFactory $ GenericCanBuildFrom.apply(GenTraversableFactory.scala:57)     在   scala.collection.generic.GenTraversableFactory $ GenericCanBuildFrom.apply(GenTraversableFactory.scala:52)     在   scala.collection.TraversableLike $ class.builder $ 1(TraversableLike.scala:229)     在   scala.collection.TraversableLike $ class.map(TraversableLike.scala:233)     在scala.collection.immutable.List.map(List.scala:285)在   org.apache.spark.sql.catalyst.trees.TreeNode $$ anonfun $ 4.apply(TreeNode.scala:333)     在   org.apache.spark.sql.catalyst.trees.TreeNode.mapProductIterator(TreeNode.scala:187)

我想念的是什么?

3 个答案:

答案 0 :(得分:2)

这是在left anti joincolumnX之间应用X的另一种方法,其中X是传输到数据框中的项目列表。左反连接将返回X中不存在的所有项目,我们通过外部连接将它们连接在一起的结果(可以将其替换为左连接以获得更好的性能,但这会排除全零的记录,即id == 3)基于分配给monotonically_increasing_id的ID:

import org.apache.spark.sql.functions.{monotonically_increasing_id, col}

val df = Seq(
(1, 1, 1),       
(2, -5, 1),       
(6, -6, 1),       
(-7, -8, -7))
.toDF("c1", "c2", "c3")
.withColumn("id", monotonically_increasing_id())

val exdf = Seq(-9, -8, -7, -6).toDF("x")

df.columns.map{ c =>
   df.select("id", c).join(exdf, col(c) === $"x", "left_anti")
}
.reduce((df1, df2) => df1.join(df2, Seq("id"), "outer"))
.na.fill(0)
.show

输出:

+---+---+---+---+
| id| c1| c2| c3|
+---+---+---+---+
|  0|  1|  1|  1|
|  1|  2| -5|  1|
|  3|  0|  0|  0|
|  2|  6|  0|  1|
+---+---+---+---+

答案 1 :(得分:1)

foldLeft非常适合您的情况,如下所示

val df = spark.sparkContext.parallelize(Seq(
  (1, 1, 1),
  (2, -5, 1),
  (6, -6, 1),
  (-7, -8, -7)
)).toDF("a", "b", "c")

val list = Seq(-7, -8, -9)

val resultDF = df.columns.foldLeft(df) { (acc, name) => {
    acc.withColumn(name, when(col(name).isin(list: _*), 0).otherwise(col(name)))
  }
}

输出:

+---+---+---+
|a  |b  |c  |
+---+---+---+
|1  |1  |1  |
|2  |-5 |1  |
|6  |-6 |1  |
|0  |0  |0  |
+---+---+---+

答案 2 :(得分:0)

我建议您广播String列表:

val stringList=sc.broadcast(<Your List of List[String]>)

在此之后使用此:

sqlDF = sqlDF.withColumn(currColumnName, when(col(currColumnName).isin(stringList.value:_*), 0).otherwise(col(currColumnName)))

确保您的currColumnName也是字符串格式。比较应该是字符串到字符串