Scala Spark在嵌套键值结构上使用reduceByKey

Scala Spark在嵌套键值结构上使用reduceByKey,scala,apache-spark,spark-dataframe,Scala,Apache Spark,Spark Dataframe,我的数据如下所示: 客户1 |项目1:x1、x2、x3;第2项:x1、x4、x5;项目1:x1、x3、x6 |时间1 | url 客户1 |项目1:x1、x7、x3;第2项:x1、x4、x5;项目3:x5 |时间2 | url2 客户2 |项目1:x1、x7、x3;项目3:x5 |时间3 | url3 我想减少相同的customerId和MapValue,以获得每个customerId的不同项的并集: 客户1 |项目1:x1、x2、x3;第2项:x1、x4、x5;第1项:x1、x3、x6;第1项

我的数据如下所示:

客户1 |项目1:x1、x2、x3;第2项:x1、x4、x5;项目1:x1、x3、x6 |时间1 | url
客户1 |项目1:x1、x7、x3;第2项:x1、x4、x5;项目3:x5 |时间2 | url2
客户2 |项目1:x1、x7、x3;项目3:x5 |时间3 | url3

我想减少相同的customerId和MapValue,以获得每个customerId的不同项的并集:

客户1 |项目1:x1、x2、x3;第2项:x1、x4、x5;第1项:x1、x3、x6;第1项: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进行连接,以获得如下结果:

客户1 |项目1:x1、x2、x3、x6、x7;第2项:x1、x4、x5;项目3:x5

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


这可以在spark中实现吗?

是的,您当然可以在spark中实现!然而,你处理问题的方式让它看起来更难

因此,我可以显示一个完整的复制pastable to REPL示例,假设您的数据存储在字符串中(而不是args(0)文件)

您称为“line”的RDD可以作为

到目前为止没有什么新的。下一步是重要的一步。我们可以准备数据,一次完成所有工作,而不是分层进行计数和聚合。这是一个可读性更高、效率也更高的映射,因为它是一个映射,后跟一个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|
+---------+------+---------+-----+----+
+---------+-------+---------+-----+                                             
| 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|
+---------+-------+---------+-----+
最后,我们可以计算并减少到您需要的向量中:

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|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|
+---------+------+---------+-----+----+
+---------+-------+---------+-----+                                             
| 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|
+---------+-------+---------+-----+

如果需要将所有内容分组到向量的数组/序列表示中,可以通过进一步聚合数据来实现这一点。希望这有帮助

还有一些时间和url不存在的值,在这种情况下,arr(2)和arr(3)将因ArrayIndexOutOfBoundsException而失败。是否可以使用4列筛选行。类似于line.split(“\\\\”).filter(l=>l.length==4)我可以忽略没有url和时间的数据。如果不需要,只需从元组中删除这些列。或者,
导入scala.util.Try
,然后将这些行更新为
val time=Try(Some(arr(2)).getOrElse(None)
val url=Try(Some(arr(3))。getOrElse(None)
取决于您是否需要这些行中的值。如果没有,则可以按照建议进行筛选。如果您这样做了,请参阅前面的注释:)这很有效。。谢谢!:)我需要的reducer是
valreduced=mapped.map{case(customer,itemKey,itemValue,time,url)=>((customer,itemValue),1)}。还原基(u+u0)。映射{case((customer,itemValue),count)=>(customer,itemValue,count)}
关于最后一部分。。如何为每个customerId创建itemValue count的向量。对于egs。如果标题[x0,x1,x2,x3,x4,x5,x6,x7,x8,x9]客户D2:根据每个x获得的值,项目频率[0 1 0 0 3 0 2 0 0 0 0],如果未获得,则为0,其中