[EDITAR: marzo de 2016: gracias por los votos! Aunque en realidad, esto no es la mejor respuesta, creo que las soluciones basadas en withColumn
, withColumnRenamed
y cast
presentada por msemelman, Martin Senne y otros son más simples y más limpio].
Creo que su enfoque está bien, recuerde que un Spark DataFrame
es un RDD (inmutable) de filas, por lo que nunca estamos reemplazando realmente una columna, solo creando nuevas DataFrame
cada vez con un nuevo esquema.
Suponiendo que tiene un df original con el siguiente esquema:
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)
Y algunos UDF definidos en una o varias columnas:
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
)
Cambiar los tipos de columna o incluso crear un nuevo DataFrame a partir de otro se puede escribir de esta manera:
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")
cuyos rendimientos:
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)
Esto está bastante cerca de su propia solución. Simplemente, mantener los cambios de tipo y otras transformaciones como elementos separados udf val
hace que el código sea más legible y reutilizable.