Python大数据处理库 PySpark实战二
Pyspark建立Spark RDD
- 每个RDD可以分成多个分区,每个分区可以看作是一个数据集片段,可以保存到Spark集群中的不同节点上
- RDD自身具有容错机制,且是一种只读的数据结构,只能通过转换生成新的RDD;一个RDD通过分区可以多台机器上并行处理;可将部分数据缓存在内存中,可多次重用;当内存不足时,可把数据落到磁盘上
- 创建RDD的方法
- parallelize(集合,分区数)
- range sc.range(1,10,2) 开始结束步长
- 使用HDFS建立RDD
pyspark shell
#pyspark shell
rdd = sc.parallelize(["hello world","hello spark"]);
rdd2 = rdd.flatMap(lambda line:line.split(" "));
rdd3 = rdd2.map(lambda word:(word,1));
rdd5 = rdd3.reduceByKey(lambda a, b : a + b);
rdd5.collect();
quit();
VScode
# vscode
#pip install findspark
#fix:ModuleNotFoundError: No module named 'pyspark'
import findspark
findspark.init()
#############################
from pyspark import SparkConf, SparkContext
# 创建SparkContext
conf = SparkConf().setAppName("WordCount").setMaster("local[*]")
sc = SparkContext(conf=conf)
rdd = sc.parallelize(["hello world","hello spark"]);
rdd2 = rdd.flatMap(lambda line:line.split(" "));
rdd3 = rdd2.map(lambda word:(word,1));
rdd5 = rdd3.reduceByKey(lambda a, b : a + b);
#print,否则无法显示结果
#[('spark', 1), ('hello', 2), ('world', 1)]
print(rdd5.collect());
#防止多次创建SparkContexts
sc.stop()
Jupyter notebook
#jupyter
from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local[*]").appName("WordCount").getOrCreate();
sc = spark.sparkContext
rdd = sc.parallelize(["hello world","hello spark"]);
rdd2 = rdd.flatMap(lambda line:line.split(" "));
rdd3 = rdd2.map(lambda word:(word,1));
rdd5 = rdd3.reduceByKey(lambda a, b : a + b);
#print,否则无法显示结果
#[('spark', 1), ('hello', 2), ('world', 1)]
print(rdd5.collect());
#防止多次创建SparkContexts
sc.stop()
动作算子
collect 把RDD类型数据转化为数组 同时从集群中拉取数据dirver端
stats 返回RDD元素的计数、均值、方差、最大值和最小值
countByKey 统计RDD[K,V]中每个K的数量 每个相同的K 结果加一 不是把V的值相加
-
first: 返回RDD中一个元素
-
max: 返回最大的一个元素
-
sum: 返回和
-
take: 返回前n个元素
-
top: 返回排序后的前n个元素 降序 top(10,key=str):按照字典序排序 前10个
-
count: 返回个数
-
collect :把RDD类型数据转化为数组 同时从集群中拉取数据dirver端
-
collectAsMap: 把键值RDD转换成Map映射保留其键值结构
-
countByKey: 统计RDD[K,V]中每个K的数量 每个相同的K 结果加一 不是把V的值相加
-
countByValue :统计一个RDD中各个Value出现的次数,返回字典,key是元素的值,value是出现的次数/
sc.parallelize(range(2,100)) 等价于 sc.range(2,100) rdd3 = sc.parallelize([("a",1),("a",1),("b",2),("a",1)]) print(rdd3.countByKey()) #defaultdict(<class 'int'>, {'a': 3, 'b': 1}) print(rdd3.countByValue()) #defaultdict(<class 'int'>, {('a', 1): 3, ('b', 2): 1})
-
stats:返回RDD元素的计数、均值、方差、最大值和最小值
rdd = sc.parallelize(range(100)) print(rdd.stats()) #(count: 100, mean: 49.5, stdev: 28.86607004772212, max: 99, min: 0)
-
aggregate : aggregate(zeroValue,seqOp,combOp) 使用seqOP函数和给定的zeroValue聚合每个分区上的元素,然后用CombOp和zeroValue聚合所有分区结果
data=[1,3,5,7,9,11,13,15,17] rdd=sc.parallelize(data,2) print(rdd.glom().collect()) # [[1, 3, 5, 7], [9, 11, 13, 15, 17]] seqOp = (lambda x, y: (x[0] + y, x[1] + 1)) #求和 和 个数 combOp = (lambda x, y: (x[0] + y[0], x[1] + y[1])) a=rdd.aggregate((0,0),seqOp,combOp) #(81, 9)=(0,0)+(16,4)+(65,5)
变换算子
coalesce 重新分区
filter 过滤
map 每个元素转换
flatmap 每个元素转换并扁平化
mapPartitions 按分区转换
mapValues KV格式 保留k 对v操作
reduce 减少个数 ; reducebykey KV格式 对v操作 减少元素个数
join 内链接; fullOuterJoin 全外部连接
groupBy 函数的返回作为k 分组 ;groupByKey KV中的K分组
keys 、values获取 对应序列
zip 元素相同 一一对应
union 合并; substract 减法 ; intersection 交集; certesian交集
cache、persist 缓存
glom 查看分区状态
sortBy:对RDD元素进行排序
-
coalesce:rdd.coalesce(numPartitions,[isShuffle=False]) 将RDD进行重新分区,分区过程中是否进行混洗操作
rdd=sc.parallelize([1, 2, 3, 4, 5], 3).glom() #[[1], [2, 3], [4, 5]] rdd2 = sc.parallelize([1, 2, 3, 4, 5, 6], 3).coalesce(1,False) #[1, 2, 3, 4, 5, 6]
-
repartition: 和coalesce(1,True) 一样 重新分区并混洗
-
distinct :去重
-
filter:返回满足过滤函数为True的元素构成 filter(lambda x: x%2 == 0)
#filter rdd5 = sc.parallelize([1,2,3,4,5]).filter(lambda x: x%2 == 0) print(rdd5.collect()) [2,4]
-
map:对RDD每个元素按照func定义的逻辑处理,在统计单词个数中常用rdd.map(func,preservesPartitioning=Flase)
rdd = sc.parallelize(["b", "a", "c", "d"]) rdd2 = rdd.map(lambda x: (x, 1)) #[('b', 1), ('a', 1), ('c', 1), ('d', 1)]
-
flatMap:对RDD中每一个元素按照func的处理逻辑操作,并将结果扁平化处理
#faltMap rdd5 = sc.parallelize([1,2,3,4,5]).flatMap(lambda x:[(x,1)]) print(rdd5.collect()) [(1, 2), (2, 4), (3, 6), (4, 8), (5, 10)]
-
flatMapValues:对RDD元素格式为KV对中的Value进行func定义的逻辑处理,形成新的KV,并把结果扁平化处理
#flatMapValues rdd = sc.parallelize([("a", [1, 2, 3]), ("c", ["w", "m"])]) ret = rdd.flatMapValues(lambda x: x) #[('a', 1), ('a', 2), ('a', 3), ('c', 'w'), ('c', 'm')]
-
mapPartitions:RDD每个分区中元素按照定义的逻辑返回处理,并分别返回值
rdd = sc.parallelize([1, 2, 3, 4 , 5], 2) def f(iter): yield sum(iter) #yield的作用是把函数变成generator,返回的是iterable对象 rdd2 = rdd.mapPartitions(f) print(rdd2.collect()) #[3,12]
-
mapValues:对KV格式的RDD中的每个元素应用函数,K值不变且保留原始分区, 对Value操作
rdd = sc.parallelize([("a", ["hello", "spark", "!"]), ("b", ["cumt"])]) rdd2 = rdd.mapValues(lambda x:len(x)) #[('a', 3), ('b', 1)]
-
mapPartitionsWithIndex:RDD每个分区中元素按照定义的逻辑返回处理,跟踪原始分区的索引
rdd = sc.parallelize([1, 2, 3, 4 ,5 ,6], 3) def f(index, iter): #分区索引 0,1,2 print(index) for x in iter: #1,2;3,4;5,6 print(x) yield index ret = rdd.mapPartitionsWithIndex(f).sum() #3=0+1+2 print(ret)
-
reduce : 按照func对RDD元素计算,减少元素个数
rdd = sc.parallelize([1, 2, 3, 4, 5]) ret = rdd.reduce(lambda x,y : x+y) 15
-
reduceByKey : 对KV的数据进行运算,减少元素个数
rdd = sc.parallelize([("a", 1), ("b", 1), ("a", 2),("b", 3)]) rdd2 = rdd.reduceByKey(lambda x,y:x+y) #[('a', 3), ('b', 4)]
-
join: 包含自身和另一个匹配键的所有成对元素,每对元素以(k,(v1,v2))元组返回,其中(k,v1)在自身,(k,v2)在另一个中
x = sc.parallelize([("a", 1), ("b", 4)]) y = sc.parallelize([("a", 2), ("a", 3)]) ret = x.join(y).collect() #[('a', (1, 2)), ('a', (1, 3))]
-
fullOuterJoin : 全外部连接 没有匹配到就是None
x = sc.parallelize([("a", 1), ("b", 4)]) y = sc.parallelize([("a", 2), ("c", 8)]) rdd = x.fullOuterJoin(y) # [('a', (1, 2)), ('b', (4, None)), ('c', (None, 8))]
-
leftOuterJoin 和 rightOuterJoin : 左外连接 和 右外连接
x = sc.parallelize([("a", 1), ("b", 4)]) y = sc.parallelize([("a", 2), ("c", 8)]) rdd = x.leftOuterJoin(y) #[('b', (4, None)), ('a', (1, 2))] rdd = x.rightOuterJoin(y) #[('c', (None, 8)), ('a', (1, 2))]
-
groupBy :groupBy(func,numPartitions=None,partitionFunc=<function portable_hash) 函数的返回作为key,通过key对其元素进行分组,返回新的RDD
-
rdd = sc.parallelize([1, 2, 3, 4, 5, 10]) rdd = rdd.groupBy(lambda x:x%2) result = rdd.collect() #[(0, <pyspark.resultiterable.ResultIterable object at 0x110ef9c50>), (1, <pyspark.resultiterable.ResultIterable object at 0x110ef94d0>)] ret = sorted([(x, sorted(y)) for (x, y) in result]) #[(0, [2, 4, 10]), (1, [1, 3, 5])]
-
groupByKey : 将RDD中每个键的值分组为单个序列,用numsPartitions分区对生成的RDD进行哈希分区 如果求和或平均值 建议使用reduceByKey 或 AggregateByKey
rdd = sc.parallelize([("a", 1), ("b", 1), ("a", 1)]) rdd2 = rdd.groupByKey().mapValues(lambda x: sum(x)) rdd3 = rdd.reduceByKey(lambda x,y: x+y) #和rdd2一样 # [('a', 2), ('b', 1)] print(sorted(rdd2.collect()))
-
keyBy: 将原有RDD中的元素作为Key,Key通过func返回值作为value创建一个元组
rdd = sc.parallelize(range(0,3)) rdd = rdd.keyBy(lambda x: x*x) #[(0, 0), (1, 1), (4, 2)]
-
keys:获取KV格式中的Key序列,返回新的RDD
rdd1 = sc.parallelize([("a",1),("b",2),("a",3)]) print(rdd1.keys().collect()) #['a', 'b', 'a']
-
values:获取KV格式中的Value序列,返回新的RDD
rdd1 = sc.parallelize([("a",1),("b",2),("a",3)]) print(rdd1.keys().collect()) #[1, 2, 3]
-
zip:rdd.zip(otherRDD)将第一个RDD中的元素作为Key,第二个RDD中的作为Value组成新的RDD,两个RDD的元素个数相同
x = sc.parallelize(range(1,6)) y = sc.parallelize(range(801, 806)) print(x.zip(y).collect()) #[(1, 801), (2, 802), (3, 803), (4, 804), (5, 805)] #x,y长度必须相等
-
zipWithIndex:RDD元素作为key,索引作为Value
rdd = sc.parallelize(["a", "b", "c", "d"], 3) print(rdd.zipWithIndex().collect()) #[('a', 0), ('b', 1), ('c', 2), ('d', 3)]
-
union:第一个RDD元素和第二个的合并
dd =sc.parallelize(range(1,10)) rdd2 =sc.parallelize(range(11,20)) rdd3 = rdd.union(rdd2) #[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19]
-
subtract:第一个中排出第二个中的元素
x = sc.parallelize([("a", 1), ("b", 4), ("b", 5), ("a", 3)]) y = sc.parallelize([("a", 1), ("b", 5)]) z = x.subtract(y) #[('b', 4), ('a', 3)]
-
subtractByKey :从元素为KV格式的RDD中除掉另一个,只要Key一样就删除
x = sc.parallelize([("a", 1), ("b", 4), ("c", 5), ("a", 3)]) y = sc.parallelize([("a", 7), ("b", 0)]) z = x.subtractByKey(y) #[('c', 5)]
-
intersection:返回交集并去重
rdd1 = sc.parallelize([("a", 2), ("b", 1), ("a", 2),("b", 3)]) rdd2 = sc.parallelize([("a", 2), ("b", 1), ("e", 5)]) ret = rdd1.intersection(rdd2).collect() #('a', 2), ('b', 1)]
-
certesian: 返回两个RDD的笛卡尔积 元素较多可能出现内存不足情况
rdd = sc.parallelize([1, 2]) rdd2 = sc.parallelize([3, 7]) rdd3 = sorted(rdd.cartesian(rdd2).collect()) #[(1, 3), (1, 7), (2, 3), (2, 7)] print(rdd3)
-
sortBy:对RDD元素进行排序,sortBy(keyfuc,ascending=True,numPartitions=None),默认升序
rdd = [('a', 6), ('f', 11), ('c', 7), ('d', 4), ('e', 5)] rdd2 = sc.parallelize(rdd).sortBy(lambda x: x[0]) #[('a', 6), ('c', 7), ('d', 4), ('e', 5), ('f', 2)] rdd3 = sc.parallelize(rdd).sortBy(lambda x: x[1]) #[('f', 2), ('d', 4), ('e', 5), ('a', 6), ('c', 7)] rdd3 = sc.parallelize(rdd).sortBy(lambda x: x[1],False) #[('c', 7), ('a', 6), ('e', 5), ('d', 4), ('f', 2)]
-
sortByKey : 按照Key排序 sortByKey(ascending=True,numPartitions=None,keyfunc=)
x = [('a', 6), ('f', 2), ('c', 7), ('d', 4), ('e', 5)] rdd = sc.parallelize(x).sortByKey(True, 1) #[('a', 6), ('c', 7), ('d', 4), ('e', 5), ('f', 2)] print(rdd.collect())
-
takeOrdered:RDD中获取排序后的前num个元素构成RDD,默认升序,可支持可选函数
rdd =sc.parallelize(range(2,100)) print(rdd.takeOrdered(10)) #[2, 3, 4, 5, 6, 7, 8, 9, 10, 11] print(rdd.takeOrdered(10, key=lambda x: -x)) #[99, 98, 97, 96, 95, 94, 93, 92, 91, 90]
-
takeSample:takeSample(withReplacement,num,seed=None) 抽样出固定大小的子数据集合,第一个参数布尔值表示是否可以多次抽样,第二个抽样的个数,第三个随机数生成器种子
dd =sc.parallelize(range(2,10)) print(rdd.takeSample(True, 20, 1)) #True代表一个元素可以出现多次 #[5, 9, 5, 3, 2, 2, 7, 7, 5, 7, 9, 9, 5, 3, 2, 4, 5, 5, 6, 8] print(rdd.takeSample(False, 20, 1)) #False代表一个元素只能出现1次 #[5, 8, 3, 7, 9, 2, 6, 4]
-
sample : sample(withReplacement,fraction,seed) 第二个参数 抽样比例[0,1]
rdd = sc.parallelize(range(100), 1) ret = rdd.sample(False, 2, 1) #可能输出[9, 11, 13, 39, 49, 55, 61, 65, 90, 91, 93, 94]
-
randomSplit:按照权重对RDD随机切分,返回多个RDD构成的列表
rdd = sc.parallelize(range(100), 1) rdd1, rdd2 = rdd.randomSplit([2, 3], 10) print(len(rdd1.collect())) #40 print(len(rdd2.collect())) #60
-
loopup: 根据key值从RDD中找到相关的元素,返回KV中的V
rdd = sc.parallelize([('a', 'b'), ('c', 'd')]) print(rdd.lookup('a')) #['b']
-
fold:对RDD每个元素按照func的逻辑进行处理fold(value,func) func有两个参数a,b a的初始值为value,后续为累加值,b代表当前元素值 可以用来累加 累乘
#fold ret=sc.parallelize([1, 2, 3, 4, 5]).fold(0, lambda x,y:x+y) #15 ret=sc.parallelize([1, 2, 3, 4, 5]).fold(1, lambda x,y:x*y) #120
-
foldByKey:对RDD元素格式为KV对中的Key进行func定义的逻辑处理,可以用来分组累加累乘
#foldByKey rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3),("b", 5)]) rdd2=rdd.foldByKey(0, lambda x,y:x+y) # [('a', 4), ('b', 7)] rdd3=rdd.foldByKey(1, lambda x,y:x*y) # [('a', 3), ('b', 10)]
-
foreach:对RDD每个元素按照func定义的逻辑处理
-
foreachPartion:对RDD每个分区中的元素按照func定义逻辑处理,一般来说foreachPartion效率比foreach高,是一次性处理一个partition数据,在写数据库的时候,性能比map高很多
rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3),("b", 5)]) def f(x): print(x) return (x[0],x[1]*2) def f2(iter): for x in iter: print(x) ret = rdd.foreach(f) ret2 = sc.parallelize([1,2,3,4,5,6,7,8],2).foreachPartition(f2)
-
aggregateByKey:aggregate(zeroValue,seqFunc,combFunc,numPartitions=None,partitionFunc=) 使用seqFunc函数和给定的zeroValue聚合每个分区上的元素,然后用CombFunc和zeroValue聚合所有分区结果
data=[("a",1),("b",2),("a",3),("b",4),("a",5),("b",6),("a",7),("b",8),("a",9),("b",10)] rdd=sc.parallelize(data,2) print(rdd.glom().collect()) #[[('a', 1), ('b', 2), ('a', 3), ('b', 4), ('a', 5)], [('b', 6), ('a', 7), ('b', 8), ('a', 9), ('b', 10)]] def seqFunc(x,y): return x + y def combFunc(x,y): return x + y a=rdd.aggregateByKey(0,seqFunc,combFunc) # [('b', 30), ('a', 25)] print(a.collect())
-
combineByKey:
- createCombiner: V => C 这个函数把当前的值作为参数 可以对其做一些操作并返回
- mergeValue :(C,V) => C 把元素V合并到之前的元素C上 (这个操作在每个分区内进行)
- mergeCombiners:(C,C) => C 把2个元素合并 (这个操作在不同分区间进行)
a = [1,2] b = [10,11] a.extend(b) #[1, 2, 10, 11] a.append(b) #[1, 2, [10, 11]] #combineByKey rdd = sc.parallelize([("a", 1), ("b", 3), ("a", 2),("b", 4)],2) def to_list(a): return [a] def append(a, b): #分区合并 a.append(b) return a def extend(a, b):#不同分区合并 a.extend(b) return a print(rdd.glom().collect()) ret = sorted(rdd.combineByKey(to_list, append, extend).collect()) #[[('a', 1), ('b', 3)], [('a', 2), ('b', 4)]] #[('a', [1, 2]), ('b', [3, 4])]
-
glom:把RDD中每一个分区的元素T转换成Array[T],每个分区只有一个数组元素
#glom rdd2 = sc.parallelize([1,2,3,4,5],3) print(rdd2.collect()) #[1, 2, 3, 4, 5] print(rdd2.glom().collect()) #[[1], [2, 3], [4, 5]] print(rdd2.coalesce(1).glom().collect()) #[[1, 2, 3, 4, 5]]
-
cache : 缓存 默认存储级别(MEMORY_ONLY)
-
persist : 缓存 可以定制存储级别 storageLevel
-
saveAsTextFile: 保存RDD文件作为一个对象,