複数の列を持つDataFrameがあり、そのうちのいくつかは構造体です。このようなもの
root
|-- foo: struct (nullable = true)
| |-- bar: string (nullable = true)
| |-- baz: string (nullable = true)
|-- abc: array (nullable = true)
| |-- element: struct (containsNull = true)
| | |-- def: struct (nullable = true)
| | | |-- a: string (nullable = true)
| | | |-- b: integer (nullable = true)
| | | |-- c: string (nullable = true)
列UserDefinedFunction
にbaz
を適用してbaz
をbaz
の関数に置き換えたいのですが、その方法がわかりません。これは望ましい出力の例です(baz
はint
になっていることに注意してください)
root
|-- foo: struct (nullable = true)
| |-- bar: string (nullable = true)
| |-- baz: int (nullable = true)
|-- abc: array (nullable = true)
| |-- element: struct (containsNull = true)
| | |-- def: struct (nullable = true)
| | | |-- a: string (nullable = true)
| | | |-- b: integer (nullable = true)
| | | |-- c: string (nullable = true)
DataFrame.withColumn
はトップレベルの列でのみ機能し、ネストされた列では機能しないようです。この問題にはScalaを使用しています。
誰かがこれを手伝ってくれる?
ありがとう
これは簡単です。ドットを使用してネストされた構造を選択するだけです。 $"foo.baz"
:
case class Foo(bar:String,baz:String)
case class Record(foo:Foo)
val df = Seq(
Record(Foo("Hi","There"))
).toDF()
df.printSchema
root
|-- foo: struct (nullable = true)
| |-- bar: string (nullable = true)
| |-- baz: string (nullable = true)
val myUDF = udf((s:String) => {
// do something with s
s.toUpperCase
})
df
.withColumn("udfResult",myUDF($"foo.baz"))
.show
+----------+---------+
| foo|udfResult|
+----------+---------+
|[Hi,There]| THERE|
+----------+---------+
UDFの結果を既存のstruct foo
に追加する場合、つまり次のようにします。
root
|-- foo: struct (nullable = false)
| |-- bar: string (nullable = true)
| |-- baz: string (nullable = true)
| |-- udfResult: string (nullable = true)
2つのオプションがあります。
withColumn
:
df
.withColumn("udfResult",myUDF($"foo.baz"))
.withColumn("foo",struct($"foo.*",$"udfResult"))
.drop($"udfResult")
select
:
df
.select(struct($"foo.*",myUDF($"foo.baz").as("udfResult")).as("foo"))
編集:構造体の既存の属性をUDFからの結果で置き換える:残念ながら、これはnotで機能します。
df
.withColumn("foo.baz",myUDF($"foo.baz"))
しかし、このように行うことができます:
// get all columns except foo.baz
val structCols = df.select($"foo.*")
.columns
.filter(_!="baz")
.map(name => col("foo."+name))
df.withColumn(
"foo",
struct((structCols:+myUDF($"foo.baz").as("baz")):_*)
)
Raphael Rothはすでに上記の回答で示されているため、struct
関数を使用してこれを行うことができます。 Make Structs Easy *ライブラリを使用すると、これを行う簡単な方法があります。ライブラリは、ColumnクラスにwithField
メソッドを追加します。これにより、DataFrameクラスのwithColumn
メソッドが追加/ DataFrame内の列を置き換えます。特定のユースケースでは、次のようなことができます。
import org.Apache.spark.sql.functions._
import com.github.fqaiser94.mse.methods._
// generate some fake data
case class Foo(bar: String, baz: String)
case class Record(foo: Foo, arrayOfFoo: Seq[Foo])
val df = Seq(
Record(Foo("Hello", "World"), Seq(Foo("Blue", "Red"), Foo("Green", "Yellow")))
).toDF
df.printSchema
// root
// |-- foo: struct (nullable = true)
// | |-- bar: string (nullable = true)
// | |-- baz: string (nullable = true)
// |-- arrayOfFoo: array (nullable = true)
// | |-- element: struct (containsNull = true)
// | | |-- bar: string (nullable = true)
// | | |-- baz: string (nullable = true)
df.show(false)
// +--------------+------------------------------+
// |foo |arrayOfFoo |
// +--------------+------------------------------+
// |[Hello, World]|[[Blue, Red], [Green, Yellow]]|
// +--------------+------------------------------+
// example user defined function that capitalizes a given string
val myUdf = udf((s: String) => s.toUpperCase)
// capitalize value of foo.baz
df.withColumn("foo", $"foo".withField("baz", myUdf($"foo.baz"))).show(false)
// +--------------+------------------------------+
// |foo |arrayOfFoo |
// +--------------+------------------------------+
// |[Hello, WORLD]|[[Blue, Red], [Green, Yellow]]|
// +--------------+------------------------------+
配列内にネストされた構造体内にネストされた列を置き換えることについて、フォローアップの質問があることに気づきました。 Make Structs Easy ライブラリによって提供される関数と spark-hofs ライブラリによって提供される関数を次のように組み合わせて、これを行うこともできます。
import za.co.absa.spark.hofs._
// capitalize the value of foo.baz in each element of arrayOfFoo
df.withColumn("arrayOfFoo", transform($"arrayOfFoo", foo => foo.withField("baz", myUdf(foo.getField("baz"))))).show(false)
// +--------------+------------------------------+
// |foo |arrayOfFoo |
// +--------------+------------------------------+
// |[Hello, World]|[[Blue, RED], [Green, YELLOW]]|
// +--------------+------------------------------+
*完全な開示:この回答で参照されている Make Structs Easy ライブラリの作成者です。