[EDIT: mars 2016: merci pour les votes! Bien que vraiment, ce n'est pas la meilleure réponse, je pense que les solutions basées sur withColumn, withColumnRenamedetcast mis en avant par msemelman, Martin Senne et d' autres sont plus simples et plus propre].
Je pense que votre approche est ok, rappeler qu'une Spark DataFrameest un (immuable) RDD de lignes, donc nous ne sommes jamais vraiment remplacer une colonne, juste créer une nouvelle à DataFramechaque fois avec un nouveau schéma.
En supposant que vous ayez un df original avec le schéma suivant:
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)
Et certains UDF définis sur une ou plusieurs colonnes:
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
)
Changer les types de colonnes ou même construire un nouveau DataFrame à partir d'un autre peut être écrit comme ceci:
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")
ce qui donne:
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)
C'est assez proche de votre propre solution. Simplement, garder les changements de type et autres transformations comme des udf vals séparés rend le code plus lisible et réutilisable.