【博学谷学习记录】超强总结,用心分享|Spark-RDD的各类算子(一)
一、RDD算子分类
RDD算子: 指的是RDD对象中提供了非常多的具有特殊功能的函数,我们一般将这样的函数称为算子(大白话: 指的RDD的API)。
整个RDD的算子,共分为二大类:Transformation(转换算子) 和 Action(动作算子)
1.转换算子特性
(1)所有的转换算子在执行完成后,都会返回一个新的RDD;
(2)所有的转换算子都是LAZY(惰性),并不会立即执行,此时可以认为通过转换算子定义RDD的计算规则;
(3)转换算子必须遇到Action算子才会触发执行。
2.动作算子特性
(1)动作算子在执行后,不会返回一个RDD,要不然没有返回值,要不就返回其他的;
(2)动作算子都是立即执行,一个动作算子就会产生一个Job执行任务,运行这个动作算子所依赖的所有的RDD。
3.部分转换算子如图
4.部分动作算子如图
二、RDD的转换算子
(1)值类型的算子:map()
- 格式: rdd.map(fn)
- 说明: 根据传入的函数,对数据进行一对一的转换操作,传入一行,返回一行。
rdd = sc.parallelize([1,2,3,4,5,6,7,8,9,10])
需求: 请对每一个元素进行 +1 返回
def fn1(e):
return e + 1
rdd.map(fn1).collect()
结果为:
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
当一个函数中只有一行代码的时候, 可以将其改写为lambda的匿名函数
rdd.map(lambda e: e + 1).collect()
结果为:
[2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
(2)值类型的算子:groupBy()
- 格式: groupBy(fn)
- 说明: 根据传入的函数对数据进行分组操作
rdd = sc.parallelize([1,2,3,4,5,6,7,8,9,10])
需求: 请将数据分为奇数和偶数二部分
划分奇数和偶数: 对2取余, 余数为 0 即为偶数 余数为1 即为奇数
def fn1(e):
if e % 2 == 0:
return 'o'
else:
return 'j'
rdd.groupBy(fn1).collect()
结果:
[
('j', <pyspark.resultiterable.ResultIterable object at 0x7f09acefba30>),
('o', <pyspark.resultiterable.ResultIterable object at 0x7f09adadb910>)
]
mapValues(list): 将 kv中value转换为list类型
rdd.groupBy(fn1).mapValues(list).collect()
结果:
[
('j', [1, 3, 5, 7, 9]),
('o', [2, 4, 6, 8, 10])
]
思考: 上述编写 fn1函数, 请问是否可以写成lambda, 如果可以, 这个lambda应该如何编写呢?
rdd.groupBy(lambda e: 'o' if e % 2 == 0 else 'j' ).mapValues(list).collect()
[
('j', [1, 3, 5, 7, 9]),
('o', [2, 4, 6, 8, 10])
]
(3)值类型的算子:filter()
- 格式: filter(fn)
- 说明: 过滤算子, 可以根据函数中指定的过滤条件, 对数据进行过滤操作, 条件返回True表示保留, 返回False 表示过滤掉
rdd = sc.parallelize([1,2,3,4,5,6,7,8,9,10])
需求: 请将 <=3的数据过滤掉
rdd.filter(lambda num: num > 3).collect()
结果:
[4, 5, 6, 7, 8, 9, 10]
(4)值类型的算子:flatMap()
- 格式: flatMap(fn)
- 说明: 在map算子的基础上, 在加入一个压扁的操作, 主要适用于一行中包含多个内容的操作, 实现一转多的操作
rdd = sc.parallelize(['张三|李四|王五|赵六','田七|周八|李九'])
需求: 将数据转换为一个个的姓名
rdd.flatMap(lambda line: line.split('|')).collect()
结果:
['张三', '李四', '王五', '赵六', '田七', '周八', '李九']
(5)双值类型的算子:union(并集) 和 intersection(交集)
- 格式: rdd1.union|intersection(rdd2)
rdd1 = sc.parallelize([1,4,5,2,8,6])
rdd2 = sc.parallelize([2,5,6,1,9,8])
结果:
并集:
rdd1.union(rdd2).collect()
结果:
[1, 4, 5, 2, 8, 6, 2, 5, 6, 1, 9, 8]
去重操作:
rdd1.union(rdd2).distinct().collect()
结果
[4, 8, 1, 5, 9, 2, 6]
交集:
rdd1.intersection(rdd2).collect()
结果:
[8, 1, 5, 2, 6]
(6)kv类型相关的算子:groupByKey()
- 格式: groupByKey()
- 说明: 根据key进行分组操作
rdd = sc.parallelize([('c01','张三'),('c02','李四'),('c02','王五'),('c01','赵六'),('c01','田七'),('c02','周八'),('c03','李九')])
需求: 根据班级分组统计
rdd.groupByKey().collect()
结果:
[
('c01', <pyspark.resultiterable.ResultIterable object at 0x7f09aced8b80>),
('c02', <pyspark.resultiterable.ResultIterable object at 0x7f09ace7f4f0>),
('c03', <pyspark.resultiterable.ResultIterable object at 0x7f09ace7f580>)
]
rdd.groupByKey().mapValues(list).collect()
结果:
[
('c01', ['张三', '赵六', '田七']),
('c02', ['李四', '王五', '周八']),
('c03', ['李九'])
]
统计每组内有多少个数据呢?
rdd.groupByKey().mapValues(list).map(lambda kv: (kv[0],len(kv[1]))).collect()
结果:
[('c01', 3), ('c02', 3), ('c03', 1)]
(7)reduceByKey()
- 格式: reduceByKey(fn)
- 说明: 根据key进行分组, 将一个组内的value数据放置到一个列表中, 对这个列表基于传入函数进行聚合计算操作
rdd = sc.parallelize([('c01','张三'),('c02','李四'),('c02','王五'),('c01','赵六'),('c01','田七'),('c02','周八'),('c03','李九')])
需求: 统计每个班级有多少个人
rdd.map(lambda kv: (kv[0],1)).reduceByKey(lambda agg,curr: agg + curr).collect()
[('c01', 3), ('c02', 3), ('c03', 1)]
如果不转换, 请问会出现什么结果呢?
('c01','张三') ('c01','赵六'),('c01','田七')
rdd.reduceByKey(lambda agg,curr: agg + curr).collect()
结果:
[('c01','张三赵六田七'),('c02','李四王五周八'),('c03','李九')]
(8)sortByKey()
- 格式: sortByKey(ascending=True|False 默认为True)
- 说明: 根据key进行排序操作, 默认按照key进行升序排序, 如果需要倒序 设置ascending为False
rdd = sc.parallelize([('c05','张三'),('c02','李四'),('c07','王五'),('c01','赵六'),('c03','田七'),('c08','周八'),('c04','李九')])
根据班级的序号进行排序操作
rdd.sortByKey().collect()
结果:
[('c01', '赵六'), ('c02', '李四'), ('c03', '田七'), ('c04', '李九'), ('c05', '张三'), ('c07', '王五'), ('c08', '周八')]
倒序排列:
rdd.sortByKey(ascending=False).collect()
结果:
[('c08', '周八'), ('c07', '王五'), ('c05', '张三'), ('c04', '李九'), ('c03', '田七'), ('c02', '李四'), ('c01', '赵六')]
rdd = sc.parallelize([('c2','张三'),('c11','李四'),('c21','王五'),('c3','赵六'),('c1','田七'),('c08','周八'),('c04','李九')])
rdd.sortByKey(ascending=False).collect()
结果: 字典序, 因为 key是字符串
[('c3', '赵六'), ('c21', '王五'), ('c2', '张三'), ('c11', '李四'), ('c1', '田七'), ('c08', '周八'), ('c04', '李九')]