[EDIT: März 2016: Danke für die Stimmen! Obwohl wirklich, das ist nicht die beste Antwort, ich glaube , die Lösungen auf Basis von withColumn
, withColumnRenamed
und cast
legte durch msemelman, Martin Senne und andere sind einfacher und sauberer].
Ich denke, Ihr Ansatz ist in Ordnung. Denken Sie daran, dass ein Spark DataFrame
eine (unveränderliche) RDD von Zeilen ist. Wir ersetzen also nie wirklich eine Spalte, sondern erstellen DataFrame
jedes Mal eine neue durch ein neues Schema.
Angenommen, Sie haben eine Original-DF mit dem folgenden 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)
Und einige UDFs, die in einer oder mehreren Spalten definiert sind:
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
)
Das Ändern von Spaltentypen oder sogar das Erstellen eines neuen DataFrame aus einem anderen kann folgendermaßen geschrieben werden:
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")
was ergibt:
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)
Dies kommt Ihrer eigenen Lösung ziemlich nahe. Wenn Sie die Typänderungen und andere Transformationen einfach getrennt halten udf val
, wird der Code einfach lesbarer und wiederverwendbarer.