[EDIT: marzo 2016: grazie per i voti! Anche se in realtà, questa non è la migliore risposta, credo che le soluzioni basate su withColumn
, withColumnRenamed
e cast
avanzata dal msemelman, Martin Senne e gli altri sono più semplici e più pulito].
Penso che il tuo approccio sia ok, ricorda che uno Spark DataFrame
è un RDD (immutabile) di righe, quindi non stiamo mai sostituendo una colonna, ma ne creiamo di nuove DataFrame
ogni volta con un nuovo schema.
Supponendo di avere un df originale con il seguente schema:
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)
E alcuni UDF sono definiti su una o più colonne:
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
)
La modifica dei tipi di colonna o persino la creazione di un nuovo DataFrame da un altro può essere scritta in questo modo:
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")
che produce:
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)
Questo è abbastanza vicino alla tua soluzione. Semplicemente, mantenendo le modifiche al tipo e altre trasformazioni come udf val
s separate , il codice diventa più leggibile e riutilizzabile.