繁体   English   中英

按键减少和求和元组

[英]Reduce and sum tuples by key

在我的Spark Scala应用程序中,我具有以下格式的RDD:

(05/05/2020, (name, 1))
(05/05/2020, (name, 1))
(05/05/2020, (name2, 1))
...
(06/05/2020, (name, 1))

我要做的是按日期对这些元素进行分组,并对具有与键相同的“名称”的元组求和。

预期产量:

(05/05/2020, List[(name, 2), (name2, 1)]),
(06/05/2020, List[(name, 1)])
...

为了做到这一点,我目前正在使用groupByKey操作和一些额外的转换,以便按键对元组进行分组,并为那些共享相同的元组计算总和。

出于性能原因,我想用reduceByKeyaggregateByKey替换此groupByKey操作,以减少通过网络传输的数据量。

但是,我无法确定如何执行此操作。 这两个转换都将值(在我的情况下为元组)之间的函数作为参数,因此我看不到如何按键对元组进行分组以计算其总和。

可以吗

是的.aggeregateBykey()可以按以下方式使用:

import scala.collection.mutable.HashMap

def merge(map: HashMap[String, Int], element: (String, Int)) = {
 if(map.contains(element._1)) map(element._1) += element._2 else map(element._1) = element._2
 map
}

val input = sc.parallelize(List(("05/05/2020",("name",1)),("05/05/2020", ("name", 1)),("05/05/2020", ("name2", 1)),("06/05/2020", ("name", 1))))

val output = input.aggregateByKey(HashMap[String, Int]())({
  //combining map & tuple   
  case (map, element) => merge(map, element) 
}, {
  // combining two maps 
  case (map1, map2) => {
   val combined = (map1.keySet ++ map2.keySet).map { i=> (i,map1.getOrElse(i,0) + map2.getOrElse(i,0)) }.toMap
   collection.mutable.HashMap(combined.toSeq: _*)
  } 
}).mapValues(_.toList)

积分: 合并两个地图并求和相同键值的最佳方法?

这是您可以使用reduceByKey合并元组的方法:

/**
File /path/to/file1:
15/04/2010  name
15/04/2010  name
15/04/2010  name2
15/04/2010  name2
15/04/2010  name3
16/04/2010  name
16/04/2010  name

File /path/to/file2:
15/04/2010  name
15/04/2010  name3
**/

import org.apache.spark.rdd.RDD

val filePaths = Array("/path/to/file1", "/path/to/file2").mkString(",")

val rdd: RDD[(String, (String, Int))] = sc.textFile(filePaths).
  map{ line =>
    val pair = line.split("\\t", -1)
    (pair(0), (pair(1), 1))
  }

rdd.
  map{ case (k, (n, v)) => (k, Map(n -> v)) }.
  reduceByKey{ (acc, m) =>
    acc ++ m.map{ case (n, v) => (n -> (acc.getOrElse(n, 0) + v)) }
  }.
  map(x => (x._1, x._2.toList)).
  collect
// res1: Array[(String, List[(String, Int)])] = Array(
//   (15/04/2010, List((name,3), (name2,2), (name3,2))), (16/04/2010, List((name,2)))
// )

请注意,由于我们希望将元组合并为Map元素,因此需要初始映射,并且RDD [K,V]的reduceByKey在转换前后需要相同的数据类型V

def reduceByKey(func: (V, V) => V): RDD[(K, V)]

您可以将RDD转换为DataFrame并只使用带有sum的groupBy,这是一种方法

import org.apache.spark.sql.types._
val schema = StructType(StructField("date", StringType, false) :: StructField("name", StringType, false) ::  StructField("value", IntegerType, false) :: Nil)

val rd = sc.parallelize(Seq(("05/05/2020", ("name", 1)),
("05/05/2020", ("name", 1)),
("05/05/2020", ("name2", 1)),
("06/05/2020", ("name", 1))))

val df = spark.createDataFrame(rd.map{ case (a, (b,c)) => Row(a,b,c)},schema)
df.show

+----------+-----+-----+
|      date| name|value|
+----------+-----+-----+
|05/05/2020| name|    1|
|05/05/2020| name|    1|
|05/05/2020|name2|    1|
|06/05/2020| name|    1|
+----------+-----+-----+

val sumdf = df.groupBy("date","name").sum("value")
sumdf.show

+----------+-----+----------+
|      date| name|sum(value)|
+----------+-----+----------+
|06/05/2020| name|         1|
|05/05/2020| name|         2|
|05/05/2020|name2|         1|
+----------+-----+----------+

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM