2015-01-27 19 views
28

Bir Data sınıfını, Dataframe veya Catalyst adlı Spark uygulamasına girdikten sonra, kodumdaki bir vaka sınıfına dönüştürmek istiyorum. Bu Scala DataFrame'in satırı, vaka sınıfına en verimli şekilde nasıl dönüştürülür?

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

eşleştirerek yapılabilir Ama satır sütun çok sayıda olduğunda çirkin olur, Çiftler, bazı Booleans bir düzine ve hatta zaman zaman null adlı söylüyorlar.

Sadece MyCaseClass'a -sorry-cast-cast edebilmek istiyorum. Mümkün mü, yoksa en ekonomik sözdizim var mı?

+1

Muhtemelen şekilsiz için databricks tarafından sağlanan bağlantıyı (https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless- altına

bakınız 2.0.0 # genel-temsil-mühürlü-aileler-vaka-sınıfları) boilerplate azaltmaya yardımcı olabilir, ama muhtemelen çok fazla null's sevmez. Belki makrolar (birçok vaka sınıfınız varsa)? –

+0

Asla makroları denemediniz. Burada bir problem, dillere yönelik standartlara inananlarım. Her zaman kendi yöntemlerimi yapabileceğimi ya da başka birini kullanabileceğimi hayal edebiliyorum ... ama nasıl bir dışsallık olmadan yapıldığını anlamaya çalışıyorum. – arivero

+0

merak ediyorum ... belki de "myCaseClass" ı Satır'dan almalı mıyım? – arivero

cevap

17

DataFrame basitçe bir Dataset [Row] türü takma adıdır. Bu işlemler, güçlü bir şekilde yazılan Scala/Java Veri Kümeleriyle birlikte gelen “yazılan dönüşümler” in tersine “türlenmemiş dönüşümler” olarak da adlandırılır.

Veri Kümesi [Kişiye] olarak DataSetbırakma [Satır] dönüşüm Bu noktada kıvılcım

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

çok basittir, Kıvılcım DataFrame = DataSetbırakma [Satır], jenerik bir koleksiyon içine veri dönüştürür Satır nesnesi, tam türü bilmediğinden. sınıf Kişi emrettiği gibi

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

Şimdi, Kıvılcım, Dataset[Row] -> Dataset[Person] türe özel Scala/Java JVM nesnesini dönüştürür.Daha fazla ayrıntı

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

+1

Orada sadece bir cevabınız var - ama her durumda * iyi bir *! Bu cevaba girene kadar özel bir Spark kodlayıcı oluşturma hakkında * herhangi bir bilgi bulamadım. btw "scala" yolu "Encoders.bean [Kişi]" – javadba

13

Sana bir vaka sınıfına bir Satır döküm olamaz biliyorum ama bazen özellikle vaka sınıfı ise, bu kolay bulmaktadır

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

gibi, doğrudan satır alanlarını erişmek için seçtik Bildiğim kadarıyla kurucu sadece satırın bazı alanlarına ihtiyaç duyar.

+1

Ve java null'ları eşleştirme sorununu ortadan kaldırırsın :-) – arivero

+0

Daha küçük sütun kümesi için bu gösterimi beğenirim, ancak sütun kümesi daha fazla belirsizliği eklerse, o zaman @Gianmarios önerisinin daha fazla olabileceğini düşünüyorum genişletilebilir. Bir kaç şeyi kendim doğrulamalıyım. Bu konuda size geri döneceğim. – Pramit

+0

Eğer sınıftaki bazı alanlar jenerik ise, bu işe yarar mı? – rileyss

6

Elbette bir Satır nesnesini bir kasa sınıfıyla eşleştirebilirsiniz. SchemaType ürününüzün birçok alanı olduğunu varsayalım ve bunlardan birkaçını davanızın sınıfına uydurmak istiyorsunuz.

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) 

}

Bu yaklaşım boş değerler söz konusu olduğunda başarısız olur ve aynı zamanda açıkça her bir alanın türünü tanımlarım gerektirir: Eğer boş alanları yok ise sadece yapabilirsiniz. null alanlar vaka sınıf için eşleşen Deseninizdeki kullanılan olanlar olmasalar bile sen

dataframe.na.drop() 
kayıtları düşecek

yaparak boş değerler içeren tüm satırları atmak yapmalı ya boş değerler ele varsa . Yoksa idare isterseniz bir Listesine Sıra nesnesi açmak ve daha sonra seçenek deseni kullanabilirsiniz:

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) 

}

Kontrol Bu github proje Sparkz() yakında bir sürü tanıtacak Spark ve DataFrame API'lerini basitleştirmek ve daha işlevsel programlama odaklı hale getirmek için kütüphaneler.

+0

Sparkz() 'ın yazarı kimden bahsediyor? –

+0

Muhtemelen şu anda https://github.com/gm-spacagna/sparkz adresindedir, ancak şu anda boştur – jbrown

2
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)] 
+0

Spark 2.1.0 için Bu işi almak için spark.implicits._ ithal etmek zorunda kaldı - Scala için güzel, zarif bir çözüm –

İlgili konular