[EDIT: март 2016: спасибо за голоса! Хотя на самом деле это не лучший ответ, я думаю, что решения, основанные на withColumn
, withColumnRenamed
и cast
, выдвинутые msemelman, Martin Senne и другими, проще и чище].
Я думаю, что ваш подход хорошо, напомню, что искра DataFrame
является (неизменным) РДДОМ рядов, так что мы никогда действительно заменяющего колонна, просто создавая новые DataFrame
каждый раз с новой схемой.
Если у вас есть оригинальный ФР со следующей схемой:
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
, сделайте код более читаемым и повторно используемым.
Это небезопасно и эффективно. __Не безопасно, потому что одна ошибка «NULL» или неправильная запись приведет к сбою всей работы. __Не эффективно, потому что UDF не прозрачны для Catalyst. Использование UDF для сложных операций просто отлично, но нет оснований использовать их для базового типа. Вот почему у нас есть метод «cast» (см. [Ответ Мартина Сенне] (http://stackoverflow.com/a/32634826/1560062)). Сделать вещи прозрачными для Catalyst требует больше работы, но базовая безопасность - это просто вопрос использования 'Try' и' Option'. – zero323
Я не видел ничего, связанного с преобразованием строки на сегодняшний день, например, «05-APR-2015» – dbspace
Есть ли способ уменьшить раздел 'withColumn()' к универсальному, который выполняет итерацию через все столбцы? – Boern