我有一个包含两列(键,值)的DataFrame,如下所示:
+------------+--------------------+
| key| value|
+------------+--------------------+
|[sid2, sid5]| value1 |
| [sid2]| value2 |
| [sid6]| value3 |
+------------+--------------------+
键是一组字符串,我想应用reduceByKey转换,如果它们之间有交叉,则两个键相等,输出应如下所示:
+------------+--------------------+
| key| value|
+------------+--------------------+
|[sid2, sid5]| [value1, value2] |
| [sid6]| value3 |
+------------+--------------------+
我尝试使用case类作为关键wapper并覆盖equals和hashCode函数,但它不起作用(SPARK-2620)。
知道怎么做吗? 提前谢谢。
更新 - DataFrame架构:
root
|-- id1: array (nullable = true)
| |-- element: string (containsNull = true)
|-- events1: array (nullable = true)
| |-- element: struct (containsNull = true)
| | |-- sid: string (nullable = true)
| | |-- uid: string (nullable = true)
| | |-- action: string (nullable = true)
| | |-- touchPoint: string (nullable = true)
| | |-- result: string (nullable = true)
| | |-- timestamp: long (nullable = false)
| | |-- url: string (nullable = true)
| | |-- onlineId: long (nullable = false)
| | |-- channel: string (nullable = true)
| | |-- category: string (nullable = true)
| | |-- clientId: long (nullable = false)
| | |-- newUser: boolean (nullable = false)
| | |-- userAgent: string (nullable = true)
| | |-- group: string (nullable = true)
| | |-- pageType: string (nullable = true)
| | |-- clientIP: string (nullable = true)
答案 0 :(得分:1)
这不能用reduceByKey
来解决,因为问题定义不适合byKey
转换。核心要求是密钥具有明确定义的标识,但这不是这种情况。
考虑我们拥有密钥[sid2, sid4, sid5]
和[sid2, sid3, sid5]
的数据集。在这种情况下,无法将对象唯一地分配给分区。覆盖哈希码根本不会帮助你。
更糟糕的是,一般情况下的问题是分布式的。考虑一组集合,例如对于每个集合,至少有一个其他集合具有非空交集。在这种情况下,所有值都应合并为一个"集群"。
总体而言 - 对于没有相当严格限制的Spark而言,这不是一个好问题,并且根本无法通过基本byKey
转换来解决。
低效的解决方案,可能部分解决您的问题是使用笛卡尔积:
rdd.cartesian(rdd)
.filter { case ((k1, _), (k2, _)) => intersects(v1, v2) }
.map { case ((k, _), (_, v)) => (k, v) }
.groupByKey
.mapValues(_.flatten.toSet)
然而,这是低效的,并没有解决歧义。
答案 1 :(得分:1)
我认为使用Spark SQL的数据集API是可行的(并且直接翻译了基于RDD的@ user9003280解决方案)。
// the dataset
val kvs = Seq(
(Seq("sid2", "sid5"), "value1"),
(Seq("sid2"), "value2"),
(Seq("sid6"), "value3")).toDF("key", "value")
scala> kvs.show
+------------+------+
| key| value|
+------------+------+
|[sid2, sid5]|value1|
| [sid2]|value2|
| [sid6]|value3|
+------------+------+
val intersect = udf { (ss: Seq[String], ts: Seq[String]) => ss intersect ts }
val solution = kvs.as("left")
.join(kvs.as("right"))
.where(size(intersect($"left.key", $"right.key")) > 0)
.select($"left.key", $"right.value")
.groupBy("key")
.agg(collect_set("value") as "values")
.dropDuplicates("values")
scala> solution.show
+------------+----------------+
| key| values|
+------------+----------------+
| [sid6]| [value3]|
|[sid2, sid5]|[value2, value1]|
+------------+----------------+