对元组中的项进行求和

时间:2014-05-01 16:35:13

标签: scala apache-spark

下面是元组列表的数据结构,ot类型List [(String,String,Int)]

   val data3 = (List( ("id1" , "a", 1), ("id1" , "a", 1), ("id1" , "a", 1) , ("id2" , "a", 1)) )
                                                  //> data3  : List[(String, String, Int)] = List((id1,a,1), (id1,a,1), (id1,a,1),
                                                  //|  (id2,a,1))

我试图计算与每个id相关联的每个Int值的出现次数。因此,上述数据结构应转换为List((id1,a,3) , (id2,a,1))

这是我想出的,但我不确定如何在元组中对类似的项目进行分组:

data3.map( { case (id,name,num) => (id , name , num + 1)})
                                              //> res0: List[(String, String, Int)] = List((id1,a,2), (id1,a,2), (id1,a,2), (i
                                              //| d2,a,2))

在实践中,data3属于spark obj RDD类型,我在此示例中使用List进行测试,但同一解决方案应与RDD兼容。我使用List进行本地测试。

更新:根据maasg提供的以下代码:

val byKey = rdd.map({case (id1,id2,v) => (id1,id2)->v})
val byKeyGrouped = byKey.groupByKey
val result = byKeyGrouped.map{case ((id1,id2),values) => (id1,id2,values.sum)}

我需要稍微修改才能进入我期望的格式为

的格式
.RDD[(String, Seq[(String, Int)])]
which corresponds to .RDD[(id, Seq[(name, count-of-names)])]

val byKey = rdd.map({case (id1,id2,v) => (id1,id2)->v})
val byKeyGrouped = byKey.groupByKey
val result = byKeyGrouped.map{case ((id1,id2),values) => ((id1),(id2,values.sum))}
val counted = result.groupedByKey

5 个答案:

答案 0 :(得分:3)

在Spark中,您可以这样做:(使用Spark Shell来说明)

val l = List( ("id1" , "a", 1), ("id1" , "a", 1), ("id1" , "a", 1) , ("id2" , "a", 1))
val rdd = sc.parallelize(l)
val grouped = rdd.groupBy{case (id1,id2,v) => (id1,id2)}
val result = grouped.map{case ((id1,id2),values) => (id1,id2,value.foldLeft(0){case (cumm, tuple) => cumm + tuple._3})}

另一种选择是将rdd映射到PairRDD并使用groupByKey

val byKey = rdd.map({case (id1,id2,v) => (id1,id2)->v})
val byKeyGrouped = byKey.groupByKey
val result = byKeyGrouped.map{case ((id1,id2),values) => (id1,id2,values.sum)}

选项2在处理大型集时是一个稍好的选项,因为它不会复制累积值中的id。

答案 1 :(得分:3)

当我使用scala-ide时,这似乎有效:

data3
  .groupBy(tupl => (tupl._1, tupl._2))
  .mapValues(v =>(v.head._1,v.head._2, v.map(_._3).sum))
  .values.toList

结果与问题所要求的相同

  

res0:List [(String,String,Int)] = List((id1,a,3),(id2,a,1))

答案 2 :(得分:1)

您应该查看List.groupBy

您可以使用id作为键,然后使用地图中值的长度(即所有共享相同ID的项目)来了解计数。

答案 3 :(得分:1)

@vptheron有正确的想法。 可以在docs

中看到
  

def groupBy [K](f:(A)⇒K):Map [K,List [A]]

     

根据某些鉴别器功能将此列表分区为列表映射。

     

注意:视图不会重新实现此方法。这意味着当应用于视图时,它将>始终强制视图并返回新列表。

     

K 鉴别器功能返回的键类型    f 鉴别器功能。
  的返回
     从键到列表的映射,使得以下不变量成立:      (xs分区f)(k)= xs过滤器(x => f(x)== k)      也就是说,每个密钥k都绑定到那些元素x的列表,其中f(x)等于k。

因此,当与groupBy一起使用时,类似下面的函数将为您提供一个键,其中键为ids。 (对不起,我无法访问Scala编译器,所以我无法测试)

def f(tupule: A) :String = {
  return tupule._1
}

然后,您必须为List中的每个id迭代Map,并总结整数出现次数。这很简单,但如果您仍然需要帮助,请在评论中提问。

答案 4 :(得分:0)

以下是最易读,最有效和可扩展的

data.map {
  case (key1, key2, value) => ((key1, key2), value)
}
.reduceByKey(_ + _)

将提供RDD[(String, String, Int)]。通过使用reduceByKey,它意味着求和将是并列化的,即对于非常大的群组,它将被分发并且总和将在地图侧发生。考虑只有10个组但数十亿条记录的情况,使用.sum将无法扩展,因为它只能分发到10个核心。

关于其他答案的更多说明:

此处不必使用head.mapValues(v =>(v.head._1,v.head._2, v.map(_._3).sum))只能使用.mapValues(v =>(v_1, v._2, v.map(_._3).sum))

如果以上显示的foldLeft显示.map(_._3).sum

,则使用val result = grouped.map{case ((id1,id2),values) => (id1,id2,value.foldLeft(0){case (cumm, tuple) => cumm + tuple._3})}非常可怕