在Scala中转换所有数据框列的有效方法

时间:2017-07-08 18:40:09

标签: scala apache-spark spark-dataframe

我正在尝试根据输入对所有数据框列进行类型转换,如下所示,如何使用转换所有数据框列的单个命令执行此操作?以下代码工作正常,但我需要一个适用于任何数据框的通用命令。提前感谢您的善意建议。

    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

}

3 个答案:

答案 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可以更好地处理查询计划中的预测量更少,这就是为什么选择多个单一选择会更好列:))