在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中转换所有数据框列的有效方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章