Spark在嵌套的Key-Value结构上使用reduceByKey

时间:2017-05-09 09:24:12

标签: scala apache-spark spark-dataframe

我的数据如下:

  

customer1表| ITEM1:X1,X2,X3; ITEM2:X1,X4,X5;物品1:X1,X3,X6 |时间1 |网址
   customer1表| ITEM1:X1,X7,X3; ITEM2:X1,X4,X5;项目3:X5 |时间2 | URL2
   顾客2 | ITEM1:X1,X7,X3;项目3:X5 |时间3 | URL3

我希望ReduceByKey使用相同的customerIds和mapValues来为每个customerId获取不同项目的联合:

  

customer1表| ITEM1:X1,X2,X3; ITEM2:X1,X4,X5; ITEM1:X1,X3,X6; ITEM1:X1,X7,X3;项目3:X5

我能够通过以下方式实现:

  

val line = spark.sparkContext.textFile(args(0))
    val record = line.map(l => l.split(" \ |"))。map(l =>(l(0),l(1)))。reduceByKey(( x,y)=> x.union(y))。mapValues(x => x.distinct)

现在,我希望第二列中的每个项目都是唯一的,同一个键中的所有值都应该使用union和distinct连接,以获得类似的内容:

  

customer1表| ITEM1:X1,X2,X3,X6,X7; ITEM2:X1,X4,X5;项目3:X5

一旦完成,我想选择每个x的所有频率,例如:x1:2,x2:1 .... 并使用我得到的频率为customerId更新了x(1-10)的向量。

这可以用火花来实现吗?

1 个答案:

答案 0 :(得分:0)

是的,你当然可以在Spark中做到这一点!你接近这个问题的方式使得它实际上看起来有点困难。

所以我可以向REPL示例显示一个完整的副本 - 让我们假设你的数据存储在一个字符串(而不是args(0)文件)中

val data = """Customer1| item1:x1,x2,x3; item2:x1,x4,x5; item1:x1,x3,x6|time1|url
Customer1| item1:x1,x7,x3; item2:x1,x4,x5; item3:x5|time2|url2
Customer2| item1:x1,x7,x3; item3:x5|time3|url3"""

和您拨打的RDD" line"可以读入RDD" rdd"如

val rdd = sc.parallelize(data.split("\n"))
到目前为止还没什么新鲜的。下一步是重要的一步。我们可以将数据准备好一次完成,而不是在层中进行计数和聚合。这更具可读性,也更有效,因为它是单个映射,后跟单个reduce。

val mapped= rdd.flatMap(line => {
   val arr = line.split("\\|")
   val customer = arr(0)
   val items = arr(1)
   val time = arr(2)
   val url = arr(3)

   items.split(";").flatMap(item => {
      val itemKey = item.split(":")(0)
      val itemValues = item.split(":")(1).split(",")

      itemValues.map(value => (customer, itemKey, value, time, url))
   })
})

我们可以看到其中的内容我们可以使用mapped.toDF("customer", "itemId", "itemValue", "time", "url").show

很好地打印出来
+---------+------+---------+-----+----+
| customer|itemId|itemValue| time| url|
+---------+------+---------+-----+----+
|Customer1| item1|       x1|time1| url|
|Customer1| item1|       x2|time1| url|
|Customer1| item1|       x3|time1| url|
|Customer1| item2|       x1|time1| url|
|Customer1| item2|       x4|time1| url|
|Customer1| item2|       x5|time1| url|
|Customer1| item1|       x1|time1| url|
|Customer1| item1|       x3|time1| url|
|Customer1| item1|       x6|time1| url|
|Customer1| item1|       x1|time2|url2|
|Customer1| item1|       x7|time2|url2|
|Customer1| item1|       x3|time2|url2|
|Customer1| item2|       x1|time2|url2|
|Customer1| item2|       x4|time2|url2|
|Customer1| item2|       x5|time2|url2|
|Customer1| item3|       x5|time2|url2|
|Customer2| item1|       x1|time3|url3|
|Customer2| item1|       x7|time3|url3|
|Customer2| item1|       x3|time3|url3|
|Customer2| item3|       x5|time3|url3|
+---------+------+---------+-----+----+

最后,我们可以计算并缩减为您需要的向量:

val reduced = mapped.map{case (customer, itemKey, itemValue, time, url) => ((customer, itemKey, itemValue), 1)}.
   reduceByKey(_+_).
   map{case ((customer, itemKey, itemValue), count) => (customer, itemKey, itemValue, count)}

并查看它:reduced.toDF("customer", "itemKey", "itemValue", "count").show

+---------+-------+---------+-----+                                             
| customer|itemKey|itemValue|count|
+---------+-------+---------+-----+
|Customer1|  item1|       x2|    1|
|Customer1|  item1|       x1|    3|
|Customer2|  item1|       x7|    1|
|Customer1|  item1|       x6|    1|
|Customer1|  item1|       x7|    1|
|Customer2|  item1|       x3|    1|
|Customer2|  item3|       x5|    1|
|Customer1|  item2|       x5|    2|
|Customer1|  item2|       x4|    2|
|Customer1|  item2|       x1|    2|
|Customer1|  item3|       x5|    1|
|Customer1|  item1|       x3|    3|
|Customer2|  item1|       x1|    1|
+---------+-------+---------+-----+

如果您需要将所有内容分组到矢量的Array / Seq表示中,您可以通过进一步聚合数据来完成此操作。希望这有帮助!