Spark算子(一)之基本RDD操作
参考书籍《大数据技术与应用》肖政宏 李俊杰 谢志明 编著
Spark算子大致分为转化(Transformation)和行动(Action)两类。
1)Transformation:不触发提交作业,完成作业中间过程处理。
函数包括:map、filter、flatMap、groupByKey、aggregateByKey、pipe和coalesce等。
2)Action:会触发SparkContext提交作业(job)。
函数包括:reduce、collect、count、first、take、countByKey和foreach等。
以下用python进行编写
文章目录
- Spark算子(一)之基本RDD操作
- 1、基本RDD的转化操作
- 1)`map(func)`:
- 2)`flatMap(func)`:
- 3)`filter(func)`:
- 4)`mapPartitions(func)`:
- 5)`mapPartitionsWithIndex(func)`:
- 6)`distinct([numTasks])`:
- 7)`sample(withReplacement,fraction,seed)`:
- 8)`cartesian(otherDataset)`:
- 9)`union(otherDataset)`:
- 10)`intersection(otherDataset)`:
- 11)`subtract(otherDataset)`:
- 2、基本RDD的行动操作
- 1)`collect()`:
- 2)`count()`:
- 3)`countByValue()`:
- 4)`take(num)`:
- 5)`top(num)`:
- 6)`takeOrdered(num)`:
- 7)`takeSample(withReplacement,num,seed)`:
- 8)`reduce(func)`:
- 9)`fold(zero,func)`:
- 10)`aggregate(zeroValue,seqOp,combOp)`:
- 11)`foreach(func)`:
1、基本RDD的转化操作
1)map(func)
:
将func函数作用到RDD的每个元素,生成一个新的RDD返回。
计算rdd1中个元素值的平方。
>>> rdd1 = sc.parallelize([1,2,3,4,5])
>>> rdd2 = rdd1.map(lambda x:x*x).collect()
>>> print(rdd2)
[1, 4, 9, 16, 25]
2)flatMap(func)
:
与map相似,但是每个输入的item能够被map到0个或者更多的items输出。
将rdd1中元素数据切分为单词
>>> lst = ['i am one','he is two','she is three']
>>> rdd1 = sc.parallelize(lst,3)
>>> rdd2 = rdd1.flatMap(lambda x:x.split()).collect()
>>> print(rdd2)
['i', 'am', 'one', 'he', 'is', 'two', 'she', 'is', 'three']
3)filter(func)
:
选出所有func返回值为true的元素,作为一个新的RDD返回。
过滤rdd1中的值不是2的元素
>>> rdd1 = sc.parallelize([1,2,3,4,5,6])
>>> rdd2 = rdd1.filter(lambda x:x!=2).collect()
>>> print(rdd2)
[1, 3, 4, 5, 6]
4)mapPartitions(func)
:
与map相似,但是mapPartition的输入函数单独作用于RDD的每个分区(block)上,因此func的输入和返回值都必须是迭代器iterator。
假设rdd1有0~9共10个元素,分成3个区,使用mapPartitions返回每个元素的平方。如果使用map方法,map中的输入函数会被调用10次;而使用mapPartitions方法,输入函数只会被调用3次,每个分区被调用一次。
>>> def Func(a):
... for i in a:
... yield i * i
...
>>> rdd1 = sc.parallelize(range(10),3)
>>> rdd2 = rdd1.mapPartitions(Func).collect()
>>> print(rdd2)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
5)mapPartitionsWithIndex(func)
:
与mapPartitions相似,但是输入函数func提供了一个正式的参数,可以用来表示分区的编号。
对rdd1的分区进行求和
>>> def func(index,iterator):
... yield(index,sum(iterator))
...
>>> rdd1 = sc.parallelize(range(10),3)
>>> rdd2 = rdd1.mapPartitionsWithIndex(func)
>>> print(rdd1.collect())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(rdd2.collect())
[(0, 3), (1, 12), (2, 30)]
>>> print(rdd1.glom().collect())
[[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]
>>> print(rdd2.glom().collect())
[[(0, 3)], [(1, 12)], [(2, 30)]]
sc的glom()
方法的作用是将同一个分区里的元素合并到一个array里
6)distinct([numTasks])
:
删除RDD中相同的元素
删除rdd1中相同的元素
>>> rdd1 = sc.parallelize([1,1,2,5,2,9,6,1])
>>> rdd2 = rdd1.distinct().collect()
>>> print(rdd2)
[1, 2, 5, 6, 9]
7)sample(withReplacement,fraction,seed)
:
以指定的种子有放回或者无放回随机抽样。
withReplaceement:true为有放回的抽样,false为无放回
fraction:
当withReplaceement = true时,fraction为大于等于0的数,表示选择每个元素的期望次数;
当withReplaceement = false时,fraction为选择每个元素的概率,范围为[0,1]
seed:随机种子
>>> rdd1 = sc.parallelize(range(20))
>>> rdd2 = rdd1.sample(False,0.5)
>>> print(rdd2.collect())
[1, 2, 5, 6, 9, 14, 16, 19]
8)cartesian(otherDataset)
:
对两个RDD内的所有元素进行笛卡尔积操作。
>>> rdd1 = sc.parallelize([('a',1),('a',2)])
>>> rdd2 = sc.parallelize([('c',3),('d',4)])
>>> rdd3 = rdd1.cartesian(rdd2).collect()
>>> print(rdd3)
[(('a', 1), ('c', 3)), (('a', 1), ('d', 4)), (('a', 2), ('c', 3)), (('a', 2), ('d', 4))]
9)union(otherDataset)
:
合并两个RDD元素,两个RDD元素的数据类型要相同。
合并rdd1和rdd2。
>>> rdd1 = sc.parallelize([('a',1),('a',2)])
>>> rdd2 = sc.parallelize([('c',3),('d',4)])
>>> rdd3 = rdd1.union(rdd2).collect()
>>> print(rdd3)
[('a', 1), ('a', 2), ('c', 3), ('d', 4)]
10)intersection(otherDataset)
:
对两个RDD内的所有元素提取交集。
计算rdd1和rdd2的交集
>>> rdd1 = sc.parallelize(range(10))
>>> rdd2 = sc.parallelize(range(6,15))
>>> rdd3 = rdd1.intersection(rdd2).collect()
>>> print(rdd1.collect())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> print(rdd2.collect())
[6, 7, 8, 9, 10, 11, 12, 13, 14]
>>> print(rdd3)
[6, 8, 7, 9]
11)subtract(otherDataset)
:
进行集合的差操作。
rdd1去除rdd1和rdd2交集中的所有元素。
>>> rdd1 = sc.parallelize(range(10))
>>> rdd2 = sc.parallelize(range(6,15))
>>> rdd3 = rdd1.subtract(rdd2).collect()
>>> print(rdd3)
[0, 2, 4, 1, 3, 5]
2、基本RDD的行动操作
1)collect()
:
返回RDD中的所有元素。
显示rdd1中的所有元素。
>>> rdd1 = sc.parallelize(range(10))
>>> print(rdd1.collect())
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2)count()
:
返回RDD中的元素个数。
计算rdd1中的元素个数。
>>> rdd1 = sc.parallelize(range(10))
>>> print(rdd1.count())
10
3)countByValue()
:
返回RDD中的各元素出现的次数。
计算rdd1中各元素出现的次数
>>> rdd1 = sc.parallelize([1,2,2,3,'a','a'])
>>> print(rdd1.countByValue())
defaultdict(<type 'int'>, {'a': 2, 1: 1, 2: 2, 3: 1})
4)take(num)
:
返回RDD前num个元素。
取rdd1中前3个元素。
>>> rdd1 = sc.parallelize(range(10))
>>> print(rdd1.take(3))
[0, 1, 2]
5)top(num)
:
返回RDD最大的num个元素。
取rdd1中最大的3个元素。
>>> rdd1 = sc.parallelize(range(10))
>>> print(rdd1.top(3))
[9, 8, 7]
6)takeOrdered(num)
:
返回RDD中最小的num个元素。
取rdd1中最小的3个元素
>>> rdd1 = sc.parallelize(range(10))
>>> print(rdd1.takeOrdered(3))
[0, 1, 2]
7)takeSample(withReplacement,num,seed)
:
takesample函数类似于sample函数。
withReplacement:True表示有放回抽样,False表示无放回。
num:表示返回的采样数据的个数。
seed:表示用于指定的随机数生成器种子。
从rdd1中随机且有放回地抽出10个数据,随机种子值为5。
>>> rdd1 = sc.parallelize(range(20))
>>> rdd2 = rdd1.takeSample(True,10,5)
>>> print(rdd2)
[18, 9, 17, 4, 5, 11, 11, 9, 4, 9]
8)reduce(func)
:
将RDD中元素两两传递给输入函数,同时产生一个新值;新产生的值与RDD中下一个元素再被传递给输入函数,直到最后只有一个值为止。
对rdd1中的元素求和。
>>> rdd1 = sc.parallelize(range(20))
>>> sum = rdd1.reduce(lambda x,y:x+y)
>>> print(sum)
190
9)fold(zero,func)
:
和reduce一样,但是需要提供初始值。
对rdd1中的元素加上初始值求和。
>>> rdd1 = sc.parallelize([1,2,3,4,5])
>>> def func(x,y):
... return x+y
>>> sum = rdd1.fold(1,func)
>>> print(sum)
17
#通过在func函数里加入
#... print(x,y,x+y)
#我们可以得到运算过程
#(1, 1, 2)
#(2, 2, 4)
#(4, 3, 7)
#(7, 4, 11)
#(11, 5, 16)
#(1, 16, 17)
#可以分析出分别在第一次和最后一次运算中加了初始值1
10)aggregate(zeroValue,seqOp,combOp)
:
zeroValue:初始值
seqOp:对每个分区进行操作
combOp:combine对每个分区的结果进行操作
>>> rdd1 = sc.parallelize([1,2,3,3],2)
>>> seqOp = lambda x,y:(x[0] + y,x[1] + 1)
>>> combOp = lambda x,y:(x[0] + y[0],x[1] + y[1])
>>> rdd2 = rdd1.aggregate((0,0),seqOp,combOp)
>>> print(rdd2)
(9, 4)
过程说明:
seqOp对第一个分区(同时并行的对第二个分区进行同样的操作)进行操作步骤是
开始先用初始值和第一个分区的第一个值,然后是结果和其余的值进行操作
0+1,0+1
1+2,1+1
结果是(3,2),同样的第二个分区的结果是(6,2)
combOp对两个分区的结果以及初始值本身进行操作
(3+9,2+2)
11)foreach(func)
:
对RDD中的每个元素使用给定的函数。
打印rdd1中的元素。
>>> def prn(x):print(x),;print(''),
>>> rdd1 = sc.parallelize([1,2,3,3])
>>> rdd1.foreach(prn)
1 2 3 3