我怎样可以改变Spark SQL的DataFrame中的列类型?

14 浏览
0 Comments

我怎样可以改变Spark SQL的DataFrame中的列类型?

假设我在做这样的事情:

val df = sqlContext.load("com.databricks.spark.csv", Map("path" -> "cars.csv", "header" -> "true"))
df.printSchema()
root
 |-- year: string (nullable = true)
 |-- make: string (nullable = true)
 |-- model: string (nullable = true)
 |-- comment: string (nullable = true)
 |-- blank: string (nullable = true)
df.show()
year make  model comment              blank
2012 Tesla S     No comment
1997 Ford  E350  Go get one now th...

但我真正想要的是将year作为Int(并可能转换一些其他列)。

我所能想到的最好的办法是:

df.withColumn("year2", 'year.cast("Int")).select('year2 as 'year, 'make, 'model, 'comment, 'blank)
org.apache.spark.sql.DataFrame = [year: int, make: string, model: string, comment: string, blank: string]

有点复杂。

我来自R,我习惯于能够编写例如:

df2 <- df %>%
   mutate(year = year %>% as.integer,
          make = make %>% toupper)

我可能会错过一些东西,因为在Spark/Scala中肯定有更好的方法来做这件事...

admin 更改状态以发布 2023年5月22日
0
0 Comments

[EDIT:2016年3月:感谢投票!虽然这不是最好的答案,但我认为由msemelman,Martin Senne和其他人提出的基于withColumnwithColumnRenamedcast的解决方案更简单,更清洁]。

我认为您的方法是可以的,记住Spark DataFrame是行的(不可变)RDD,所以我们从未真正替换列,只是每次用新的模式创建新的DataFrame

假设您具有原始df,其具有以下模式:

scala> df.printSchema
root
 |-- Year: string (nullable = true)
 |-- Month: string (nullable = true)
 |-- DayofMonth: string (nullable = true)
 |-- DayOfWeek: string (nullable = true)
 |-- DepDelay: string (nullable = true)
 |-- Distance: string (nullable = true)
 |-- CRSDepTime: string (nullable = true)

并且有几个UDF定义在一个或多个列上:

import org.apache.spark.sql.functions._
val toInt    = udf[Int, String]( _.toInt)
val toDouble = udf[Double, String]( _.toDouble)
val toHour   = udf((t: String) => "%04d".format(t.toInt).take(2).toInt ) 
val days_since_nearest_holidays = udf( 
  (year:String, month:String, dayOfMonth:String) => year.toInt + 27 + month.toInt-12
 )

更改列类型甚至从另一个构建新的DataFrame可以写成如下:

val featureDf = df
.withColumn("departureDelay", toDouble(df("DepDelay")))
.withColumn("departureHour",  toHour(df("CRSDepTime")))
.withColumn("dayOfWeek",      toInt(df("DayOfWeek")))              
.withColumn("dayOfMonth",     toInt(df("DayofMonth")))              
.withColumn("month",          toInt(df("Month")))              
.withColumn("distance",       toDouble(df("Distance")))              
.withColumn("nearestHoliday", days_since_nearest_holidays(
              df("Year"), df("Month"), df("DayofMonth"))
            )              
.select("departureDelay", "departureHour", "dayOfWeek", "dayOfMonth", 
        "month", "distance", "nearestHoliday")            

这会产生:

scala> df.printSchema
root
 |-- departureDelay: double (nullable = true)
 |-- departureHour: integer (nullable = true)
 |-- dayOfWeek: integer (nullable = true)
 |-- dayOfMonth: integer (nullable = true)
 |-- month: integer (nullable = true)
 |-- distance: double (nullable = true)
 |-- nearestHoliday: integer (nullable = true)

这非常接近您自己的解决方案。简单地将类型更改和其他转换作为单独的udf val保留,可以使代码更具可读性和可重用性。

0
0 Comments

编辑:最新版本

自Spark 2.x以来,使用Scala时应使用数据集API。请在此处查看文档:

https://spark.apache.org/docs/latest/api/scala/org/apache/spark/sql/Dataset.html#withColumn(colName:String,col:org.apache.spark.sql.Column):org.apache.spark.sql.DataFrame

如果使用Python,虽然更容易,但我在此处留下链接,因为这是一个得到高票问题:

https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.sql.DataFrame.withColumn.html

>>> df.withColumn('age2', df.age + 2).collect()
[Row(age=2, name='Alice', age2=4), Row(age=5, name='Bob', age2=7)]

[1] https://spark.apache.org/docs/latest/sql-programming-guide.html:

在Scala API中,DataFrame只是Dataset[Row]的类型别名。
而在Java API中,用户需要使用Dataset来表示DataFrame。

编辑:最新最新版本

自Spark 2.x以来,您可以使用.withColumn。请在此处查看文档:

https://spark.apache.org/docs/2.2.0/api/scala/index.html#org.apache.spark.sql.Dataset@withColumn(colName:String,col:org.apache.spark.sql.Column):org.apache.spark.sql.DataFrame

最早的回答

从Spark 1.4版本开始,您可以使用DataType在列上应用cast方法:

import org.apache.spark.sql.types.IntegerType
val df2 = df.withColumn("yearTmp", df.year.cast(IntegerType))
    .drop("year")
    .withColumnRenamed("yearTmp", "year")

如果您正在使用SQL表达式,您也可以这样做:

val df2 = df.selectExpr("cast(year as int) year", 
                        "make", 
                        "model", 
                        "comment", 
                        "blank")

有关更多信息,请参阅文档:
http://spark.apache.org/docs/1.6.0/api/scala/#org.apache.spark.sql.DataFrame

0