我在Scala中DataFrame
中转换值时出现问题。我的初始DataFrame
看起来像这样:
+----+----+----+----+
|col1|col2|col3|col4|
+----+----+----+----+
| A| X| 6|null|
| B| Z|null| 5|
| C| Y| 4|null|
+----+----+----+----+
col1
和col2
类型为String
,col3
和col4
为Int
。
结果应如下所示:
+----+----+----+----+------+------+------+
|col1|col2|col3|col4|AXcol3|BZcol4|CYcol4|
+----+----+----+----+------+------+------+
| A| X| 6|null| 6| null| null|
| B| Z|null| 5| null| 5| null|
| C| Y| 4| 4| null| null| 4|
+----+----+----+----+------+------+------+
这意味着应该在col1
,col2
和提取值的列之后命名三个新列。提取的值来自列col2
,col3
或col5
,具体取决于哪个值不是null
。
那么如何实现呢?我首先想到了这样的UDF
:
def myFunc (col1:String, col2:String, col3:Long, col4:Long) : (newColumn:String, rowValue:Long) = {
if col3 == null{
val rowValue=col4;
val newColumn=col1+col2+"col4";
} else{
val rowValue=col3;
val newColumn=col1+col2+"col3";
}
return (newColumn, rowValue);
}
val udfMyFunc = udf(myFunc _ ) //needed to treat it as partially applied function
但我怎样才能以正确的方式从数据框中调用它?
当然,上面的所有代码都是垃圾,可能有更好的方法。由于我只是在处理第一段代码片段,请告诉我们......将Int
值与null
进行比较已经无效了。
任何帮助表示赞赏!谢谢!
答案 0 :(得分:3)
有一种更简单的方法:
val df3 = df2.withColumn("newCol", concat($"col1", $"col2")) //Step 1
.withColumn("value",when($"col3".isNotNull, $"col3").otherwise($"col4")) //Step 2
.groupBy($"col1",$"col2",$"col3",$"col4",$"newCol") //Step 3
.pivot("newCol") // Step 4
.agg(max($"value")) // Step 5
.orderBy($"newCol") // Step 6
.drop($"newCol") // Step 7
df3.show()
步骤如下:
.agg(first($"value"))
如果值恰好是字符串而不是数字类型 - max函数只能应用于数字类型由于@ user8371915帮助我首先回答了我自己的支点问题。
结果如下:
+----+----+----+----+----+----+----+
|col1|col2|col3|col4| AX| BZ| CY|
+----+----+----+----+----+----+----+
| A| X| 6|null| 6|null|null|
| B| Z|null| 5|null| 5|null|
| C| Y| 4| 4|null|null| 4|
+----+----+----+----+----+----+----+
您可能必须使用列标题字符串连接来获得正确的结果。
答案 1 :(得分:1)
好的,我有一个解决方法来实现我想要的。我做了以下事情:
(1)我根据此建议生成了一个包含[newColumnName,rowValue]
元组的新列Derive multiple columns from a single column in a Spark DataFrame
case class toTuple(newColumnName: String, rowValue: String)
def createTuple (input1:String, input2:String) : toTuple = {
//do something fancy here
var column:String= input1 + input2
var value:String= input1
return toTuple(column, value)
}
val UdfCreateTuple = udf(createTuple _)
(2)将函数应用于DataFrame
dfNew= df.select($"*", UdfCreateTuple($"col1",$"col2").alias("tmpCol")
(3)创建具有不同值newColumnName
val dfDistinct = dfNew.select($"tmpCol.newColumnName").distinct
(4)创建一个具有不同值的数组
var a = dfDistinct.select($"newCol").rdd.map(r => r(0).asInstanceOf[String])
var arrDistinct = a.map(a => a).collect()
(5)创建键值映射
var seqMapping:Seq[(String,String)]=Seq()
for (i <- arrDistinct){
seqMapping :+= (i,i)
}
(6)将映射应用于原始数据帧,参见Mapping a value into a specific column based on annother column
val exprsDistinct = seqMapping.map { case (key, target) =>
when($"tmpCol.newColumnName" === key, $"tmpCol.rowValue").alias(target) }
val dfFinal = dfNew.select($"*" +: exprsDistinct: _*)
嗯,这有点麻烦但是我可以在不知道有多少列的情况下派生出一组新列,同时将值传输到新列中。