web-dev-qa-db-ja.com

Spark JSONへの行

Spark v.1.6(scalaを使用))データフレームからJSONを作成したいと思います。df.toJSONを実行する簡単なソリューションがあることを知っています。

ただし、私の問題は少し異なります。たとえば、次の列を持つデータフレームを考えてみましょう。

|  A  |     B     |  C1  |  C2  |    C3   |
-------------------------------------------
|  1  | test      |  ab  |  22  |  TRUE   |
|  2  | mytest    |  gh  |  17  |  FALSE  |

最後にデータフレームを持ちたい

|  A  |     B     |                        C                   |
----------------------------------------------------------------
|  1  | test      | { "c1" : "ab", "c2" : 22, "c3" : TRUE }    |
|  2  | mytest    | { "c1" : "gh", "c2" : 17, "c3" : FALSE }   |

cはC1C2C3を含むJSONです。残念ながら、コンパイル時には、データフレームがどのように見えるかわかりません(列AおよびBは常に「固定」されています)。

これが必要な理由については、結果を送信するためにProtobufを使用しています。残念ながら、私のデータフレームには予想よりも多くの列がある場合があり、それでもProtobufを介して送信しますが、定義内のすべての列を指定したくありません。

どうすればこれを達成できますか?

14
navige

Spark 2.1では、このユースケースをネイティブでサポートする必要があります( #15354 を参照)。

import org.Apache.spark.sql.functions.to_json
df.select(to_json(struct($"c1", $"c2", $"c3")))
19

まず、Cをstructに変換します。

val dfStruct = df.select($"A", $"B", struct($"C1", $"C2", $"C3").alias("C"))

これは、以前と同様にtoJSONを使用してJSONLに変換できる構造です。

dfStruct.toJSON.collect
// Array[String] = Array(
//   {"A":1,"B":"test","C":{"C1":"ab","C2":22,"C3":true}}, 
//   {"A":2,"B":"mytest","C":{"C1":"gh","C2":17,"C3":false}})

単一の列を変換できる組み込みメソッドは認識していませんが、個別にjoinに変換するか、UDFでお気に入りのJSONパーサーを使用できます。

case class C(C1: String, C2: Int, C3: Boolean)

object CJsonizer {
  import org.json4s._
  import org.json4s.JsonDSL._
  import org.json4s.jackson.Serialization
  import org.json4s.jackson.Serialization.write

  implicit val formats = Serialization.formats(org.json4s.NoTypeHints)

  def toJSON(c1: String, c2: Int, c3: Boolean) = write(C(c1, c2, c3))
}


val cToJSON = udf((c1: String, c2: Int, c3: Boolean) => 
  CJsonizer.toJSON(c1, c2, c3))

df.withColumn("c_json", cToJSON($"C1", $"C2", $"C3"))
5
zero323

ここには、JSONパーサーはありません。スキーマに適合します。

import org.Apache.spark.sql.functions.{col, concat, concat_ws, lit}

df.select(
  col(df.columns(0)),
  col(df.columns(1)),
  concat(
    lit("{"), 
    concat_ws(",",df.dtypes.slice(2, df.dtypes.length).map(dt => {
      val c = dt._1;
      val t = dt._2;
      concat(
        lit("\"" + c + "\":" + (if (t == "StringType") "\""; else "")  ),
        col(c),
        lit(if(t=="StringType") "\""; else "") 
      )
    }):_*), 
    lit("}")
  ) as "C"
).collect()
5
David Griffin

このコマンドを使用して、to_jsonの問題を解決します。

output_df = (df.select(to_json(struct(col("*"))).alias("content")))
2
Cyanny