我正在尝试根据输入对所有数据框列进行类型转换,如下所示,如何使用转换所有数据框列的单个命令执行此操作?以下代码工作正常,但我需要一个适用于任何数据框的通用命令。提前感谢您的善意建议。
for (colIndex <- 0 to tmpDF.columns.length - 1) {
val columns = df.columns
newdf = df.withColumn(columns(0), df(columns(0)).cast(dataType(0)))
.withColumn(columns(1), df(columns(1)).cast(dataType(1)))
.withColumn(columns(2), df(columns(2)).cast(dataType(2)))
.withColumn(columns(3), df(columns(3)).cast(dataType(3)))
.withColumn(columns(4), df(columns(4)).cast(dataType(4)))
// newdf = df.withColumn(columns(colIndex), df(columns(colIndex)).cast(dataType(colIndex))) --> This didn't work, only last column was updated
}
答案 0 :(得分:2)
使用更多&#34;功能方式&#34;的另一个建议是使用foldLeft
。在这种情况下,您的&#34; dataType&#34;变量可以是Map[String, DataType]
(或Map[String, String]
)将每个列名映射到您想要给出的新类型:
import org.apache.spark.sql.types._
import spark.implicits._
val df = Seq((1, "1"), (2, "2")).toDF("col1", "col2")
val dataType: Map[String, DataType] = Map(
"col1" -> StringType,
"col2" -> IntegerType
)
val result = df.columns.foldLeft(df) {
(newDF, colName) => newDF.withColumn(colName, newDF(colName).cast(dataType(colName)))
}
result.show
请注意,不需要可变变量,在Scala中通常应该是我们的目标。
如果要继续使用索引而不是列名,可以相应地调整上面的代码:
val dataType = List(StringType, IntegerType)
val cols = df.columns
val result = cols.indices.foldLeft(df) {
(newDF, i) => newDF.withColumn(cols(i), newDF(cols(i)).cast(dataType(i)))
}
在这两种情况下,输出为:
df: org.apache.spark.sql.DataFrame = [col1: int, col2: string]
result: org.apache.spark.sql.DataFrame = [col1: string, col2: int]
+----+----+
|col1|col2|
+----+----+
| 1| 1|
| 2| 2|
+----+----+
Here您可以找到有关foldLeft
。
答案 1 :(得分:1)
它只会更新最后一列,因为您始终追加到df
的列,您应该继续追加到newDF
这应该有效:
var newDF = df
val columns = df.columns
for (colIndex <- tmpDF.columns.indices) {
newDF = newDF.withColumn(columns(colIndex), df(columns(colIndex)).cast(dataType(colIndex)))
}
答案 2 :(得分:1)
这两个答案都在使用了很多 - 也没关系,但是每个withColumn都会在查询计划中添加Projection。做一个大选择会更好:
val columns = df.columns
val columnsNew = new Array[Column]
for (colIndex <- tmpDF.columns.indices) {
columnsNew(colIndex) = columns(colIndex), df(columns(colIndex)).cast(dataType(colIndex)))
}
var newDF = df.select(columnsNew :_*)
您应该只有一个投影,因此它可以在非常大的数据集上更快 - CodeGen可以更好地处理查询计划中的预测量更少,这就是为什么选择多个单一选择会更好列:))