如何将UDF应用于收集的行? (失败,出现“java.lang.ClassCastException:java.lang.String无法强制转换为org.apache.spark.sql.Column”)

时间:2017-04-21 19:39:52

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

我有两个数据框,其中包含这样的数据

第一个数据框就是这样的

+-----+-----------+-----+----------------------+
|value|mergeValues|table|columnName            |
+-----+-----------+-----+----------------------+
|1    |1,2,3      |     |columnName1           |
|2    |4,5,6,7    |     |columnName1           |
|3    |8,9        |     |columnName1           |
|1    |1,2,3      |     |columnName4           |
|2    |4,5,6,7    |     |columnName4           |
|3    |8,9        |     |columnName4           |
|1    |1,2,3      |     |columnName5           |
|2    |4,5,6,7    |     |columnName5           |
|3    |8,9        |     |columnName5           |
|1    |1,2,3      |     |columnName6           |
+-----+-----------+-----+----------------------+

另一个数据帧结构如下所示

columnName1 | columnName2 | columnName3 |columnName4 |columnName5 | columnName6 
1               
3
2
4
5

现在我必须像这样创建映射数据帧。

Mapping logic is :
 get value from 2ndDF and check firstdf mergeValue if that contains then map to firstdf value.
here value of 2nd df columnName1 is 1 it is present in firstDf mergeValues list map it to firstDf[value] which is 1. same for 2,3,4,5,6,7 ... 
columnName1 | columnName2 | columnName3 |columnName4 |columnName5 | columnName6 
1               
1
1
2
2

要做到这一点,我正在使用UDF方式,但它失败了,创建此数据框的正确方法是什么。

我的代码是:

val firstDF=sparkSession.read.load(first)
val testDF = sparkSession.read.load(test)

val populateColumn: ((String, String, String) => String) = (mergeValues: String, value: String,
                                                            actualValue: String) => {
  if (mergeValues.contains(actualValue.trim)) {
    value
  } else {
    actualValue
  }

}


val populateColumnUdf = udf(populateColumn)

val firstDFList=firstDF.collect
firstDFList.foreach(Case => {
  println(Case)
  testDF.withColumn(Case.getAs("columnName"), populateColumnUdf(Case.getAs("mergeValues"),
    Case.getAs("value"), col(Case.getAs("columnName"))))
})

testDF.show

这是我得到的错误

  

java.lang.String无法强制转换为org.apache.spark.sql.Column   java.lang.ClassCastException:java.lang.String无法强制转换为org.apache.spark.sql.Column

2 个答案:

答案 0 :(得分:1)

检查执行以下操作的部件中的类型:

populateColumnUdf(Case.getAs("mergeValues"), Case.getAs("value"), col(Case.getAs("columnName")))

Case的类型为RowgetAs为您提供给定fieldName的值。(请参阅org.apache.spark.sql.Row)。那个Column期望的肯定 populateColumnUdf。在这种情况下,您宁愿使用populateColumn Scala函数。您已离开DataFrame / UDF上下文,仅在Scala中。

答案 1 :(得分:1)

正如Jacek Laskowski所说,在你的代码中, Case 是一个org.apache.spark.sql.Row类型

上调用 getAs 会在特定字段返回此行的值(例如,第一个数据框中第一行的值为“1, 2,3“在”mergeValues“栏)

withColumn 方法需要两个参数。第一个参数是要替换的列的名称,第二个参数是替换列的 org.apache.spark.sql.Column

在第二个参数中,是您提供udf的位置。 udf将列作为参数。这些输入列的数据类型应该与udf包含的函数(在本例中为 populateColumn )的输入类型相对应。

不确定你的col()函数在你在代码中提供给你的udf的参数中做了什么。

如果我正确理解您的代码,您会找到类似以下的内容(此代码不完整且无法运行):

val firstDF = sparkSession.read.load(first)
val testDF = sparkSession.read.load(test)

val populateColumn: ((String, String, String) => String) =
    (mergeValues: String, value: String, actualValue: String) => {
    if (mergeValues.contains(actualValue.trim)) {
      value
    } else {
      actualValue
    }
  }

 val populateColumnUdf = udf(populateColumn)

 val replacementCol = new Column("columnName1")

//mergeValuesCol and valueCol needs to be the columns from firstDF
testDF.withColumn("columnName1", populateColumnUdf(mergeValuesCol, valueCol, replacementCol))

您需要提供 firstDF 的外部列(mergeValues和value)中的值(有关参考,请参阅Passing a data frame column and external list to udf under withColumn)。

或者考虑合并/加入给定条件的两个数据帧。

希望这有点帮助! 感谢编辑问题的人,以便我能理解问题是什么:)