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. 使用MYSQL数据库实现编程----第二章第三章课堂知识小总结

    第二章1:创建数据库create database myschool 2.数据类型  1.整型 int  2.小数 double  精确度要求高的 ----Decimal(18,4)  2222222 ...

  2. node(3)Buffer缓冲区

    buffer 专门用来存放二进制数据的缓冲区:处理文件流 TCP流 const buf = Buffer.from('runoob', 'ascii'); // 创建一个长度为 10.且用 0x1 填 ...

  3. error: illegal character '\ufeff' 的解决方案

    今天使用scalac 命令编译scala文件的时候,出错了,出现如下错误提示:

  4. shell脚本实例-实现监控tcp的链接状态另一种方式批量创建用户

    Array实现TCP的链接状态 #!/usr/bin/bash declare -A status type=`ss -an | grep :80|awk '{print $2}'` for i in ...

  5. django面试题必问

    1.谈谈你对http协议的认识. HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议.它可以使浏览器更加高效,使 ...

  6. python-web-django前后端交互

    1.前端请求数据URL由谁来写 在开发中,URL主要是由后台来写好给前端. 若后台在查询数据,需要借助查询条件才能查询到前端需要的数据时,这时后台会要求前端提供相关的查询参数(即URL请求的参数). ...

  7. Python Algorithms – chapter3 计数初步

    一些基本递归式的解决方案及其应用实例 主定理的三种情况 排序算法之侏儒排序法 def gnomesort(seq): i = 0 while i < len(seq): if i == 0 or ...

  8. 高级数据结构及应用 —— 使用 bitmap 进行字符串去重

    bitmap 即为由单个元素为 boolean(0/1, 0 表示未出现,1 表示已经出现过)的数组. 如果C/C++ 没有原生的 boolean 类型,可以用 int 或 char 来作为 bitm ...

  9. C 语言经典面试题 —— 宏

    1. 运算符优先级与括号 #define Cube(a) a*a*a 无法解决 Cube(1+1) ⇒ 1+1*1+1*1+1 ⇒ 4,期待的应当是 8,故将其改造为 #define Cube(a) ...

  10. C++中vector容器的常用操作方法实例总结

    C++中vector容器的常用操作方法实例总结 参考 1. C++中vector容器的常用操作方法实例总结: 完