2016-09-28 22 views
12

Düzleştirmeye çalıştığım bir Dataframe'im var. İşlemin bir parçası olarak, onu patlatmak istiyorum, bu yüzden bir dizi sütunum varsa, dizinin her bir değeri ayrı bir satır oluşturmak için kullanılacaktır. Örneğin,Spark sql boş değerleri kaybetmeden nasıl patlayabilir

id | name | likes 
_______________________________ 
1 | Luke | [baseball, soccer] 

haline gelmelidir

id | name | likes 
_______________________________ 
1 | Luke | baseball 
1 | Luke | soccer 

Bu benim kod

private DataFrame explodeDataFrame(DataFrame df) { 
    DataFrame resultDf = df; 
    for (StructField field : df.schema().fields()) { 
     if (field.dataType() instanceof ArrayType) { 
      resultDf = resultDf.withColumn(field.name(), org.apache.spark.sql.functions.explode(resultDf.col(field.name()))); 
      resultDf.show(); 
     } 
    } 
    return resultDf; 
} 

sorun benim verilerde, dizi kolonların bazı boş değerlere sahip olmasıdır olduğunu. Bu durumda, tüm satır silinir. Yani bu dataframe:

id | name | likes 
_______________________________ 
1 | Luke | [baseball, soccer] 
2 | Lucy | null 

id | name | likes 
_______________________________ 
1 | Luke | baseball 
1 | Luke | soccer 

yerine

id | name | likes 
_______________________________ 
1 | Luke | baseball 
1 | Luke | soccer 
2 | Lucy | null 

ben boş satırları kaybetmek kalmamak nasıl diziler patlayabilir olacak?

Ben Spark 1.5.2 ve Java kullanıyorum 8

cevap

20

Sen explode_outer işlevini kullanabilirsiniz 2.2+

Kıvılcım:

import org.apache.spark.sql.functions.explode_outer 

df.withColumn("likes", explode_outer($"likes")).show 

// +---+----+--------+ 
// | id|name| likes| 
// +---+----+--------+ 
// | 1|Luke|baseball| 
// | 1|Luke| soccer| 
// | 2|Lucy| null| 
// +---+----+--------+ 

Spark < = 2,1

Scala'da ama Java eşdeğeri olmalı hemen hemen aynı (bireysel fonksiyonları içe aktarmak için import static kullanın).

import org.apache.spark.sql.functions.{array, col, explode, lit, when} 

val df = Seq(
    (1, "Luke", Some(Array("baseball", "soccer"))), 
    (2, "Lucy", None) 
).toDF("id", "name", "likes") 

df.withColumn("likes", explode(
    when(col("likes").isNotNull, col("likes")) 
    // If null explode an array<string> with a single null 
    .otherwise(array(lit(null).cast("string"))))) 

buradaki fikir istenen türde bir array(NULL) ile NULL yerine temelde.

val dfStruct = Seq((1L, Some(Array((1, "a")))), (2L, None)).toDF("x", "y") 

val st = StructType(Seq(
    StructField("_1", IntegerType, false), StructField("_2", StringType, true) 
)) 

dfStruct.withColumn("y", explode(
    when(col("y").isNotNull, col("y")) 
    .otherwise(array(lit(null).cast(st))))) 

veya

dfStruct.withColumn("y", explode(
    when(col("y").isNotNull, col("y")) 
    .otherwise(array(lit(null).cast("struct<_1:int,_2:string>"))))) 

Not: karmaşık tür (aka structs) için tam şema sağlamak zorunda

dizisi Columnfalse için containsNull seti ile oluşturuldu Eğer gerektiği önce bunu değiştirin (Spark 2.1 ile test edilmiştir):

df.withColumn("array_column", $"array_column".cast(ArrayType(SomeType, true))) 
+0

harika görünüyor, teşekkür ederim!Bir takip sorumlum var: sütun türüm bir StructType ise ne olur? Cast (new StructType()) kullanmayı denedim, ancak veri türü uyuşmazlığı aldım: THEN ve ELSE ifadeleri hepsi aynı türde veya ortak bir türden anlaşılabilir olmalı, 'Yöntemimi olabildiğince genel bir şekilde yapmaya çalışıyorum, yani tüm sütun türlerine uyar. – alexgbelov

+0

Ayrıca, sütun türünü almak için DataFrame.dtypes() kullanıyorum. Sütun türlerini almanın daha iyi bir yolu var mı? – alexgbelov

+1

a) Tüm alanlarla tam şema sağlamanız gerekir. b) “dtypes” veya “schema”. – zero323

0

Kabul edilen cevabı takiben, dizi elemanları karmaşık bir tür olduğunda, onu elle tanımlamak zor olabilir (örn. Büyük yapılar).

bunu otomatik olarak yapmak için aşağıdaki yardımcı yöntemi yazdı:

İlgili konular