aggregate算是spark中比较常用的一个函数,理解起来会比较费劲一些,现在通过几个详细的例子带大家来着重理解一下aggregate的用法。

1.先看看aggregate的函数签名
在spark的源码中,可以看到aggregate函数的签名如下:

def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U

可以看出,这个函数是个柯里化的方法,输入参数分为了两部分:(zeroValue: U)与(seqOp: (U, T) => U, combOp: (U, U) => U)

2.aggregate的用法
函数签名比较复杂,可能有的小伙伴看着就晕菜了。别捉急,我们再来看看函数前面的注释,关于此函数的用法我们就会比较清楚。

/**
* Aggregate the elements of each partition, and then the results for all the partitions, using
* given combine functions and a neutral "zero value". This function can return a different result
* type, U, than the type of this RDD, T. Thus, we need one operation for merging a T into an U
* and one operation for merging two U's, as in scala.TraversableOnce. Both of these functions are
* allowed to modify and return their first argument instead of creating a new U to avoid memory
* allocation.
*
* @param zeroValue the initial value for the accumulated result of each partition for the
* `seqOp` operator, and also the initial value for the combine results from
* different partitions for the `combOp` operator - this will typically be the
* neutral element (e.g. `Nil` for list concatenation or `0` for summation)
* @param seqOp an operator used to accumulate results within a partition
* @param combOp an associative operator used to combine results from different partitions
*/

翻译过来就是:aggregate先对每个分区的元素做聚集,然后对所有分区的结果做聚集,聚集过程中,使用的是给定的聚集函数以及初始值”zero value”。这个函数能返回一个与原始RDD不同的类型U,因此,需要一个合并RDD类型T到结果类型U的函数,还需要一个合并类型U的函数。这两个函数都可以修改和返回他们的第一个参数,而不是重新新建一个U类型的参数以避免重新分配内存。
参数zeroValue:seqOp运算符的每个分区的累积结果的初始值以及combOp运算符的不同分区的组合结果的初始值 - 这通常将是初始元素(例如“Nil”表的列表 连接或“0”表示求和)
参数seqOp: 每个分区累积结果的聚集函数。
参数combOp: 一个关联运算符用于组合不同分区的结果

3.求平均值
看来了上面的原理介绍,接下来我们看干货。
首先可以看网上最多的一个例子:

val list = List(1,2,3,4,5,6,7,8,9)
val (mul, sum, count) = sc.parallelize(list, 2).aggregate((1, 0, 0))(
(acc, number) => (acc._1 * number, acc._2 + number, acc._3 + 1),
(x, y) => (x._1 * y._1, x._2 + y._2, x._3 + y._3)
)
(sum / count, mul)

在常见的求均值的基础上稍作了变动,sum是求和,count是累积元素的个数,mul是求各元素的乘积。
解释一下具体过程:
1.初始值是(1, 0 ,0)
2.number是函数中的T,也就是List中的元素,此时类型为Int。而acc的类型为(Int, Int, Int)。acc._1 * num是各元素相乘(初始值为1),acc._2 + number为各元素相加。
3.sum / count为计算平均数。

4.另外的例子
为了加深理解,看另外一个的例子。

val raw = List("a", "b", "d", "f", "g", "h", "o", "q", "x", "y")
val (biggerthanf, lessthanf) = sc.parallelize(raw, 1).aggregate((0, 0))(
(cc, str) => {
var biggerf = cc._1
var lessf = cc._2
if (str.compareTo("f") >= 0) biggerf = cc._1 + 1
else if(str.compareTo("f") < 0) lessf = cc._2 + 1
(biggerf, lessf)
},
(x, y) => (x._1 + y._1, x._2 + y._2)
)

这个例子中,我们想做的就是统计一下在raw这个list中,比”f”大与比”f”小的元素分别有多少个。代码本身的逻辑也比较简单,就不再更多解释。

5.aggregateByKey与combineByKey的比较
aggregate是针对序列的操作,aggregateByKey则是针对k,v对的操作。顾名思义,aggregateByKey则是针对key做aggregate操作。spark中函数的原型如下:

def aggregateByKey[U: ClassTag](zeroValue: U)(seqOp: (U, V) => U,
combOp: (U, U) => U): RDD[(K, U)] = self.withScope {
aggregateByKey(zeroValue, defaultPartitioner(self))(seqOp, combOp)
}

都是针对k,v对的操作,spark中还有一个combineByKey的操作:

def combineByKey[C](
createCombiner: V => C,
mergeValue: (C, V) => C,
mergeCombiners: (C, C) => C): RDD[(K, C)] = self.withScope {
combineByKeyWithClassTag(createCombiner, mergeValue, mergeCombiners)(null)
}

为了看清楚两个的联系,我们再看看 aggregateByKey里面的真正实现:

def aggregateByKey[U: ClassTag](zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U,
combOp: (U, U) => U): RDD[(K, U)] = self.withScope {
// Serialize the zero value to a byte array so that we can get a new clone of it on each key
val zeroBuffer = SparkEnv.get.serializer.newInstance().serialize(zeroValue)
val zeroArray = new Array[Byte](zeroBuffer.limit)
zeroBuffer.get(zeroArray)

lazy val cachedSerializer = SparkEnv.get.serializer.newInstance()
val createZero = () => cachedSerializer.deserialize[U](ByteBuffer.wrap(zeroArray))

// We will clean the combiner closure later in `combineByKey`
val cleanedSeqOp = self.context.clean(seqOp)
combineByKeyWithClassTag[U]((v: V) => cleanedSeqOp(createZero(), v),
cleanedSeqOp, combOp, partitioner)
}

从上面这段源码可以清晰看出,aggregateByKey调用的就是combineByKey方法。seqOp方法就是mergeValue,combOp方法则是mergeCombiners,cleanedSeqOp(createZero(), v)是createCombiner, 也就是传入的seqOp函数, 只不过其中一个值是传入的zeroValue而已!
因此, 当createCombiner和mergeValue函数的操作相同, aggregateByKey更为合适!

spark aggregate函数详解的更多相关文章

  1. spark wordcont Spark: sortBy和sortByKey函数详解

    //统计单词top10def main(args: Array[String]): Unit = { val conf = new SparkConf().setAppName("tst&q ...

  2. malloc 与 free函数详解<转载>

    malloc和free函数详解   本文介绍malloc和free函数的内容. 在C中,对内存的管理是相当重要.下面开始介绍这两个函数: 一.malloc()和free()的基本概念以及基本用法: 1 ...

  3. NSSearchPathForDirectoriesInDomains函数详解

    NSSearchPathForDirectoriesInDomains函数详解     #import "NSString+FilePath.h" @implementation ...

  4. JavaScript正则表达式详解(二)JavaScript中正则表达式函数详解

    二.JavaScript中正则表达式函数详解(exec, test, match, replace, search, split) 1.使用正则表达式的方法去匹配查找字符串 1.1. exec方法详解 ...

  5. Linux C popen()函数详解

    表头文件 #include<stdio.h> 定义函数 FILE * popen( const char * command,const char * type); 函数说明 popen( ...

  6. kzalloc 函数详解(转载)

    用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0. view plain /** * kzal ...

  7. Netsuite Formula > Oracle函数列表速查(PL/SQL单行函数和组函数详解).txt

    PL/SQL单行函数和组函数详解 函数是一种有零个或多个参数并且有一个返回值的程序.在SQL中Oracle内建了一系列函数,这些函数都可被称为SQL或PL/SQL语句,函数主要分为两大类: 单行函数 ...

  8. jQuery.attr() 函数详解

    一,jQuery.attr()  函数详解: http://www.365mini.com/page/jquery-attr.htm 二,jQuery函数attr()和prop()的区别: http: ...

  9. memset函数详解

    语言中memset函数详解(2011-11-16 21:11:02)转载▼标签: 杂谈 分类: 工具相关  功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大 ...

随机推荐

  1. java的类class 和对象object

    java 语言的源代码是以类为单位存放在文件中,已public修饰的类名须和存放这个类的源文件名一样.而 一个源文件中只能有一个public的类,类名的首字母通常为大写. 使用public修饰的类可以 ...

  2. 某些material英文翻译

    chrome  铬,铬合金, 镀铬 matte  无光泽的,不光滑的 Decal   陶瓷的 duo color  双色 livery  (这个没找到什么吊意思,我看像是一个类似打logo的材质)

  3. linux Bash 常用

    linux 帮助文档 man + [命令] eg: man ls[命令] + --help eg:ls --helphelp +[命令] eg:help ceinfo + [命令] eg:info l ...

  4. 《深入分析Java Web技术内幕》读书笔记 - 第1章 深入Web请求过程

    第1章 深入Web请求过程 1 1.1 B/S网络架构概述 2 基于统一的应用层协议HTTP来交互数据. 1.2 如何发起一个请求 4 HTTP连接本质是建立Socket连接.请求实现方式:工具包如H ...

  5. Linux文件系统命令 umask

    命令:umask 功能:指定创建文件或者文件夹的时,默认的权限设置 用法:umask -S:以rwx的方式查看创建文件的权限 umask -p:以补位的方式查看文件的权限,显示的是补位的十进制. 另外 ...

  6. 20165326 java第一周学习笔记

    第一周学习笔记 一.理论视频学习 1.Java的特点:简单.面向对象.平台无关 2.Java的开发步骤&简单的应用程序: 文本编辑器写入代码 命名类名.java,文件类型所有文件,编码ANSI ...

  7. TensorFlow函数:tf.ones

    tf.ones 函数 ones( shape, dtype=tf.float32, name=None ) 定义于:tensorflow/python/ops/array_ops.py. 请参阅指南: ...

  8. Nginx 自动补全url地址补全最后的斜线

    参考地址: http://blog.csdn.net/dong123dddd/article/details/51660368 location /riskcontrol { root /data; ...

  9. English (一)

    Phrase        do somebody a favour give sb a hand do something for sb come to sb aid  帮助某人 what can ...

  10. Java虚拟机的组成

    Java虚拟机主要分为以下五个区: 一.方法区: 1. 有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载 2. 方法区主要用 ...