Spark RDD 编程指导(Python API)

本文详细介绍了Apache Spark中的RDD(弹性分布式数据集)的各种操作,包括创建、转换和动作算子,如parallelize、reduce、glom、collect、textFile、map、filter、distinct等,并展示了如何持久化RDD、使用广播变量和累加器,以及通过外部数据集创建RDD的方法。通过实例演示了如何在实践中应用这些操作。
摘要由CSDN通过智能技术生成

通过并行集合创建RDD

parallelize和reduce

data = [1, 2, 3, 4, 5]
distData1 = sc.parallelize(data) # 此时distData1就是一个RDD
distData1.reduce(lambda a, b: a + b) # 将所有元素相加
# reduce(f)是对RDD中的元素通过函数f进行两两操作,产生的值作为新的元素再和RDD中下一个元素一起传递给函数f进行两两操作,循环往复直到所有元素完成操作,返回最后的值
15

glom和collect

distData2 = sc.parallelize(data, 3) # 做了分区
distData2.glom().collect() 
# glom()将每个分区的元素分别放在一个列表中,作为新的RDD返回(相当于查看分区内容)
# collect()返回一个包含RDD中所有元素的列表
[[1], [2, 3], [4, 5]]

通过外部数据集创建RDD

textFile和map

distData3 = sc.textFile('E:/spark-2.3.0-bin-hadoop2.7/data/streaming/AFINN-111.txt')
# disData3.collect() # 可以看出RDD中一个元素对应文本的一行
distData3.map(lambda s: len(s)).reduce(lambda a, b: a + b) # 将文本每行长度相加
# map(f)对RDD中的每个对象通过函数f进行映射,分别产生一个列表
25616
distData4 = sc.textFile('E:/spark-2.3.0-bin-hadoop2.7/data/graphx/users.txt', 4) # 默认minPartions=2,设置为4
distData4.glom().collect()
[['1,BarackObama,Barack Obama', '2,ladygaga,Goddess of Love'],
 ['3,jeresig,John Resig', '4,justinbieber,Justin Bieber'],
 ['6,matei_zaharia,Matei Zaharia'],
 ['7,odersky,Martin Odersky', '8,anonsys']]

wholeTextFiles

# wholeTextFiles(path)读取path中的所有文件,返回(key, value)对,key代表文件绝对路径,value代表文件内容
distData5 = sc.wholeTextFiles('E:/spark-2.3.0-bin-hadoop2.7/data/graphx/')
distData5.collect()
[('file:/E:/spark-2.3.0-bin-hadoop2.7/data/graphx/followers.txt',
  '2 1\n4 1\n1 2\n6 3\n7 3\n7 6\n6 7\n3 7\n'),
 ('file:/E:/spark-2.3.0-bin-hadoop2.7/data/graphx/users.txt',
  '1,BarackObama,Barack Obama\n2,ladygaga,Goddess of Love\n3,jeresig,John Resig\n4,justinbieber,Justin Bieber\n6,matei_zaharia,Matei Zaharia\n7,odersky,Martin Odersky\n8,anonsys\n')]

saveAsPickleFile

# saveAsPickleFile()将RDD保存为SequenceFile或序列化对象,默认批处理大小为10
distData4.saveAsPickleFile('pickle') # RDD有4个分区,所以同样pickle里面有4个序列化文件

pickleFile

# pickleFile()解序列化
sc.pickleFile('pickle').collect()
['1,BarackObama,Barack Obama',
 '2,ladygaga,Goddess of Love',
 '3,jeresig,John Resig',
 '4,justinbieber,Justin Bieber',
 '6,matei_zaharia,Matei Zaharia',
 '7,odersky,Martin Odersky',
 '8,anonsys']

saveAsSequenceFile

# saveAsSequenceFile()保存SequenceFile
sequencedata = [('jack', 100), ('Tom', 99), ('Jerry', 90)]
distData6 = sc.parallelize(sequencedata)
distData6.saveAsSequenceFile('sequence')

sequenceFile

# 加载SequenceFile
sc.sequenceFile('sequence').collect()
[('jack', 100), ('Tom', 99), ('Jerry', 90)]

RDD操作

转换算子-Value型

map和flatMap

