Una vez que tengo en la Chispa de algunos de Fila de la clase, ya sea Dataframe o Catalizador, quiero convertirlo a un caso de la clase en mi código. Esto se puede hacer mediante la coincidencia de

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

Pero se vuelve feo cuando la fila tiene un gran número de columnas, digamos una docena de Dobles, algunas Booleanos e incluso el ocasional null.

Me gustaría ser capaz de -lo siento – cast Fila para myCaseClass. Es posible que, o tienen ya la tengo, el más económico de la sintaxis?

  • Probablemente sin forma (github.com/milessabin/shapeless/wiki/…) puede ayudar a reducir la repetitivo, pero probablemente no gusta mucho nulls. Tal vez macros (en caso de tener muchas clases)?
  • Nunca trató de macros. Un problema aquí es que yo soy un creyente en las normas para las lenguas. Me imagino que siempre puedo hacer mis propios métodos, o utilizar de otra persona… pero yo prefiero tratar de entender cómo se hace sin ningún externos.
  • pensando… tal vez podría yo subclase «myCaseClass» de la Fila?
  • Esta es una decepción. Tengo un gran complejo caso de la clase y ahora necesito asignar manualmente cada columna para que cuando quiero cargar y trabajar con ella. Me pone triste 🙁
InformationsquelleAutor arivero | 2015-01-27

4 Comentarios

  1. 36

    DataFrame es simplemente un alias de tipo de conjunto de datos[Fila] . Estas operaciones se conocen también como «sin tipo de transformaciones» en contraste con «escribió transformaciones» que vienen con establecimiento inflexible de tipos de Scala/Java conjuntos de datos.

    La conversión de conjunto de datos[Fila] al conjunto de datos[Persona] es muy simple en la chispa de la

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

    En este punto, la Chispa, convierte sus datos en DataFrame = conjunto de datos[Fila], una colección de genéricos objeto de Fila, ya que no sabe el tipo exacto.

    //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)

    Ahora, la Chispa se convierte la Dataset[Row] -> Dataset[Person] tipo específico de la Scala /Java JVM objeto, dictada por la clase Persona.

    Por favor consulte el siguiente enlace proporcionado por databricks para más detalles

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

    • Usted sólo tiene una respuesta allí, pero es un buena en cualquier caso! No podía encontrar la alguna más información sobre cómo crear una personalizada Chispa codificador hasta que se topan con esta respuesta. por cierto el scala manera es Encoders.bean[Person]
    • cabe señalar que as no es seguro ya que no se compruebe que el reparto es válido. No entiendo cómo se agrega un feo función de su API (que debería llamarse unsafeAs – y dónde está la caja fuerte as que al mismo tiempo hace el filtrado o la devuelve DataSet[Option[T]]?)
    • Supongo que esto no funciona con el caso de las clases? Me sale un error Cannot infer type for class Person because it is not bean-compliant aquí.
    • Esta es claramente la solución más limpia cuando quiero transformar una Dataset[Row] en un Dataset[Person], pero ¿y si sólo quiero convertir un solo Row objeto? Me estoy cayendo de nuevo en sólo manualmente la construcción de la Person de cada campo en el Row. Hay una manera mejor?
    • la respuesta correcta para el caso de las clases es el siguiente – que necesita para import spark.implicits._ que sólo puede utilizar df.as[Person]
  2. 23

    Como yo sé que usted no puede emitir una Fila a un caso de la clase, pero a veces me eligió para acceder a los campos de fila directamente, como

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

    Me parece que esto sea más fácil, especialmente si el caso constructor de la clase sólo las necesidades de algunos de los campos de la fila.

    • Y evitar el problema de la coincidencia de java nulos 🙂
    • Me gusta esta representación también para los más pequeños conjunto de columnas, pero si el conjunto de columnas es más grande que añade a la ambigüedad, a continuación, creo que @Gianmarios sugerencia podría ser más extensible. Tengo que comprobar un par de cosas a mí mismo. Va a volver a usted en esto.
    • si algunos de los campos en caso de que clase son genéricos, tendría que trabajar?
    • Gracias! Esta fue una gran ayuda.
  3. 15
    scala> import spark.implicits._    
    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)]

    Aquí el spark en spark.implicits._ es su SparkSession. Si usted está dentro de la REPL la sesión ya está definido como spark de lo contrario tendrá que ajustar el nombre de acuerdo a corresponder a su SparkSession.

    • Para Chispa 2.1.0 tuve que importar chispa.implicits._ para conseguir este trabajo de niza, la solución elegante para la Scala
    • Consulte stackoverflow.com/questions/39968707/… para obtener más detalles de spark.implicits._
    • Esta debe ser la respuesta correcta.
    • Hago esta fuera de la REPL y en una prueba, y se dice que Error:(44, 30) not enough arguments for method as: (implicit evidence$2: org.apache.spark.sql.Encoder[Student])org.apache.spark.sql.Dataset[Student]. Unspecified value parameter evidence$2. val rows = df.as[Student].collectAsList()
  4. 7

    Por supuesto, usted puede comparar un objeto de Fila en un caso de la clase. Supongamos que tu SchemaType tiene muchos campos y quieres coincidir con algunos de ellos en su caso de la clase.
    Si usted no tiene campos null, usted puede simplemente hacer:

    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)
    }

    Este enfoque se producirá en el caso de valores nulos y también requieren que usted haga de definir explícitamente el tipo de cada campo.
    Si usted tiene que controlar los valores null que necesita para descartar todas las filas que contienen valores null haciendo

    dataframe.na.drop()

    Que tendrá la baja de los registros, incluso si los campos null no son las que se utilizan en su correspondencia de patrones para su caso de la clase.
    O si desea controlar lo que podría a su vez el objeto de Fila en una Lista y, a continuación, utilizar la opción de patrón:

    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)
    }

    Comprobar este github del proyecto Sparkz () que pronto va a introducir una gran cantidad de bibliotecas para la simplificación de la Chispa y DataFrame Api y hacerlos más funcionales de programación orientado.

    • Quién es el autor de Sparkz() que estás hablando?
    • Presumiblemente es en github.com/gm-spacagna/sparkz, pero está vacía en el momento
    • ¿la solución anterior trabajo para nadie.

Dejar respuesta

Please enter your comment!
Please enter your name here