2017-06-29 20 views
5

Tôi có một DataFrame có nhiều cột trong đó một số trong số đó là cấu trúc. Một cái gì đó như thế nàySpark SQL lồng vớiColumn

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) 

Tôi muốn áp dụng một UserDefinedFunction trên cột baz để thay thế baz với một chức năng của baz, nhưng tôi không thể tìm ra cách để làm điều đó. Dưới đây là một ví dụ về đầu ra mong muốn (lưu ý rằng baz bây giờ là một int là)

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) 

Dường như DataFrame.withColumn chỉ hoạt động trên các cột cấp cao nhất nhưng không phải trên cột lồng nhau. Tôi đang sử dụng Scala cho vấn đề này.

Ai đó có thể giúp tôi với điều này không?

Cảm ơn

Trả lời

4

thật dễ dàng, chỉ cần sử dụng dấu chấm để chọn cấu trúc lồng nhau, ví dụ: $"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| 
+----------+---------+ 

Nếu bạn muốn thêm kết quả của UDF của bạn vào struct hiện foo, tức là để có được:

root 
|-- foo: struct (nullable = false) 
| |-- bar: string (nullable = true) 
| |-- baz: string (nullable = true) 
| |-- udfResult: string (nullable = true) 

có hai tùy chọn:

với withColumn:

df 
.withColumn("udfResult",myUDF($"foo.baz")) 
.withColumn("foo",struct($"foo.*",$"udfResult")) 
.drop($"udfResult") 

với select:

df 
.select(struct($"foo.*",myUDF($"foo.baz").as("udfResult")).as("foo")) 

EDIT: Thay thế các thuộc tính tồn tại trong struct với kết quả từ UDF: không may, điều này không công việc:

df 
.withColumn("foo.baz",myUDF($"foo.baz")) 

nhưng có thể được thực hiện như thế này:

// 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")):_*) 
) 
+0

Tôi nghĩ rằng điều này đang đi đúng hướng, nhưng lý tưởng tôi muốn làm điều này mà không liệt kê tất cả các tên cột trong vòng uct. – Jon

+0

@Jon xem chỉnh sửa của tôi –

+0

Điều này dường như hoạt động, nhưng tôi không thể thay thế tên của cột phụ một cách dễ dàng. – Jon

Các vấn đề liên quan