# flatMap比map多一步,平坦化,所有对象合并为一个列表
rdd1 = sc.parallelize([2, 3, 4])
rdd2 = rdd1.map(lambda x: range(1, x))
rdd3 = rdd1.flatMap(lambda x: range(1, x)) 
print(rdd2.collect())
print(rdd3.collect())
[range(1, 2), range(1, 3), range(1, 4)]
[1, 1, 2, 1, 2, 3]

mapPartitions

# mapPartitions一次获取到一个分区所有元素后再进行操作,速度比map快,但是内存占用大,容易溢出
rdd = sc.parallelize([9, 3, 5, 7], 2) # 分成2个区
def f(iterator):
    yield max(iterator)
rdd.mapPartitions(f).collect() # 取各分区最大值
[9, 7]

mapPartitionsWithIndex

# mapPartitionsWithIndex可以只针对特定分区进行操作
rdd = sc.parallelize([10, 3, 8, 6], 2)
def f(index, iterator):
    if (index == 0):
        pass
    else:
        yield list(iterator)
rdd.mapPartitionsWithIndex(f).collect() # 只包含第二个分区中的元素
[[8, 6]]

filter

# filter过滤“出”某些元素
# 过滤出偶数
sc.parallelize([1, 2, 3, 4, 5]) \
    .filter(lambda x: x%2==0) \
    .collect() 
[2, 4]

distinct

# distinct去重
sc.parallelize([1, 2, 2, 3, 3, 4, 5]) \
    .distinct() \
    .collect()
[1, 2, 3, 4, 5]

union

# union合并两个rdd的元素
rdd1 = sc.parallelize([1, 2, 3, 4])
rdd2 = sc.parallelize([5, 6, 7, 1])
rdd1.union(rdd2).collect()
[1, 2, 3, 4, 5, 6, 7, 1]

intersection

# intersection求两个rdd元素的交集
rdd1 = sc.parallelize([1, 2, 3, 4])
rdd2 = sc.parallelize([3, 4, 5, 6])
rdd1.intersection(rdd2).collect()
[3, 4]

subtract

# subtract求差集
rdd1 = sc.parallelize([1, 2, 3, 4])
rdd2 = sc.parallelize([3, 4, 5, 6])
rdd1.subtract(rdd2).collect()
[1, 2]

sortBy

# sortBy根据指定key进行排序
sc.parallelize([('a', 4), ('b', 2), ('c', 3)]) \
    .sortBy(lambda e: e[1]) \
    .collect()
[('b', 2), ('c', 3), ('a', 4)]

转换算子-Key-Value型

mapValues

# mapValues对(key,Value)型数据中的Value进行操作
sc.parallelize([('a', 1), ('b', 2), ('c', 3)]) \
    .mapValues(lambda v: v**2) \
    .collect()
[('a', 1), ('b', 4), ('c', 9)]

flatMapValues

# 比mapValues多一步平坦化操作
sc.parallelize([('a', [1, 5]), ('b', [2, 4]), ('c', [3, 3])]) \
    .flatMapValues(lambda v: v) \
    .collect()
[('a', 1), ('a', 5), ('b', 2), ('b', 4), ('c', 3), ('c', 3)]

reduceByKey

# 对相同key的元素的value进行两两操作
sc.parallelize([('a', 1), ('b', 2), ('a', 3), ('b', 4)]) \
    .reduceByKey(lambda v1, v2: v1 + v2) \
    .collect() # 相加
[('a', 4), ('b', 6)]

groupByKey

# 相同key值元素放在一个列表中
sc.parallelize([('a', 1), ('b', 2), ('a', 3), ('b', 4)]) \
    .groupByKey() \
    .mapValues(list) \
    .collect()
[('a', [1, 3]), ('b', [2, 4])]

sortByKey

# sortByKey一定对PairRDD进行操作,默认按key升序排序
sc.parallelize([('1', 1), ('b', 2), ('a', 3), ('5', 4)]) \
    .sortByKey() \
    .collect()
[('1', 1), ('5', 4), ('a', 3), ('b', 2)]

keys

# keys返回仅包含key的RDD
sc.parallelize([(1, 'a'), (2, 'b')]) \
    .keys() \
    .collect()
[1, 2]

values

# values返回仅包含value的RDD
sc.parallelize([(1, 'a'), (2, 'b')]) \
    .values() \
    .collect()
['a', 'b']

join

# joins 内连接
rdd1 = sc.parallelize([('a', 1), ('b', 2)])
rdd2 = sc.parallelize([('a', 3), ('a', 4)])
rdd1.join(rdd2).collect()
[('a', (1, 3)), ('a', (1, 4))]

