我怎样可以改变Spark SQL的DataFrame中的列类型?
我怎样可以改变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中肯定有更好的方法来做这件事...
[EDIT:2016年3月:感谢投票!虽然这不是最好的答案,但我认为由msemelman,Martin Senne和其他人提出的基于withColumn
,withColumnRenamed
和cast
的解决方案更简单,更清洁]。
我认为您的方法是可以的,记住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
保留,可以使代码更具可读性和可重用性。
编辑:最新版本
自Spark 2.x以来,使用Scala时应使用数据集API。请在此处查看文档:
如果使用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
。请在此处查看文档:
最早的回答
从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