将Map分解为键作为列及其在其中的值

时间:2019-08-21 13:59:48

标签: apache-spark apache-spark-sql apache-spark-dataset

我已经为此苦苦挣扎了一段时间,无法解决这个问题,到目前为止,我只发现了explode()的{​​{1}}列到MapType的示例行条目。

例如,我想要实现的是在同一行中具有5个列的5个条目的Map。

以这个DF为例...

n

在分解列case class SampleRow(one: String, two: String, three: String, four: String, five: Map[String, String]) val df = List( SampleRow( "one", "two", "three", "four", Map("sample_one" -> "hey", "sample_two" -> "hey")) ).toDF() 之后,DF应为以下内容。

five

到目前为止,我一直尝试以下操作。

    Columns ->  one | two | three | four | sample_one | sample_two
    Values  -> "one"|"two"|"three"|"four"|   "hey"    |    "hey"

但是这样做会在控制台中提示以下错误。

val explodedDS = originDS
      .select(cols :+ $"key".as("outerMap") :+ $"value.*":_*) // Column 'value' as a previous Map has been exploded before

我了解到将Map to Columns分解会产生一个问题,即直到所有Row对象都包含完全相同数量的Columns(为null或带有值),才可以推断模式,对吗?

但是除此之外,尽管存在模式问题,是否还有其他选择可以完成此任务?

1 个答案:

答案 0 :(得分:1)

这可能不是最快的,但似乎可行:

import org.apache.spark.sql.{Encoder, Encoders}
import org.apache.spark.sql.functions._
import spark.implicits._

case class SampleRow(one: String, two: String, three: String, four: String, five: Map[String, String])
implicit def enc: Encoder[SampleRow] = Encoders.product[SampleRow]

val df = spark.createDataset(List(
          SampleRow(
            "one", 
            "two", 
            "three", 
            "four", 
            Map("sample_one" -> "hey", "sample_two" -> "hey"))
        ))

df.select($"*", explode($"five"))
  .groupBy("one", "two", "three", "four")
  .pivot("key")
  .agg(first($"value"))
  .show()

这将产生所需的输出:

+---+---+-----+----+----------+----------+
|one|two|three|four|sample_one|sample_two|
+---+---+-----+----+----------+----------+
|one|two|three|four|       hey|       hey|
+---+---+-----+----+----------+----------+

对于您的实际用例,这可能无法完全概括,但是应该足够接近才能使之可行。

相关问题