leftOuterJoin

# leftOuterJoin 左外连接
rdd1 = sc.parallelize([('a', 1), ('b', 2)])
rdd2 = sc.parallelize([('a', 3), ('a', 4)])
rdd1.leftOuterJoin(rdd2).collect()
[('b', (2, None)), ('a', (1, 3)), ('a', (1, 4))]

rightOuterJoin

# rightOuterJoin 右外连接
rdd1 = sc.parallelize([('a', 1), ('b', 2)])
rdd2 = sc.parallelize([('a', 3), ('a', 4)])
rdd1.rightOuterJoin(rdd2).collect()
[('a', (1, 3)), ('a', (1, 4))]

动作算子

# 前面已经说了collect,reduce, saveAsTextFile

count

# count返回RDD中元素个数
sc.parallelize([1,2,3,4,5]).count()
5

take

# take返回包含RDD中前n个元素的列表
sc.parallelize([1,2,3,4,5]).take(3)
[1, 2, 3]

takeOrdered

# 返回RDD中前n个最小元素组成的列表,默认升序排列
sc.parallelize([1, 5, 2, 7, 3]).takeOrdered(3)
[1, 2, 3]

first

# 返回RDD中第一个元素
sc.parallelize([2, 3, 4]).first()
2

top

# top返回RDD中最大的n个元素组成的列表,默认降序排列。
sc.parallelize([1,2,3,11,12]).top(3)
[12, 11, 3]
# 如果设置了key,则先对各元素进行对应处理
sc.parallelize([1,2,3,11,22]).top(4, key=str) # 字符串的大小比较是从左往右
[3, 22, 2, 11]

foreach

# 遍历RDD的每个元素,分别执行f函数操作,无返回值
sc.parallelize([1,2,3,4,5]) \
    .foreach(lambda x: exec('x=x+3; print(x)'))

foreachPartition

# 对每个分区操作,无返回值
sc.parallelize([1,2,3,4,5], 3) \
    .foreachPartition(lambda part: exec('s = sum(part); print(s)'))

collectAsMap

# 以字典形式,返回PairRDD中的键值对。如果key重复,则后面的value覆盖前面的。
sc.parallelize([('a', 1), ('b', 2), ('c', 3), ('a', 4)]) \
    .collectAsMap()
{'a': 4, 'b': 2, 'c': 3}

countByKey

# 以字典形式,返回PairRDD中key值出现的次数
sc.parallelize([('a', 1), ('b', 2), ('b', 3), ('a', 4)]) \
    .countByKey()
defaultdict(int, {'a': 2, 'b': 2})

持久化

# 1.如果一个RDD需要重复使用,那么需要从头再次执行来获取数据。RDD对象可以重用,但数据无法重用。
# 2.持久化操作必须在行动算子执行时完成
# 3.RDD对象的持久化操作既可为重用,也是因数据执行较长从而提高效率
dataRDD = sc.textFile('E:/spark-2.3.0-bin-hadoop2.7/data/graphx/users.txt')
num = dataRDD.map(lambda s: len(s)).reduce(lambda a, b: a + b)
dataRDD.persist()
l = dataRDD.glom().collect()

print(num)
print(l)
print("StorageLevel: ", dataRDD.getStorageLevel()) # 获取存储等级
162
[['1,BarackObama,Barack Obama', '2,ladygaga,Goddess of Love', '3,jeresig,John Resig', '4,justinbieber,Justin Bieber'], ['6,matei_zaharia,Matei Zaharia', '7,odersky,Martin Odersky', '8,anonsys']]
StorageLevel:  Memory Serialized 1x Replicated

共享变量

广播变量

# 使在Executor内存中只保留一个只读变量,而不是将其副本与任务一起发送。
b = sc.broadcast([1,2,3,4,5])
print(b.value)
l = sc.parallelize([0, 0]).map(lambda x: b.value).collect()
print(l)    
b.unpersist() # 从Executor内存中删除广播变量
[1, 2, 3, 4, 5]
[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]

累加器

# 如果只是counter = 0,最终结果为0。因为Executor不会将累加结果返回给Driver
counter = sc.accumulator(0)
rdd = sc.parallelize(range(10))
def increment(x):
    global counter
    counter += x
rdd.foreach(increment)
print("Counter value: ", counter.value)
Counter value:  45
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值