Spark (Python版) 零基础学习笔记(二)—— Spark Transformations总结及举例
1. map(func)
将func函数作用到数据集的每个元素,生成一个新的分布式的数据集并返回
>>> a = sc.parallelize(('a', 'b', 'c'))
>>> a.map(lambda x: x+'').collect()
['a1', 'b1', 'c1']
2. filter(func)
选出所有func返回值为true的元素,作为一个新的数据集返回
>>> a = sc.parallelize(range(10))
>>> a.filter(lambda x: x%2==0).collect() # 选出0-9的偶数
[0, 2, 4, 6, 8]
3. flatMap(func)
与map相似,但是每个输入的item能够被map到0个或者更多的items输出,也就是说func的返回值应当是一个Sequence,而不是一个单独的item
>>> l = ['I am Tom', 'She is Jenny', 'He is Ben']
>>> a = sc.parallelize(l,3)
>>> a.flatMap(lambda line: line.split()).collect() # 将每个字符串中的单词划分出来
['I', 'am', 'Tom', 'She', 'is', 'Jenny', 'He', 'is', 'Ben']
4. mapPartitions(func)
与map相似,但是mapPartitions的输入函数单独作用于RDD的每个分区(block)上,因此func的输入和返回值都必须是迭代器iterator。
例如:假设RDD有十个元素0~9,分成三个区,使用mapPartitions返回每个元素的平方。如果使用map方法,map中的输入函数会被调用10次,而使用mapPartitions方法,输入函数只会被调用3次,每个分区被调用1次。
>>> def squareFunc(a):
. . . for i in a:
. . . yield i*i
. . .
>>> a = sc.parallelize(range(10), 3)
PythonRDD[1] at RDD at PythonRDD.scala:48
>>> a.mapPartitions(squareFunc).collect()
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
5. mapPartitionsWithIndex(func)
与mapPartitions相似,但是输入函数func提供了一个正式的参数,可以用来表示分区的编号。
>>> def func(index, iterator): # 返回每个分区的编号和数值
. . . yield (‘index ‘ + str(index) + ’ is: ‘ + str(list(iterator)))
. . .
>>> a = sc.parallelize(range(10),3)
>>> a.mapPartitionsWithIndex(func).collect()
['index 0 is: [0, 1, 2]', 'index 1 is: [3, 4, 5]', 'index 2 is: [6, 7, 8, 9]']
>>> def squareIndex(index, iterator): # 返回每个数值所属分区的编号和数值的平方
... for i in iterator:
... yield ("The index is: " + str(index) + ", and the square is: " + str(i*i))
...
>>> a.mapPartitionsWithIndex(squareIndex).collect()
['The index is: 0, and the square is: 0',
'The index is: 0, and the square is: 1',
'The index is: 1, and the square is: 4',
'The index is: 1, and the square is: 9',
'The index is: 1, and the square is: 16',
'The index is: 2, and the square is: 25',
'The index is: 2, and the square is: 36',
'The index is: 3, and the square is: 49',
'The index is: 3, and the square is: 64',
'The index is: 3, and the square is: 81']
6. sample(withReplacement, fraction, seed)
从数据中抽样,withReplacement表示是否有放回,withReplacement=true表示有放回抽样,fraction为抽样的概率(0<=fraction<=1),seed为随机种子。
例如:从1-100之间抽取样本,被抽取为样本的概率为0.2
>>> data = sc.parallelize(range(1,101),2)
>>> sample = data.sample(True, 0.2)
>>> sampleData.count()
19
>>> sampleData.collect()
[16, 19, 24, 29, 32, 33, 44, 45, 55, 56, 56, 57, 65, 65, 73, 83, 84, 92, 96]
!!!注意,Spark中的sample抽样,当withReplacement=True时,相当于采用的是泊松抽样;当withReplacement=False时,相当于采用伯努利抽样,fraction并不是表示抽样得到的样本占原来数据总量的百分比,而是一个元素被抽取为样本的概率。fraction=0.2并不是说明要抽出100个数字中20%的数据作为样本,而是每个数字被抽取为样本的概率为0.2,这些数字被认为来自同一总体,样本的大小并不是固定的,而是服从二项分布。
7. union(otherDataset)
并集操作,将源数据集与union中的输入数据集取并集,默认保留重复元素(如果不保留重复元素,可以利用distinct操作去除,下边介绍distinct时会介绍)。
>>> data1 = sc.parallelize(range(10))
>>> data2 = sc.parallelize(range(6,15))
>>> data1.union(data2).collect()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 6, 7, 8, 9, 10, 11, 12, 13, 14]
8. intersection(otherDataset)
交集操作,将源数据集与union中的输入数据集取交集,并返回新的数据集。
>>> data1 = sc.parallelize(range(10))
>>> data2 = sc.parallelize(range(6,15))
>>> data1.intersection(data2).collect()
[8, 9, 6, 7]
9. distinct([numTasks])
去除数据集中的重复元素。
>>> data1 = sc.parallelize(range(10))
>>> data2 = sc.parallelize(range(6,15))
>>> data1.union(data2).distinct().collect()
[0, 8, 1, 9, 2, 10, 11, 3, 12, 4, 5, 13, 14, 6, 7]
下边的一系列transactions会用的键(Key)这一概念,在进行下列有关Key操作时使用的数据集为记录伦敦各个片区(英文称为ward)中学校和学生人数相关信息的表格,下载地址:
https://data.london.gov.uk/dataset/london-schools-atlas/resource/64f771ee-38b1-4eff-8cd2-e9ba31b90685#
下载后将其中命名为WardtoSecSchool_LDS_2015的sheet里边的数据保存为csv格式,删除第一行的表头,并重新命名为school.csv
数据格式为:
(Ward_CODE, Ward_NAME, TotalWardPupils, Ward2Sec_Flow_No., Secondary_School_URN, Secondary_School_Name, Pupil_count)
首先对数据进行一些预处理:
>>> school = sc.textFile("file:///home/yang/下载/school.csv")
Data = sc.textFile("file:///home/yang/下载/school.csv")
>>> school.count() # 共有16796行数据
16796
>>> import re # 引入python的正则表达式包
>>> rows = school.map(lambda line: re.subn(',[\s]+',': ', line))
注意:1. 从本地读取数据时,代码中要通过 “file://” 前缀指定读取本地文件。Spark shell 默认是读取 HDFS 中的文件,需要先上传文件到 HDFS 中,否则会有“org.apache.hadoop.mapred.InvalidInputException: Input path does not exist: hdfs://localhost:9000/user/hadoop/school.csv”的错误。
2. 对数据集进行了一下预处理,利用正则匹配替换字符串,由于一些学校的名字的字符串中本身含有逗号,比如“The City Academy, Hackney”, 此时如果利用csv的分隔符’,’进行分割,并不能将名字分割为“The City Academy”和“Hackney”。我们注意到csv的分隔符逗号后边是没有空格的,而名字里边的逗号后边都会有空格(英语书写习惯),因此,先利用re.subn语句对逗号后边含有至少一个空格(正则表达式为’,[\s]+’)的子字符串进行替换,替换为’: ’,然后再进行后续操作。以上即为对这一数据集的预处理过程。
10. groupByKey([numTasks])
作用于由键值对(K, V)组成的数据集上,将Key相同的数据放在一起,返回一个由键值对(K, Iterable)组成的数据集。
注意:1. 如果这一操作是为了后续在每个键上进行聚集(aggregation),比如sum或者average,此时使用reduceByKey或者aggregateByKey的效率更高。2. 默认情况下,输出的并行程度取决于RDD分区的数量,但也可以通过给可选参数numTasks赋值来调整并发任务的数量。
>>> newRows = rows.map(lambda r: r[0].split(','))
>>> ward_schoolname = newRows .map(lambda r: (r[1], r[5])).groupByKey() # r[1]为ward的名字,r[5]为学校的名字
>>> ward_schoolname.map(lambda x: {x[0]: list(x[1])}).collect() # 列出每个ward区域内所有的学校的名字
[{'Stifford Clays': ['William Edwards School', 'Brentwood County High School', "The Coopers' Company and Coborn School", 'Becket Keys Church of England Free School', ...]
# 输出结果为在Stifford Clays这个ward里的学校有William Edwards School,Brentwood County High School,The Coopers' Company and Coborn School等等...
11. reduceByKey(func, [numTasks])
作用于键值对(K, V)上,按Key分组,然后将Key相同的键值对的Value都执行func操作,得到一个值,注意func的类型必须满足
>>> pupils = newRows.map(lambda r: (r[1], int(r[6]))) # r[1]为ward的名字,r[6]为每个学校的学生数
>>> ward_pupils = pupils.reduceByKey(lambda x, y: x+y) # 计算各个ward中的学生数
>>> ward_pupils.collect() # 输出各个ward中的学生数
[('Stifford Clays', 1566), ('Shenley', 1625), ('Southbury', 3526),
('Rainham and Wennington', 769), ('Bromley Town', 574), ('Waltham Abbey Honey Lane', 835),
('Telegraph Hill', 1238), ('Chigwell Village', 1506), ('Gooshays', 2097), ('Edgware', 2585),
('Camberwell Green', 1374), ('Glyndon', 4633),...]
12. aggregateByKey(zeroValue, seqOp, comOp, [numTasks])
在于键值对(K, V)的RDD中,按key将value进行分组合并,合并时,将每个value和初始值作为seqOp函数的参数,进行计算,返回的结果作为一个新的键值对(K, V),然后再将结果按照key进行合并,最后将每个分组的value传递给comOp函数进行计算(先将前两个value进行计算,将返回结果和下一个value传给comOp函数,以此类推),将key与计算结果作为一个新的键值对(K, V)输出。
例子: 上述统计ward内学生人数的操作也可以通过aggregateByKey实现,此时,seqOp和comOp都是进行加法操作,代码如下:
>>> ward_pupils = pupils.aggregateByKey(0, lambda x, y: x+y, lambda x, y: x+y)
>>> ward_pupils.collect()
[('Stifford Clays', 1566), ('Shenley', 1625), ('Southbury', 3526),
('Rainham and Wennington', 769), ('Bromley Town', 574), ('Waltham Abbey Honey Lane', 835),
('Telegraph Hill', 1238), ('Chigwell Village', 1506), ('Gooshays', 2097), ('Edgware', 2585),
('Camberwell Green', 1374), ('Glyndon', 4633),...]
13. sortByKey([ascending=True], [numTasks])
按照Key进行排序,ascending的值默认为True,True/False表示升序还是降序
例如:将上述ward按照ward名字降序排列,打印出前十个
>>> ward_pupils.sortByKey(False, 4).take(10)
[('Yiewsley', 2560), ('Wormholt and White City', 1455), ('Woodside', 1204),
('Woodhouse', 2930), ('Woodcote', 1214), ('Winchmore Hill', 1116), ('Wilmington', 2243),
('Willesden Green', 1896), ('Whitefoot', 676), ('Whalebone', 2294)]
14. join(otherDataset, [numTasks])
类似于SQL中的连接操作,即作用于键值对(K, V)和(K, W)上,返回元组 (K, (V, W)),spark也支持外连接,包括leftOuterJoin,rightOuterJoin和fullOuterJoin。例子:
>>> class1 = sc.parallelize(('Tom', 'Jenny', 'Bob')).map(lambda a: (a, 'attended'))
>>> class2 = sc.parallelize(('Tom', 'Amy', 'Alice', 'John')).map(lambda a: (a, 'attended'))
>>> class1.join(class2).collect()
[('Tom', ('attended', 'attended'))]
>>> class1.leftOuterJoin(class2).collect()
[('Tom', ('attended', 'attended')), ('Jenny', ('attended', None)), ('Bob', ('attended', None))]
>>> class1.rightOuterJoin(class2).collect()
[('John', (None, 'attended')), ('Tom', ('attended', 'attended')), ('Amy', (None, 'attended')), ('Alice', (None, 'attended'))]
>>> class1.fullOuterJoin(class2).collect()
[('John', (None, 'attended')), ('Tom', ('attended', 'attended')), ('Jenny', ('attended', None)), ('Bob', ('attended', None)), ('Amy', (None, 'attended')), ('Alice', (None, 'attended'))]
15. cogroup(otherDataset, [numTasks])
作用于键值对(K, V)和(K, W)上,返回元组 (K, (Iterable, Iterable))。这一操作可叫做groupWith。
>>> class1 = sc.parallelize(('Tom', 'Jenny', 'Bob')).map(lambda a: (a, 'attended'))
>>> class2 = sc.parallelize(('Tom', 'Amy', 'Alice', 'John')).map(lambda a: (a, 'attended'))
>>> group = class1.cogroup(class2)
>>> group.collect()
[('John', (<pyspark.resultiterable.ResultIterable object at 0x7fb7e808afd0>, <pyspark.resultiterable.ResultIterable object at 0x7fb7e808a1d0>)),
('Tom', (<pyspark.resultiterable.ResultIterable object at 0x7fb7e808a7f0>, <pyspark.resultiterable.ResultIterable object at 0x7fb7e808a048>)),
('Jenny', (<pyspark.resultiterable.ResultIterable object at 0x7fb7e808a9b0>, <pyspark.resultiterable.ResultIterable object at 0x7fb7e808a208>)),
('Bob', (<pyspark.resultiterable.ResultIterable object at 0x7fb7e808ae80>, <pyspark.resultiterable.ResultIterable object at 0x7fb7e8b448d0>)),
('Amy', (<pyspark.resultiterable.ResultIterable object at 0x7fb7e8b44c88>, <pyspark.resultiterable.ResultIterable object at 0x7fb7e8b44588>)),
('Alice', (<pyspark.resultiterable.ResultIterable object at 0x7fb7e8b44748>, <pyspark.resultiterable.ResultIterable object at 0x7fb7e8b44f98>))]
>>> group.map(lambda x: {x[0]: [list(x[1][0]), list(x[1][1])]}).collect()
[{'John': [[], ['attended']]}, {'Tom': [['attended'], ['attended']]}, {'Jenny': [['attended'], []]}, {'Bob': [['attended'], []]}, {'Amy': [[], ['attended']]}, {'Alice': [[], ['attended']]}]
16. cartesian(otherDataset)
笛卡尔乘积,作用于数据集T和U上,返回(T, U),即数据集中每个元素的两两组合
>>> a = sc.parallelize(('a', 'b', 'c'))
>>> b = sc.parallelize(('d', 'e', 'f'))
>>> a.cartesian(b).collect()
[('a', 'd'), ('a', 'e'), ('a', 'f'), ('b', 'd'), ('b', 'e'), ('b', 'f'), ('c', 'd'), ('c', 'e'), ('c', 'f')]
17. pipe(command, [envVars])
将驱动程序中的RDD交给shell处理(外部进程),例如Perl或bash脚本。RDD元素作为标准输入传给脚本,脚本处理之后的标准输出会作为新的RDD返回给驱动程序。
18. coalesce(numPartitions)
将RDD的分区数减小到numPartitions个。当数据集通过过滤规模减小时,使用这个操作可以提升性能。
19. repartition(numPartitions)
重组数据,数据被重新随机分区为numPartitions个,numPartitions可以比原来大,也可以比原来小,平衡各个分区。这一操作会将整个数据集在网络中重新洗牌。
20. repartitionAndSortWithinPartitions(partitioner)
根据给定的partitioner函数重新将RDD分区,并在分区内排序。这比先repartition然后在分区内sort高效,原因是这样迫使排序操作被移到了shuffle阶段。
Spark (Python版) 零基础学习笔记(二)—— Spark Transformations总结及举例的更多相关文章
- Spark (Python版) 零基础学习笔记(一)—— 快速入门
由于Scala才刚刚开始学习,还是对python更为熟悉,因此在这记录一下自己的学习过程,主要内容来自于spark的官方帮助文档,这一节的地址为: http://spark.apache.org/do ...
- CSS零基础学习笔记.
酸菜记 之 CSS的零基础. 这篇是我自己从零基础学习CSS的笔记加理解总结归纳的,如有不对的地方,请留言指教, 学前了解: CSS中字母是不分大小写的; CSS文件可以使用在各种程序文件中(如:PH ...
- salesforce 零基础学习(二十二)Test简单使用
本篇内容只是本人简单的mark开发中常出现的一些疑问,方便后期项目使用时奠定基础,如果对Test零基础童鞋,欢迎查看Test官方的使用介绍: https://help.salesforce.com/a ...
- python基础学习笔记二之列表
1.列表 ①列表的创建: ②列表的查询(索引): ③列表的切片操作: 此处要注意到:返回索引0到3的元素,顾头不顾尾. ④列表的增加: s.append() #直接在结尾追加 s.insert() ...
- javascript第二遍基础学习笔记(二)
一.操作符 1.一元操作符: 自加自减(分前置和后置2种):++.-- 区别:前置的先自加或自减,后进行计算:而后置的是先进行计算,后自加或自减(在其会产生负面影响时才能体现区别): ; i++; / ...
- Java基础学习笔记二十八 管家婆综合项目
本项目为JAVA基础综合项目,主要包括: 熟练View层.Service层.Dao层之间的方法相互调用操作.熟练dbutils操作数据库表完成增删改查. 项目功能分析 查询账务 多条件组合查询账务 添 ...
- salesforce 零基础学习(二十)简单APP制作
本篇参考链接:https://developer.salesforce.com/trailhead/project/salesforce_developer_workshop 本篇讲述的是最简单的AP ...
- Java基础学习笔记二十一 多线程
多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...
- Java基础学习笔记二十二 网络编程
络通信协议 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样.在计算机网络中,这些连接和通信的规则 ...
随机推荐
- ACM学习历程—POJ3565 Ants(最佳匹配KM算法)
Young naturalist Bill studies ants in school. His ants feed on plant-louses that live on apple trees ...
- 学习大牛笔记nginx + gunicorn + supervisor
安装 gunicorn pip install gunicorn pip 是一个重要的工具,python 用来管理包.还有一个最佳生产就是每次使用 pip 安装的库,都写入一个 requirement ...
- photon server (1)
Photon是一套使用广泛的socket server引擎,服务端底层C++编写,客户端C#编写,跨多平台,收费,效率可观的一款引擎.实用上前有九城游戏(原魔兽世界代理),现在笔者发现多款腾讯旗下3D ...
- Parallel Programming-Task Result && Continuation Task
本文主要介绍带有返回值的Task和Continuation Task 带返回值的Task Continuation Task ContinueWhenAll即多任务延续 一.带返回值的Task 1.1 ...
- 浅谈双流水线调度问题以及Jhonson算法
引入:何为流水线问题 有\(n\)个任务,对于每个任务有\(m\)道工序,每个任务的\(m\)道工序必须在不同的m台机器上依次完成才算把这个任务完成,在前\(i-1\)道工序完成后才能去完成第\(i\ ...
- 深入理解javascript中的立即执行函数
这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是用(function(){…})()包住业务代码,使用jquery时比较常见,需要的朋友可以 ...
- 我的SIP开发之路
http://hi.baidu.com/ltlovelty/blog/item/837baf1ece7fc6f11ad57647.html 经过对SIP协议和开源协议栈快半年的研究,我现在终于有点入门 ...
- makefile 基础知识
$@ 目标文件名 $< 第一个依赖文件名 $^ 规则所有依赖文件列表 如果不想让执行语句被打印出来,就在语句前面加上@符号 模式规则 %.o:%.c 后缀规则 .c.o 生成单进程的Mak ...
- 一个有关Golang变量作用域的坑
转自:http://tonybai.com/2015/01/13/a-hole-about-variable-scope-in-golang/ 临近下班前编写和调试一段Golang代码,但运行结果始终 ...
- java web基础知识
1)TCP三次握手 第一次握手:客户端A将标志位SYN置为1,随机产生一个值为seq=J(J的取值范围为=1234567)的数据包到服务器,客户端A进入SYN_SENT状态,等待服务端B确认: 第二次 ...