フラット化しようとしているデータフレームがあります。プロセスの一部として、爆発させたいので、配列の列がある場合は、配列の各値を使用して個別の行を作成します。例えば、
id | name | likes
_______________________________
1 | Luke | [baseball, soccer]
になるはずです
id | name | likes
_______________________________
1 | Luke | baseball
1 | Luke | soccer
これは私のコードです
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;
}
問題は、私のデータでは、配列列の一部にヌルが含まれていることです。その場合、行全体が削除されます。したがって、このデータフレーム:
id | name | likes
_______________________________
1 | Luke | [baseball, soccer]
2 | Lucy | null
になる
id | name | likes
_______________________________
1 | Luke | baseball
1 | Luke | soccer
の代わりに
id | name | likes
_______________________________
1 | Luke | baseball
1 | Luke | soccer
2 | Lucy | null
Null行が失われないように配列を分解するにはどうすればよいですか?
私はSpark 1.5.2およびJava 8
Spark 2.2 +
_explode_outer
_関数を使用できます:
_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|
// +---+----+--------+
_
スパーク<= 2.1
In Scala but Java同等のものはほぼ同一である必要があります(個々の関数をインポートするには_import static
_を使用します)。
_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")))))
_
ここでの考え方は、基本的にNULL
を目的のタイプのarray(NULL)
に置き換えることです。複合型(別名structs
)の場合、完全なスキーマを提供する必要があります。
_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)))))
_
または
_dfStruct.withColumn("y", explode(
when(col("y").isNotNull, col("y"))
.otherwise(array(lit(null).cast("struct<_1:int,_2:string>")))))
_
注:
配列Column
がcontainsNull
をfalse
に設定して作成されている場合、これを最初に変更する必要があります(Spark 2.1)でテスト済み):
_df.withColumn("array_column", $"array_column".cast(ArrayType(SomeType, true)))
_
受け入れられた答えをフォローアップすると、配列要素が複雑な型である場合、手で定義するのが難しい場合があります(たとえば、大きな構造体で)。
自動的に行うために、次のヘルパーメソッドを作成しました。
def explodeOuter(df: Dataset[Row], columnsToExplode: List[String]) = {
val arrayFields = df.schema.fields
.map(field => field.name -> field.dataType)
.collect { case (name: String, type: ArrayType) => (name, type.asInstanceOf[ArrayType])}
.toMap
columnsToExplode.foldLeft(df) { (dataFrame, arrayCol) =>
dataFrame.withColumn(arrayCol, explode(when(size(col(arrayCol)) =!= 0, col(arrayCol))
.otherwise(array(lit(null).cast(arrayFields(arrayCol).elementType)))))
}
explode_outer()
関数を使用できます。