在Scala中转换所有数据框列的有效方法[重复]

Posted

技术标签:

【中文标题】在Scala中转换所有数据框列的有效方法[重复]【英文标题】:Efficient way to convert all data frame columns in Scala [duplicate] 【发布时间】:2017-07-08 18:40:09 【问题描述】:

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

    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


【问题讨论】:

【参考方案1】:

另一个建议,使用更“实用的方式”,是foldLeft。在这种情况下,您的“dataType”变量可以是一个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的信息。

【讨论】:

【参考方案2】:

它只更新最后一列,因为您总是将列附加到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))) 

【讨论】:

【参考方案3】:

两个答案都在做很多 withColumn - 可以,但是每个 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 可以更好地处理查询计划中的更少投影,这就是为什么最好使用多列进行单选:)

【讨论】:

@RaphaelRoth 我已经使用了你的部分代码,我将你的答案投票为“谢谢”:) 希望你没问题 不错的选择!如果我们只想处理列的一个子集而不删除其他列或更改顺序,似乎代码会变得更加复杂,在这种情况下,我认为可能值得坚持withColumn,我错了吗?跨度> @DanieldePaula 是的,withColumn 非常适合处理列子集或仅手动添加或更改几列:) 祝贺 Spark 前 20 名! :)

以上是关于在Scala中转换所有数据框列的有效方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章

迭代获取数据框列的最大值,加一并重复 r 中的所有行

如何使用 Spark 数据框列上的函数或方法使用 Scala 进行转换

如何在循环中读取数据框列值并检查每列的数据类型

根据另一列的值过滤数据框列[重复]

在 Spark 中将数据框列转换为向量

如何通过标识整数转换数据框列的每个值