如何从字符串键=由“;”分隔的值创建数据帧

时间:2016-09-17 16:10:03

标签: apache-spark hive apache-spark-sql

我有一个Hive表,结构如下:

enter image description here

我需要读取字符串字段,打破键并变成Hive表列,最终表应如下所示:

enter image description here

非常重要,字符串中的键数是动态的,键的名称也是动态的

尝试使用Spark SQL读取字符串,使用基于所有字符串的模式创建数据帧,并使用saveAsTable()函数将数据帧转换为hive final表,但不知道如何执行此操作< / p>

有什么建议吗?

1 个答案:

答案 0 :(得分:1)

天真(假设(code, date)组合并且=中没有嵌入的;string,可能如下所示:

import org.apache.spark.sql.functions.{explode, split}

val df = Seq(
    (1, 1, "key1=value11;key2=value12;key3=value13;key4=value14"),
    (1, 2, "key1=value21;key2=value22;key3=value23;key4=value24"),
    (2, 4, "key3=value33;key4=value34;key5=value35")
).toDF("code", "date", "string")

val bits = split($"string", ";")
val kv = split($"pair", "=")

df
  .withColumn("bits", bits)  // Split column by `;`
  .withColumn("pair", explode($"bits"))  // Explode into multiple rows
  .withColumn("key", kv(0))  // Extract key
  .withColumn("val", kv(1))  // Extract value 
  // Pivot to wide format
  .groupBy("code", "date")
  .pivot("key")
  .agg(first("val"))

// +----+----+-------+-------+-------+-------+-------+
// |code|date|   key1|   key2|   key3|   key4|   key5|
// +----+----+-------+-------+-------+-------+-------+
// |   1|   2|value21|value22|value23|value24|   null|
// |   1|   1|value11|value12|value13|value14|   null|
// |   2|   4|   null|   null|value33|value34|value35|
// +----+----+-------+-------+-------+-------+-------+

(code, date)不唯一时,可以轻松调整以处理案例,并且您可以使用string处理更复杂的UDF模式。

使用RDDDataset时,您可能会更好,具体取决于您使用的语言和列数。同样值得考虑放弃explode / pivot以支持UDF。

val parse = udf((text: String) => text.split(";").map(_.split("=")).collect {
  case Array(k, v) => (k, v)
}.toMap)

val keys = udf((pairs: Map[String, String]) => pairs.keys.toList)

// Parse strings to Map[String, String]
val withKVs = df.withColumn("kvs", parse($"string"))

val keys = withKVs
  .select(explode(keys($"kvs"))).distinct // Get unique keys
  .as[String] 
  .collect.sorted.toList // Collect and sort

// Build a list of expressions for subsequent select
val exprs = keys.map(key => $"kvs".getItem(key).alias(key)) 

withKVs.select($"code" :: $"date" :: exprs: _*)

在Spark 1.5中,您可以尝试:

val keys = withKVs.select($"kvs").rdd
  .flatMap(_.getAs[Map[String, String]]("kvs").keys)
  .distinct
  .collect.sorted.toList