web-dev-qa-db-ja.com

新しいStruct列をDataFrameに追加する方法

現在、MongoDBからデータベースを抽出して、Spark=を使用してElasticSearchにgeo_pointsを取り込みます。

Mongoデータベースには緯度と経度の値がありますが、ElasticSearchではそれらをgeo_pointタイプにキャストする必要があります。

Sparkにlatおよびlon列をarrayまたはstruct

どんな助けも大歓迎です!

19
Kim Ngo

私はあなたがこのようなフラットスキーマのようなものから始めると仮定します:

root
 |-- lat: double (nullable = false)
 |-- long: double (nullable = false)
 |-- key: string (nullable = false)

まず、サンプルデータを作成します。

import org.Apache.spark.sql.Row
import org.Apache.spark.sql.functions.{col, udf}
import org.Apache.spark.sql.types._

val rdd = sc.parallelize(
    Row(52.23, 21.01, "Warsaw") :: Row(42.30, 9.15, "Corte") :: Nil)

val schema = StructType(
    StructField("lat", DoubleType, false) ::
    StructField("long", DoubleType, false) ::
    StructField("key", StringType, false) ::Nil)

val df = sqlContext.createDataFrame(rdd, schema)

簡単な方法は、udfとcaseクラスを使用することです:

case class Location(lat: Double, long: Double)
val makeLocation = udf((lat: Double, long: Double) => Location(lat, long))

val dfRes = df.
   withColumn("location", makeLocation(col("lat"), col("long"))).
   drop("lat").
   drop("long")

dfRes.printSchema

そして私たちは得る

root
 |-- key: string (nullable = false)
 |-- location: struct (nullable = true)
 |    |-- lat: double (nullable = false)
 |    |-- long: double (nullable = false)

難しい方法は、データを変換し、後でスキーマを適用することです:

val rddRes = df.
    map{case Row(lat, long, key) => Row(key, Row(lat, long))}

val schemaRes = StructType(
    StructField("key", StringType, false) ::
    StructField("location", StructType(
        StructField("lat", DoubleType, false) ::
        StructField("long", DoubleType, false) :: Nil
    ), true) :: Nil 
)

sqlContext.createDataFrame(rddRes, schemaRes).show

期待される出力が得られます

+------+-------------+
|   key|     location|
+------+-------------+
|Warsaw|[52.23,21.01]|
| Corte|  [42.3,9.15]|
+------+-------------+

ネストされたスキーマをゼロから作成するのは面倒なので、できれば最初のアプローチをお勧めします。より高度な構造が必要な場合は、簡単に拡張できます。

case class Pin(location: Location)
val makePin = udf((lat: Double, long: Double) => Pin(Location(lat, long))

df.
    withColumn("pin", makePin(col("lat"), col("long"))).
    drop("lat").
    drop("long").
    printSchema

期待される出力が得られます。

root
 |-- key: string (nullable = false)
 |-- pin: struct (nullable = true)
 |    |-- location: struct (nullable = true)
 |    |    |-- lat: double (nullable = false)
 |    |    |-- long: double (nullable = false)

残念ながらnullableフィールドを制御することはできないため、プロジェクトで重要な場合はスキーマを指定する必要があります。

最後に、1.4で導入されたstruct関数を使用できます。

import org.Apache.spark.sql.functions.struct

df.select($"key", struct($"lat", $"long").alias("location"))
50
zero323

これを試して:

import org.Apache.spark.sql.functions._

df.registerTempTable("dt")

dfres = sql("select struct(lat,lon) as colName from dt")
5
user8817325