Подтвердить что ты не робот

Как наиболее эффективно преобразовать строку Scala DataFrame в класс case?

Как только я попал в Spark в некоторый класс Row, либо Dataframe, либо Catalyst, я хочу преобразовать его в класс case в моем коде. Это можно сделать, сопоставляя

someRow match {case Row(a:Long,b:String,c:Double) => myCaseClass(a,b,c)}

Но он становится уродливым, когда строка имеет огромное количество столбцов, например, дюжину парных разрядов, некоторые булевы и даже случайные нули.

Я хотел бы просто иметь возможность -позволить строку в myCaseClass. Возможно ли, или у меня есть самый экономичный синтаксис?

4b9b3361

Ответ 1

DataFrame - это просто псевдоним типа Dataset [Row]. Эти операции также называются "нетипизированными преобразованиями" в отличие от "типизированных преобразований", которые поставляются с строго типизированными наборами данных Scala/Java.

Преобразование из набора данных [Ряд] в набор данных [Лицо] очень просто в искры

val DFtoProcess = SQLContext.sql("SELECT * FROM peoples WHERE name='test'")

В этот момент Spark преобразует ваши данные в DataFrame = Dataset [Row] - коллекцию общего объекта Row, поскольку он не знает точного типа.

// Create an Encoders for Java class (In my eg. Person is a JAVA class)
// For scala case class you can pass Person without .class reference
val personEncoder = Encoders.bean(Person.class) 

val DStoProcess = DFtoProcess.as[Person](personEncoder)

Теперь Spark преобразует объект Dataset[Row] -> Dataset[Person], специфичный для типа Scala/Java JVM, как это определено классом Person.

Подробнее см. ссылку ниже, предоставленную databricks.

https://databricks.com/blog/2016/07/14/a-tale-of-three-apache-spark-apis-rdds-dataframes-and-datasets.html

Ответ 2

Насколько я знаю, вы не можете использовать класс Row для класса case, но иногда я предпочитаю напрямую обращаться к строкам, например

map(row => myCaseClass(row.getLong(0), row.getString(1), row.getDouble(2))

Я считаю, что это проще, особенно если конструктор класса case нуждается только в некоторых из полей из строки.

Ответ 3

Конечно, вы можете сопоставить объект Row с классом case. Предположим, что ваш SchemaType имеет много полей, и вы хотите сопоставить некоторые из них в своем классе case. Если у вас нет нулевых полей, вы можете просто:

case class MyClass(a: Long, b: String, c: Int, d: String, e: String)

dataframe.map {
  case Row(a: java.math.BigDecimal, 
    b: String, 
    c: Int, 
    _: String,
    _: java.sql.Date, 
    e: java.sql.Date,
    _: java.sql.Timestamp, 
    _: java.sql.Timestamp, 
    _: java.math.BigDecimal, 
    _: String) => MyClass(a = a.longValue(), b = b, c = c, d = d.toString, e = e.toString)

}

Этот подход потерпит неудачу в случае нулевых значений, а также потребует явного определения типа каждого отдельного поля. Если вам нужно обрабатывать нулевые значения, вам нужно либо отбросить все строки, содержащие нулевые значения, выполнив

dataframe.na.drop()

Это приведет к потере записей, даже если нулевые поля не используются в вашем шаблоне для вашего класса case. Или, если вы хотите обработать его, вы можете повернуть объект Row в список, а затем использовать шаблон опций:

case class MyClass(a: Long, b: String, c: Option[Int], d: String, e: String)

dataframe.map(_.toSeq.toList match {
  case List(a: java.math.BigDecimal, 
    b: String, 
    c: Int, 
    _: String,
    _: java.sql.Date, 
    e: java.sql.Date,
    _: java.sql.Timestamp, 
    _: java.sql.Timestamp, 
    _: java.math.BigDecimal, 
    _: String) => MyClass(a = a.longValue(), b = b, c = Option(c), d = d.toString, e = e.toString)

}

Проверьте этот проект gythub Sparkz(), который вскоре представит множество библиотек для упрощения API Spark и DataFrame и сделает их более функциональными, ориентированными на программирование.

Ответ 4

scala> val df = Seq((1, "james"), (2, "tony")).toDF("id", "name")
df: org.apache.spark.sql.DataFrame = [id: int, name: string]

scala> case class Student(id: Int, name: String)
defined class Student

scala> df.as[Student].collectAsList
res6: java.util.List[Student] = [Student(1,james), Student(2,tony)]