在Spark中有许多聚类操作是基于combineByKey的,例如group那个家族的操作等。所以combineByKey这个函数也是比较重要,所以下午花了点时间看来下这个函数。也参考了http://www.tuicool.com/articles/miueaqv这篇博客。

先看下combineByKey定义:
/**
   * Generic function to combine the elements for each key using a custom set of aggregation
   * functions. Turns an RDD[(K, V)] into a result of type RDD[(K, C)], for a "combined type" C
   * Note that V and C can be different -- for example, one might group an RDD of type
   * (Int, Int) into an RDD of type (Int, Seq[Int]). Users provide three functions:
   *
   * - `createCombiner`, which turns a V into a C (e.g., creates a one-element list)
   * - `mergeValue`, to merge a V into a C (e.g., adds it to the end of a list)
   * - `mergeCombiners`, to combine two C's into a single one.
   *
   * In addition, users can control the partitioning of the output RDD, and whether to perform
   * map-side aggregation (if a mapper can produce multiple items with the same key).
   */
  def combineByKey[C](createCombiner: V => C,
    mergeValue: (C, V) => C,
    mergeCombiners: (C, C) => C,
    partitioner: Partitioner,
    mapSideCombine: Boolean = true,
    serializer: Serializer = null): RDD[(K, C)] = {
  //实现略
  }
这个函数主要是将键值对[(K,V)]转换为[(K,C)],并且这里的V,C类型可以不同。
对于里面的三个函数的作用,上述的博客的例子讲得很通俗,所以就拿过来直接讲下。
假设我们要将一堆的各类水果给榨果汁,并且要求果汁只能是纯的,不能有其他品种的水果。那么我们需要一下几步:
1 定义我们需要什么样的果汁
2 定义一个榨果汁机,即给定水果,就能给出我们定义的果汁
3 定义一个果汁混合器,即能将相同类型的水果果汁给混合起来。
那么有了这三步,我们就只需要往这个果汁机中仍水果,那么这个果汁机就会产生果汁,并且果汁经过果汁混合器就能将相同品种的水果给聚在一块了。
那么对比上述三步,combineByKey的三个函数也就是这三个功能
1 createCombiner就是定义了v如何转换为c
2 mergeValue 就是定义了如何给定一个V将其与原来的C合并成新的C
3 就是定义了如何将相同key下的C给合并成一个C
下面以一个例子来说明,例如有
data=sc.parallelize([("a",2),("a",3),("b",4)])这个数据,后面的数字表示该字符权重,我们现在想求每个字符出现的平均权重。
data.combineByKey((lambda v:(v,1)),
                              (lambda c,v:(c[0]+v,c[1]+1)),
                              (lambda x,y:(x[0]+y[0],x[1]+y[1])))
那么第一个函数
lambda v:(v,1)中的参数v为键值对的value,我们定义C为(v,1)后面的1用与计数
第二个函数
(lambda c,v:(c[0]+v,c[1]+1)) 这里的参数c可以理解为已经榨好的果汁,v为新加进去的水果,在这里假设此时c为(2,1)v为3,那么应该对应的权重相加,并且计数加1,c[0]+v就是权重相加,c[0]是2,接着就是对其计数加1.(其实这里已经是对相同的key进行归类了,否则c和v的key不同,在这里就会混乱)
第三个函数
(lambda x,y:(x[0]+y[0],x[1]+y[1])),这里是将相同品种的水果果汁给混合起来,所以这里的参数x,y其实都是c类型的。所以这里需要做的就是相应的的权重相加,并且计数相加。这里的x[0]+y[0]就是权重相加,x[1]+y[1]就是计数相加。
 
利用combineByKey实现groupByKey
groupByKey是将key相同的value聚合成一个list
data=sc.parallelize([("a",2),("a",3),("b",4),("b",2)])
一开始自己的写法是:

temp=data.combineByKey((lambda v:[v]),
            (lambda c,v:c.append(v)),
            (lambda x,y:x.extend(y)))

但总是返回[("a",None),("b",None)]后来才发现原来是因为python中对于list.append()没有返回值,所以c.append(v)返回值为None。但是这三个函数都是需要返回值的。对于第二个而言返回的是V加入C后的C,对于第三个而言返回的是C和另一个C合并后的C

所以

def g(c,v);

  c.append(v)

  return c

def h(c1,c2):

  c1.extend(c2)

  return c1

data.combineByKey((lambda v:[v]),g,h)这样就能正确返回了

 
总结:
虽然对于细节了解不够深,但是猜测第二个函数像是Hadoop中的local combiner就是对本地中的相同的key的水果进行榨汁混合,第三个函数像是在全局中对相同的key的水果进行混合(此时不需要榨汁了)。
 
 
 
posted on 2015-06-29 17:41  sunrye  阅读(1247)  评论(0编辑  收藏  举报