如何使用CombineByKey RDD分组和汇总多个字段?

时间:2018-11-13 10:12:37

标签: scala apache-spark group-by rdd apache-spark-mllib

我有一个样本文件,我试图使用combineByKey来查找给定字段的另一个字段的总数,以及另一个字段的计数和值列表。我试图了解combineByKey,这与我使用aggregateByKeythis question所理解的要求相同,现在我想了解combineByKey

我尝试了以下与aggregateByKey相同的代码,但出现类型不匹配错误。我不确定createCombinermergeValuemergeCombiner的类型是否正确。请帮助我更好地了解combineByKey

样本数据:

44,8602,37.19
44,8331,99.19
44,1919,39.54
44,2682,41.88
44,7366,66.54
44,3405,81.09
44,9957,94.79 

combineByKey的代码:

val rdd = sc.textFile("file:///../customer-orders.csv_sample").map(x => x.split(",")).map(x => (x(0).toInt, (x(1).toInt, x(2).toFloat)))

def createCombiner = (tuple: (Seq[Int],Double, Int)) => (tuple,1)

def mergeValue = (acc: (Seq[Int],Double,Int),xs: (Int,Float)) => {
  println(s"""mergeValue: (${acc._1} ++ ${Seq(xs._1)}, ${acc._2} +${xs._2},${acc._3} + 1)""")
  (acc._1 ++ Seq(xs._1), acc._2 + xs._2, acc._3 + 1)
}

def mergeCombiner = (acc1: (Seq[Int],Double,Int), acc2: (Seq[Int],Double,Int)) => {
  println(s"""mergeCombiner: (${acc1._1} ++ ${acc2._1}, ${acc1._2} +${acc2._2}, ${acc1._3} + ${acc2._3})""")
  (acc1._1 ++ acc2._1, acc1._2 + acc2._2, acc1._3 + acc2._3)
}

rdd.combineByKey(createCombiner,mergeValue,mergeCombiner).collect().foreach(println)

错误消息:

error: type mismatch;
found   : ((Seq[Int], Double, Int)) => ((Seq[Int], Double, Int), Int)
required: ((Int, Float)) => ?
rdd.combineByKey(createCombiner,mergeValue,mergeCombiner).collect().foreach(println)
                 ^

预期结果是:

customerid, (orderids,..,..,....), totalamount, number of orderids

使用提供的样本数据将是:

(44,(List(8602, 8331, 1919, 2682, 7366, 3405, 9957),460.2200012207031,7))

不匹配指向createCombiner。有人可以帮助我了解combineByKey吗?

3 个答案:

答案 0 :(得分:0)

我对Spark不熟悉。

希望这对您有所帮助。

val array = Array((44,8602,37.19),(44,8331,99.19),(44,1919,39.54),(44,2682,41.88),(44,7366,66.54),(44,3405,81.09),(44,9957,94.79))

array.groupBy(_._1).map(e => (e._1, e._2.map(_._2).toList, e._2.map(_._3).sum))
//res1: scala.collection.immutable.Iterable[(Int, List[Int], Double)] = List((44,List(8602, 8331, 1919, 2682, 7366, 3405, 9957),460.21999999999997))

我看到您的此错误是由于

  

def createCombiner =(元组:(Seq [Int],Double,Int))=>(元组,1)

我认为createCombiner应该取一些Seq的元组,并返回IntSeq(groupby)的元组

  

def createCombiner =(元组:Seq [(Int,Int,Double)])=> tuple.groupBy(_._ 1)

希望这会有所帮助。

答案 1 :(得分:0)

这是CombineByKey的签名:

combineByKey[C](createCombiner: (V) ⇒ C, mergeValue: (C, V) ⇒ C, mergeCombiners: (C, C) ⇒ C): RDD[(K, C)]

mergeValue的类型为(C, V) => C

其中C应该为((Seq[Int],Double, Int), Int),而V应该为(Seq[Int],Double, Int)

您的mergeValue方法的类型为C (Seq[Int],Double,Int)和V (Int,Float)

mergeCombiner的类型也不正确。

这应该是(C, C) => C,其中C是((Seq[Int],Double, Int), Int)

答案 2 :(得分:0)

这里的问题是createCombiner函数。查看combineByKey

combineByKey[C](createCombiner: (V) ⇒ C, mergeValue: (C, V) ⇒ C, mergeCombiners: (C, C) ⇒ C): RDD[(K, C)]

简单地说,C是您要以((Seq[Int], Double, Int))结尾的格式,而V是您要以((Int, Double))开头的格式。在这里,我将Float更改为Double,因为这是Spark中常用的方法。这意味着createCombiner函数应如下所示:

def createCombiner = (tuple: (Int, Double)) => (Seq(tuple._1), tuple._2, 1)

mergeValuemergeCombiner看起来都不错,但是,如果在集群上执行代码,则在Spark中将看不到任何打印语句(请参见:Spark losing println() on stdout)。