首页 > 解决方案 > 如何从数据框在 scala 中创建地图字段?

问题描述

我有两个数据框如下:

df1:

language (column name)
tamil
telugu
hindi

df2:

id    keywords 
101   ["tamildiary", "tamilkeyboard", "telugumovie"]
102   ["tamilmovie"]
103   ["hindirhymes", "hindimovie"]

使用这两个数据框,我需要创建如下所示的内容。(基本上,需要找到针对每个 id 的语言特定关键字的数量并将它们存储在地图中)

id    keywords
101   {"tamil":2, "telugu":1}
102   {"tamil":1}
103   {"hindi":2}

谁能帮我做这件事。非常感谢!

标签: scaladataframe

解决方案


regexp_replace考虑使用正则表达式匹配具有前导语言关键字的字符串的两个 DataFrame ,然后是几个groupBys 来聚合一个数组(language, count),然后通过 转换为映射map_from_entries

val df1 = Seq("tamil", "telugu", "hindi").toDF("language")

val df2 = Seq(
  (101, Seq("tamildiary", "tamilkeyboard", "telugumovie")),
  (102, Seq("tamilmovie")),
  (103, Seq("hindirhymes", "hindimovie"))
).toDF("id", "keywords")

val pattern = concat(lit("^"), df1("language"), lit(".*"))

df2.
  withColumn("keyword", explode($"keywords")).as("df2").
  join(df1.as("df1"), regexp_replace($"df2.keyword", pattern, lit("")) =!= $"df2.keyword").
  groupBy("id", "language").agg(size(collect_list($"language")).as("count")).
  groupBy("id").agg(map_from_entries(collect_list(struct($"language", $"count"))).as("keywords")).
  show(false)
// +---+-------------------------+
// |id |keywords                 |
// +---+-------------------------+
// |101|[tamil -> 2, telugu -> 1]|
// |103|[hindi -> 2]             |
// |102|[tamil -> 1]             |
// +---+-------------------------+

对于Spark 2.0 - 2.3,创建一个 UDF 来模仿map_from_entries仅在 中可用的功能Spark 2.4+

import org.apache.spark.sql.Row

val arrayToMap = udf{ (arr: Seq[Row]) =>
  arr.map{ case Row(k: String, v: Int) => (k, v) }.toMap
}

df2.
  withColumn("keyword", explode($"keywords")).as("df2").
  join(df1.as("df1"), regexp_replace($"df2.keyword", pattern, lit("")) =!= $"df2.keyword").
  groupBy("id", "language").agg(size(collect_list($"language")).as("count")).
  groupBy("id").agg(arrayToMap(collect_list(struct($"language", $"count"))).as("keywords")).
  show(false)

作为旁注,以下是通过 SQL 表达式的正则表达式匹配条件的几个替代方案:

使用regexp_extract

expr("regexp_extract(df2.keyword, concat('^(', df1.language, ').*'), 1) = df1.language")

使用rlike

expr("df2.keyword rlike concat('^', df1.language, '.*')")

推荐阅读