Spark系列:Python版Spark编程指南
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Spark系列:Python版Spark编程指南
⽬录
⼀、介绍
⼆、连接Spark
三、创建RDD
四、RDD常⽤的转换 Transformation
五、RDD 常⽤的执⾏动作 Action
⼆、连接Spark
Spark1.3.0只⽀持Python2.6或更⾼的版本(但不⽀持Python3)。
它使⽤了标准的CPython解释器,所以诸如NumPy⼀类的C库也是可以使⽤的。
通过Spark⽬录下的bin/spark-submit脚本你可以在Python中运⾏Spark应⽤。
这个脚本会载⼊Spark的Java/Scala库然后让你将应⽤提交到集群中。
你可以执⾏bin/pyspark来打开Python的交互命令⾏。
如果你希望访问HDFS上的数据,你需要为你使⽤的HDFS版本建⽴⼀个PySpark连接。
常见的HDFS版本标签都已经列在了这个第三⽅发⾏版页⾯。
最后,你需要将⼀些Spark的类import到你的程序中。
加⼊如下这⾏:
from pyspark import SparkContext, SparkConf
在⼀个Spark程序中要做的第⼀件事就是创建⼀个SparkContext对象来告诉Spark如何连接⼀个集群。
为了创建SparkContext,你⾸先需要创建⼀个SparkConf对象,这个对象会包含你的应⽤的⼀些相关信息。
conf = SparkConf().setAppName(appName).setMaster(master)
sc = SparkContext(conf=conf)
appName参数是在集群UI上显⽰的你的应⽤的名称。
master是⼀个Spark、Mesos或YARN集群的URL,如果你在本地运⾏那么这个参数应该是特殊的”local”字符串。
在实际使⽤中,当你在集群中运⾏你的程序,你⼀般不会把master参数写死在代码中,⽽是通过⽤spark-submit运⾏程序来获得这个参数。
但是,在本地测试以及单元测试时,你仍需要⾃⾏传⼊”local”来运⾏Spark程序。
三、创建RDD
Spark是以RDD概念为中⼼运⾏的。
RDD是⼀个容错的、可以被并⾏操作的元素集合。
创建⼀个RDD有两个⽅法:在你的驱动程序中并⾏化⼀个已经存在的集合;从外部存储系统中引⽤⼀个数据集,这个存储系统可以是⼀个共享⽂件系统,⽐如HDFS、HBase或任意提供了Hadoop输⼊格式的数据来源。
并⾏化集合
并⾏化集合是通过在驱动程序中⼀个现有的迭代器或集合上调⽤SparkContext的parallelize⽅法建⽴的。
为了创建⼀个能够并⾏操作的分布数据集,集合中的元素都会被拷贝。
⽐如,以下语句创建了⼀个包含1到5的并⾏化集合:
data = [1, 2, 3, 4, 5]
distData = sc.parallelize(data)
分布数据集(distData)被建⽴起来之后,就可以进⾏并⾏操作了。
⽐如,我们可以调⽤disData.reduce(lambda a, b: a+b)来对元素进⾏叠加。
在后⽂中我们会描述分布数据集上⽀持的操作。
并⾏集合的⼀个重要参数是将数据集划分成分⽚的数量。
对每⼀个分⽚,Spark会在集群中运⾏⼀个对应的任务。
典型情况下,集群中的每⼀个CPU将对应运⾏2-4个分⽚。
⼀般情况下,Spark会根据当前集群的情况⾃⾏设定分⽚数量。
但是,你也可以通过将第⼆个参数传递给parallelize⽅法(⽐如sc.parallelize(data, 10))来⼿动确定分⽚数量。
注意:有些代码中会使⽤切⽚(slice,分⽚的同义词)这个术语来保持向下兼容性。
⼀个简单的⽰例:
import findspark
findspark.init()
from pyspark import SparkContext
from pyspark import SparkConf
conf = SparkConf().setAppName("miniProject").setMaster("local[*]")
sc=SparkContext.getOrCreate(conf)
rdd=sc.parallelize([1,2,3,4,5])
rdd1=rdd.map(lambda r:r+10) #map对每⼀个元素操作
print(rdd1.collect())
外部数据集
PySpark可以通过Hadoop⽀持的外部数据源(包括本地⽂件系统、HDFS、 Cassandra、HBase、亚马逊S3等等)建⽴分布数据集。
Spark⽀持⽂本⽂件、序列⽂件以及其他任何 Hadoop输⼊格式⽂件。
通过⽂本⽂件创建RDD要使⽤SparkContext的textFile⽅法。
这个⽅法会使⽤⼀个⽂件的URI(或本地⽂件路径,hdfs://、s3n://这样的URI等等)然后读⼊这个⽂件建⽴⼀个⽂本⾏的集合。
以下是⼀个例⼦:
>>> distFile = sc.textFile("data.txt")
建⽴完成后distFile上就可以调⽤数据集操作了。
⽐如,我们可以调⽤map和reduce操作来叠加所有⽂本⾏的长度,代码如下:
distFile.map(lambda s: len(s)).reduce(lambda a, b: a + b)
在Spark中读⼊⽂件时有⼏点要注意:
如果使⽤了本地⽂件路径时,要保证在worker节点上这个⽂件也能够通过这个路径访问。
这点可以通过将这个⽂件拷贝到所有worker上或者使⽤⽹络挂载的共享⽂件系统来解决。
包括textFile在内的所有基于⽂件的Spark读⼊⽅法,都⽀持将⽂件夹、压缩⽂件、包含通配符的路径作为参数。
⽐如,以下代码都是合法的:
textFile("/my/directory")
textFile("/my/directory/*.txt")
textFile("/my/directory/*.gz")
textFile⽅法也可以传⼊第⼆个可选参数来控制⽂件的分⽚数量。
默认情况下,Spark会为⽂件的每⼀个块(在HDFS中块的⼤⼩默认是64MB)创建⼀个分⽚。
但是你也可以通过传⼊⼀个更⼤的值来要求Spark建⽴更多的分⽚。
注意,分⽚的数量绝不能⼩于⽂件块的数量。
除了⽂本⽂件之外,Spark的Python API还⽀持多种其他数据格式:
SparkContext.wholeTextFiles能够读⼊包含多个⼩⽂本⽂件的⽬录,然后为每⼀个⽂件返回⼀个(⽂件名,内容)对。
这是与textFile⽅法为每⼀个⽂本⾏返回⼀条记录相对应的。
RDD.saveAsPickleFile和SparkContext.pickleFile⽀持将RDD以串⾏化的Python对象格式存储起来。
串⾏化的过程中会以默认10个⼀批的数量批量处理。
序列⽂件和其他Hadoop输⼊输出格式。
注意
这个特性⽬前仍处于试验阶段,被标记为Experimental,⽬前只适⽤于⾼级⽤户。
这个特性在未来可能会被基于Spark SQL的读写⽀持所取代,因为Spark SQL是更好的⽅式。
可写类型⽀持
PySpark序列⽂件⽀持利⽤Java作为中介载⼊⼀个键值对RDD,将可写类型转化成Java的基本类型,然后使⽤ Pyrolite将java结果对象串⾏化。
当将⼀个键值对RDD储存到⼀个序列⽂件中时PySpark将会运⾏上述过程的相反过程。
⾸先将Python对象反串⾏化成Java对象,然后转化成可写类型。
以下可写类型会⾃动转换:
| 可写类型 | Python类型 |
| Text | unicode str|
| IntWritable | int |
| FloatWritable | float |
| DoubleWritable | float |
| BooleanWritable | bool |
| BytesWritable | bytearray |
| NullWritable | None |
| MapWritable | dict |
数组是不能⾃动转换的。
⽤户需要在读写时指定ArrayWritable的⼦类型.在读⼊的时候,默认的转换器会把⾃定义的ArrayWritable⼦类型转化成Java的Object[],之后串⾏化成Python的元组。
为了获得Python的array.array类型来使⽤主要类型的数组,⽤户需要⾃⾏指定转换器。
保存和读取序列⽂件
和⽂本⽂件类似,序列⽂件可以通过指定路径来保存与读取。
键值类型都可以⾃⾏指定,但是对于标准可写类型可以不指定。
>>> rdd = sc.parallelize(range(1, 4)).map(lambda x: (x, "a" * x ))
>>> rdd.saveAsSequenceFile("path/to/file")
>>> sorted(sc.sequenceFile("path/to/file").collect())
[(1, u'a'), (2, u'aa'), (3, u'aaa')]
保存和读取其他Hadoop输⼊输出格式
PySpark同样⽀持写⼊和读出其他Hadoop输⼊输出格式,包括’新’和’旧’两种Hadoop MapReduce API。
如果有必要,⼀个Hadoop配置可以以Python字典的形式传⼊。
以下是⼀个例⼦,使⽤了Elasticsearch ESInputFormat:
$ SPARK_CLASSPATH=/path/to/elasticsearch-hadoop.jar ./bin/pyspark
>>> conf = {"es.resource" : "index/type"} # assume Elasticsearch is running on localhost defaults
>>> rdd = sc.newAPIHadoopRDD("org.elasticsearch.hadoop.mr.EsInputFormat",\
"org.apache.hadoop.io.NullWritable", "org.elasticsearch.hadoop.mr.LinkedMapWritable", conf=conf)
>>> rdd.first() # the result is a MapWritable that is converted to a Python dict
(u'Elasticsearch ID',
{u'field1': True,
u'field2': u'Some Text',
u'field3': 12345})
四、RDD常⽤的转换 Transformation
RDD⽀持两类操作:转化操作,⽤于从已有的数据集转化产⽣新的数据集;启动操作,⽤于在计算结束后向驱动程序返回结果。
举个例⼦,map是⼀个转化操作,可以将数据集中每⼀个元素传给⼀个函数,同时将计算结果作为⼀个新的RDD返回。
另⼀⽅⾯,reduce操作是⼀个启动操作,能够使⽤某些函数来聚集计算RDD中所有的元素,并且向驱动程序返回最终结果(同时还有⼀个并⾏的reduceByKey操作可以返回⼀个分布数据集)。
在Spark所有的转化操作都是惰性求值的,就是说它们并不会⽴刻真的计算出结果。
相反,它们仅仅是记录下了转换操作的操作对象(⽐如:⼀个⽂件)。
只有当⼀个启动操作被执⾏,要向驱动程序返回结果时,转化操作才会真的开始计算。
这样的设计使得Spark运⾏更加⾼效——⽐如,我们会发觉由map操作产⽣的数据集将会在reduce操作中⽤到,之后仅仅是返回了reduce的最终的结果⽽不是map产⽣的庞⼤数据集。
在默认情况下,每⼀个由转化操作得到的RDD都会在每次执⾏启动操作时重新计算⽣成。
但是,你也可以通过调⽤persist(或cache)⽅法来将RDD持久化到内存中,这样Spark就可以在下次使⽤这个数据集时快速获得。
Spark同样提供了对将RDD持久化到硬盘上或在多个节点间复制的⽀持。
下⾯的表格列出了Spark⽀持的常⽤转化操作。
欲知细节,请查阅RDD API⽂档(, , )和键值对RDD函数⽂档(, )。
转化操作 | 作⽤
————| ——
map(func) | 返回⼀个新的分布数据集,由原数据集元素经func处理后的结果组成
filter(func) | 返回⼀个新的数据集,由传给func返回True的原数据集元素组成
flatMap(func) | 与map类似,但是每个传⼊元素可能有0或多个返回值,func可以返回⼀个序列⽽不是⼀个值
mapParitions(func) | 类似map,但是RDD的每个分⽚都会分开独⽴运⾏,所以func的参数和返回值必须都是迭代器
mapParitionsWithIndex(func) | 类似mapParitions,但是func有两个参数,第⼀个是分⽚的序号,第⼆个是迭代器。
返回值还是迭代器
sample(withReplacement, fraction, seed) | 使⽤提供的随机数种⼦取样,然后替换或不替换
union(otherDataset) | 返回新的数据集,包括原数据集和参数数据集的所有元素
intersection(otherDataset) | 返回新数据集,是两个集的交集
distinct([numTasks]) | 返回新的集,包括原集中的不重复元素
groupByKey([numTasks]) | 当⽤于键值对RDD时返回(键,值迭代器)对的数据集
aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) | ⽤于键值对RDD时返回(K,U)对集,对每⼀个Key的value进⾏聚集计算
sortByKey([ascending], [numTasks])⽤于键值对RDD时会返回RDD按键的顺序排序,升降序由第⼀个参数决定
join(otherDataset, [numTasks]) | ⽤于键值对(K, V)和(K, W)RDD时返回(K, (V, W))对RDD
cogroup(otherDataset, [numTasks]) | ⽤于两个键值对RDD时返回(K, (V迭代器, W迭代器))RDD
cartesian(otherDataset) | ⽤于T和U类型RDD时返回(T, U)对类型键值对RDD
pipe(command, [envVars]) | 通过shell命令管道处理每个RDD分⽚
coalesce(numPartitions) | 把RDD的分⽚数量降低到参数⼤⼩
repartition(numPartitions) | 重新打乱RDD中元素顺序并重新分⽚,数量由参数决定
repartitionAndSortWithinPartitions(partitioner) | 按照参数给定的分⽚器重新分⽚,同时每个分⽚内部按照键排序
具体⽰例:
map
将函数作⽤于数据集的每⼀个元素上,⽣成⼀个分布式的数据集返回
Return a new RDD by applying a function to each element of this RDD.
>>> rdd = sc.parallelize(["b", "a", "c"])
>>> sorted(rdd.map(lambda x: (x, 1)).collect())
[('a', 1), ('b', 1), ('c', 1)]
⼀个完整的例⼦:
from <span class='wp_keywordlink_affiliate'><a href="https:///tag/pyspark" title="View all posts in pyspark" target="_blank">pyspark</a></span> import SparkConf,SparkContext
#配置
conf = SparkConf() #.setAppName("spark demo ").setMaster("local[2]")
sc = SparkContext(conf=conf)
data = range(10)
print(list(data))
r1 = sc.parallelize(data)
r2 = r1.map(lambda x:x+1)
print(r2.collect())
sc.stop()
结果是:
filter
返回所有 funtion 返回值为True的函数,⽣成⼀个分布式的数据集返回
Return a new RDD containing only the elements that satisfy a predicate.
>>> rdd = sc.parallelize([1, 2, 3, 4, 5])
>>> rdd.filter(lambda x: x % 2 == 0).collect()
[2, 4]
⼀个完整的例⼦:
from pyspark import SparkConf,SparkContext
#配置
conf = SparkConf() #.setAppName("spark demo ").setMaster("local[2]")
sc = SparkContext(conf=conf)
data = range(10)
print(list(data))
r1 = sc.parallelize(data)
r2 = r1.filter(lambda x:x>5)
print(r2.collect())
sc.stop()
结果是:
flatMap
Return a new RDD by first applying a function to all elements of this RDD, and then flattening the results.⼀个完整的例⼦:
from pyspark import SparkConf,SparkContext
#配置
conf = SparkConf() #.setAppName("spark demo ").setMaster("local[2]")
sc = SparkContext(conf=conf)
data = ["hello zeropython","hello "]
# print(list(data))
r1 = sc.parallelize(data)
r2 = r1.flatMap(lambda x:x.split(""))
r3 = r1.map(lambda x:x.split(""))
print(r2.collect())
print(r3.collect())
sc.stop()
RDD, and then flattening the results.
结果是:
groupBykey
按照相同key的数据分成⼀组
from _operator import add
from pyspark import SparkConf,SparkContext
#配置
conf = SparkConf() #.setAppName("spark demo ").setMaster("local[2]")
sc = SparkContext(conf=conf)
sc.setLogLevel("WARN")
"""
Return a new RDD by first applying a function to all elements of this
RDD, and then flattening the results.
"""
data = ["hello zeropython","hello "]
# print(list(data))
r1 = sc.parallelize(data)
r2 = r1.flatMap(lambda x:x.split("")).map(lambda y:(y,1))
print("r2",r2.collect())
r3 = r2.groupByKey()
print("r3",r3.collect())
r4 = r3.map(lambda x:{x[0]:list(x[1])})
print("r4",r4.collect())
print(r2.reduceByKey(add).collect())
sc.stop()
结果是:
groupBy运算
groupBy运算可以按照传⼊匿名函数的规则,将数据分为多个Array。
⽐如下⾯的代码将intRDD分为偶数和奇数:result = intRDD.groupBy(lambda x : x % 2).collect()
print (sorted([(x, sorted(y)) for (x, y) in result]))
输出为:
[(0, [2]), (1, [1, 3, 5, 5])]
reduceBykey
把相同的key 的数据分发到⼀起并进⾏运算
from _operator import add
from pyspark import SparkConf,SparkContext
#配置
conf = SparkConf() #.setAppName("spark demo ").setMaster("local[2]")
sc = SparkContext(conf=conf)
data = ["hello zeropython","hello "]
# print(list(data))
r1 = sc.parallelize(data)
r2 = r1.flatMap(lambda x:x.split("")).map(lambda x:(x,1))
print("r2",r2.collect())
r3 = r2.reduceByKey(lambda x,y:x+y)
print("r3",r3.collect())
sc.stop()
结果是:
sortbykey
Sorts this RDD, which is assumed to consist of (key, value) pairs. from _operator import add
from pyspark import SparkConf,SparkContext
#配置
conf = SparkConf() #.setAppName("spark demo ").setMaster("local[2]")
sc = SparkContext(conf=conf)
# sc.setLogLevel("FATAL")
# sc.setLogLevel("ERROR")
sc.setLogLevel("ERROR")
data = ["hello zeropython","hwlldsf world","","","hello "] # print(list(data))
r1 = sc.parallelize(data)
r2 = r1.flatMap(lambda x:x.split(""))\
.map(lambda y:(y,1))\
.reduceByKey(lambda x,y:x+y)\
.sortByKey(lambda x:x[1])
# sortByKey排序根据关键词的值进⾏排序
# reduceByKey 让[("a",[1,1,1,1])] 转换成 [("a",3)]
print(r2.collect())
sc.stop()
结果是:
union
1 2 3 4 5 6 7 8"""
Return the union of this RDD and another one. >>> rdd = sc.parallelize([1, 1, 2, 3])
>>> rdd.union(rdd).collect()
[1, 1, 2, 3, 1, 1, 2, 3]
"""
distinct
1 2 3 4 5 6 7"""
Return a new RDD containing the distinct elements in this RDD. >>> sorted(sc.parallelize([1, 1, 2, 3]).distinct().collect())
[1, 2, 3]
"""
join
1 2 3 4 5>>> a = sc.parallelize([("A", "a1"), ("C", "c1"), ("D", "d1"), ("F", "f1"), ("F", "f2")]) >>> b = sc.parallelize([("A", "a2"), ("C", "c2"), ("C", "c3"), ("E", "e1")])
>>> a.join(b).collect()
[('C', ('c1', 'c2')), ('C', ('c1', 'c3')), ('A', ('a1', 'a2'))]
leftOuterJoin
1 2 3>>> a.leftOuterJoin(b).collect()
[('F', ('f1', None)), ('F', ('f2', None)), ('D', ('d1', None)), ('C', ('c1', 'c2')), ('C', ('c1', 'c3')), ('A', ('a1', 'a2'))]
rightOuterJoin
1 2 3>>> a.rightOuterJoin(b).collect()
[('E', (None, 'e1')), ('C', ('c1', 'c2')), ('C', ('c1', 'c3')), ('A', ('a1', 'a2'))]
fullOuterJoin
1 2 3 4>>> a.fullOuterJoin(b).collect()
[('F', ('f1', None)), ('F', ('f2', None)), ('D', ('d1', None)), ('E', (None, 'e1')), ('C', ('c1', 'c2')), ('C', ('c1', 'c3')), ('A', ('a1', 'a2'))] >>>
randomSplit运算
randomSplit 运算将整个集合以随机数的⽅式按照⽐例分为多个RDD,⽐如按照0.4和0.6的⽐例将intRDD分为两个RDD,并输出:
1 2 3 4 5 6 7intRDD = sc.parallelize([3,1,2,5,5])
stringRDD = sc.parallelize(['Apple','Orange','Grape','Banana','Apple']) sRDD = intRDD.randomSplit([0.4,0.6])
print (len(sRDD))
print (sRDD[0].collect())
print (sRDD[1].collect())
输出为:
1 2 3 42 [3, 1] [2, 5, 5]
多个RDD转换运算
RDD也⽀持执⾏多个RDD的运算,这⾥,我们定义三个RDD:
1 2 3 4intRDD1 = sc.parallelize([3,1,2,5,5]) intRDD2 = sc.parallelize([5,6]) intRDD3 = sc.parallelize([2,7])
并集运算
可以使⽤union函数进⾏并集运算:
1
2
print (intRDD1.union(intRDD2).union(intRDD3).collect())输出为:
1
2
[3, 1, 2, 5, 5, 5, 6, 2, 7]
交集运算
可以使⽤intersection进⾏交集运算:
1
2
print(intRDD1.intersection(intRDD2).collect())
两个集合中只有⼀个相同元素5,所以输出为:
1
2
[5]
差集运算
subtract(减去去除)
可以使⽤subtract函数进⾏差集运算:
1
2
print (intRDD1.subtract(intRDD2).collect())
由于两个RDD的重复部分为5,所以输出为[1,2,3]:
1[2, 1, 3]
2
笛卡尔积运算
笛卡尔乘积是指在数学中,两个集合X和Y的笛卡尓积(Cartesian product),⼜称直积,表⽰为X × Y,第⼀个对象是X的成员⽽第⼆个对象是Y的所有可能有序对的其中⼀个成员
笛卡尔积⼜叫笛卡尔乘积,是⼀个叫笛卡尔的⼈提出来的。
简单的说就是两个集合相乘的结果。
假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
可以使⽤cartesian函数进⾏笛卡尔乘积运算:
1
2
print (intRDD1.cartesian(intRDD2).collect())
由于两个RDD分别有5个元素和2个元素,所以返回结果有10各元素:
1
2
[(3, 5), (3, 6), (1, 5), (1, 6), (2, 5), (2, 6), (5, 5), (5, 6), (5, 5), (5, 6)]
五、RDD 常⽤的执⾏动作 Action
下⾯的表格列出了Spark⽀持的部分常⽤启动操作。
欲知细节,请查阅RDD API⽂档(, , )和键值对RDD函数⽂档(, )。
启动操作 | 作⽤
reduce(func) | 使⽤func进⾏聚集计算,func的参数是两个,返回值⼀个,两次func运⾏应当是完全解耦的,这样才能正确地并⾏运算
collect() | 向驱动程序返回数据集的元素组成的数组
count() | 返回数据集元素的数量
first() | 返回数据集的第⼀个元素
take(n) | 返回前n个元素组成的数组
takeSample(withReplacement, num, [seed]) | 返回⼀个由原数据集中任意num个元素的suzuki,并且替换之
takeOrder(n, [ordering]) | 返回排序后的前n个元素
saveAsTextFile(path) | 将数据集的元素写成⽂本⽂件
saveAsSequenceFile(path) | 将数据集的元素写成序列⽂件,这个API只能⽤于Java和Scala程序
saveAsObjectFile(path) | 将数据集的元素使⽤Java的序列化特性写到⽂件中,这个API只能⽤于Java和Scala程序
countByCount() | 只能⽤于键值对RDD,返回⼀个(K, int) hashmap,返回每个key的出现次数
foreach(func) | 对数据集的每个元素执⾏func, 通常⽤于完成⼀些带有副作⽤的函数,⽐如更新累加器(见下⽂)或与外部存储交互等
Action(执⾏):触发Spark作业的运⾏,真正触发转换算⼦的计算
Pyspark rdd 常⽤的转换 Transformation Pyspark(⼆
)
1 2 3intRDD = sc.parallelize([3,1,2,5,5])
stringRDD = sc.parallelize(['Apple','Orange','Grape','Banana','Apple'])
基本“动作”运算
读取元素
可以使⽤下列命令读取RDD内的元素,这是Actions运算,所以会马上执⾏:
1 2 3 4 5 6 7 8 9#取第⼀条数据
print (intRDD.first())
#取前两条数据
print (intRDD.take(2))
#升序排列,并取前3条数据
print (intRDD.takeOrdered(3))
#降序排列,并取前3条数据
print (intRDD.takeOrdered(3,lambda x:-x))
输出为:
1 2 3 4 53 [3, 1] [1, 2, 3] [5, 5, 3]
统计功能
可以将RDD内的元素进⾏统计运算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15#统计
print (intRDD.stats()) #最⼩值
print (intRDD.min()) #最⼤值
print (intRDD.max()) #标准差
print (intRDD.stdev()) #计数
print (intRDD.count()) #求和
print (intRDD.sum()) #平均
print (intRDD.mean())
输出为:
RDD Key-Value基本“转换”运算
Spark RDD⽀持键值对运算,Key-Value运算时mapreduce运算的基础,本节介绍RDD键值的基本“转换”运算。
初始化
我们⽤元素类型为tuple元组的数组初始化我们的RDD,这⾥,每个tuple的第⼀个值将作为键,⽽第⼆个元素将作为值。
作为值
1
2
kvRDD1 = sc.parallelize([(3,4),(3,6),(5,6),(1,2)])
得到key和value值
可以使⽤keys和values函数分别得到RDD的键数组和值数组:
1 2 3print (kvRDD1.keys().collect()) print (kvRDD1.values().collect())
输出为:
筛选元素
可以按照键进⾏元素筛选,也可以通过值进⾏元素筛选,和之前的⼀样,使⽤filter函数,这⾥要注意的是,虽然RDD中是以键值对形式存在,但是本质上还是⼀个⼆元组,⼆元组的第⼀个值代表键,第⼆个值代表值,所以按照如下的代码既可以按照键进⾏筛选,我们筛选键值⼩于5的数据:
1
2
print (kvRDD1.filter(lambda x:x[0] < 5).collect())
输出为:
1
2
[(3, 4), (3, 6), (1, 2)]
同样,将x[0]替换为x[1]就是按照值进⾏筛选,我们筛选值⼩于5的数据:
1
2
print (kvRDD1.filter(lambda x:x[1] < 5).collect())
输出为:
1
2
[(3, 4), (1, 2)]
值运算
我们可以使⽤mapValues⽅法处理value值,下⾯的代码将value值进⾏了平⽅处理:
1
2
print (kvRDD1.mapValues(lambda x:x**2).collect())
输出为:
1
2
[(3, 16), (3, 36), (5, 36), (1, 4)]
按照key排序
可以使⽤sortByKey按照key进⾏排序,传⼊参数的默认值为true,是按照从⼩到⼤排序,也可以传⼊参数false,表⽰从⼤到⼩排序:
1 2 3 4print (kvRDD1.sortByKey().collect()) print (kvRDD1.sortByKey(True).collect()) print (kvRDD1.sortByKey(False).collect())
输出为:
1 2 3 4[(1, 2), (3, 4), (3, 6), (5, 6)] [(1, 2), (3, 4), (3, 6), (5, 6)] [(5, 6), (3, 4), (3, 6), (1, 2)]
合并相同key值的数据
使⽤reduceByKey函数可以对具有相同key值的数据进⾏合并。
⽐如下⾯的代码,由于RDD中存在(3,4)和(3,6)两条key值均为3的数据,他们将被合为⼀条数据:1
2
print (kvRDD1.reduceByKey(lambda x,y:x+y).collect())
输出为
1
2
[(1, 2), (3, 10), (5, 6)]
多个RDD Key-Value“转换”运算
初始化
⾸先我们初始化两个k-v的RDD:
1 2 3kvRDD1 = sc.parallelize([(3,4),(3,6),(5,6),(1,2)]) kvRDD2 = sc.parallelize([(3,8)])
内连接运算
join运算可以实现类似数据库的内连接,将两个RDD按照相同的key值join起来,kvRDD1与kvRDD2的key值唯⼀相同的是3,kvRDD1中有两条key值为3的数据(3,4)和(3,6),⽽kvRDD2中只有⼀条key值为3的数据(3,8),所以join的结果是(3,(4,8))和(3,(6,8)):
1
2
print (kvRDD1.join(kvRDD2).collect())
输出为:
1
2
[(3, (4, 8)), (3, (6, 8))]
左外连接
使⽤leftOuterJoin可以实现类似数据库的左外连接,如果kvRDD1的key值对应不到kvRDD2,就会显⽰None
1
2
print (kvRDD1.leftOuterJoin(kvRDD2).collect())
输出为:
1
2
[(1, (2, None)), (3, (4, 8)), (3, (6, 8)), (5, (6, None))]
右外连接
使⽤rightOuterJoin可以实现类似数据库的右外连接,如果kvRDD2的key值对应不到kvRDD1,就会显⽰None
1
2
print (kvRDD1.rightOuterJoin(kvRDD2).collect())
输出为:
1
2
[(3, (4, 8)), (3, (6, 8))]
删除相同key值数据
使⽤subtractByKey运算会删除相同key值得数据:
1
2
print (kvRDD1.subtractByKey(kvRDD2).collect())
结果为:
1
2
[(1, 2), (5, 6)]
Key-Value“动作”运算
读取数据
可以使⽤下⾯的⼏种⽅式读取RDD的数据:
1 2 3 4 5 6 7 8 9#读取第⼀条数据
print (kvRDD1.first())
#读取前两条数据
print (kvRDD1.take(2))
#读取第⼀条数据的key值print (kvRDD1.first()[0])
#读取第⼀条数据的value值print (kvRDD1.first()[1])
输出为:
1 2 3 4 5(3, 4) [(3, 4), (3, 6)] 3
4
按key值统计:
使⽤countByKey函数可以统计各个key值对应的数据的条数:
1
2
print (kvRDD1.countByKey().collect())
输出为:
1
2
defaultdict(<type 'int'>, {1: 1, 3: 2, 5: 1})
lookup查找运算
使⽤lookup函数可以根据输⼊的key值来查找对应的Value值:
1
2
print (kvRDD1.lookup(3))
输出为:
1
2
[4, 6]
持久化操作
spark RDD的持久化机制,可以将需要重复运算的RDD存储在内存中,以便⼤幅提升运算效率,有两个主要的函数:
持久化
使⽤persist函数对RDD进⾏持久化:
1
2
kvRDD1.persist()
在持久化的同时我们可以指定持久化存储等级:
⾸先我们导⼊相关函数:
1
2
from .storagelevel import StorageLevel
在scala中可以直接使⽤上述的持久化等级关键词,但是在中封装为了⼀个类,
StorageLevel类,并在初始化时指定⼀些参数,通过不同的参数组合,可以实现上⾯的不同存储等级。
StorageLevel类的初始化函数如下:
1 2 3 4 5 6 7 def __init__(self, useDisk, useMemory, useOffHeap, deserialized, replication=1): eDisk = useDisk
eMemory = useMemory
eOffHeap = useOffHeap
self.deserialized = deserialized
self.replication = replication
那么不同的存储等级对应的参数为:
1 2 3 4 5 6 7 8 9 10 11 12 13StorageLevel.DISK_ONLY = StorageLevel(True, False, False, False)
StorageLevel.DISK_ONLY_2 = StorageLevel(True, False, False, False, 2) StorageLevel.MEMORY_ONLY = StorageLevel(False, True, False, False) StorageLevel.MEMORY_ONLY_2 = StorageLevel(False, True, False, False, 2) StorageLevel.MEMORY_AND_DISK = StorageLevel(True, True, False, False) StorageLevel.MEMORY_AND_DISK_2 = StorageLevel(True, True, False, False, 2) StorageLevel.OFF_HEAP = StorageLevel(True, True, True, False, 1)
"""
.. note:: The following four storage level constants are deprecated in 2.0, since the records \ will always be serialized in Python.
"""
StorageLevel.MEMORY_ONLY_SER = StorageLevel.MEMORY_ONLY
14 15 16 17 18 19 20 21""".. note:: Deprecated in 2.0, use ``StorageLevel.MEMORY_ONLY`` instead.""" StorageLevel.MEMORY_ONLY_SER_2 = StorageLevel.MEMORY_ONLY_2 """.. note:: Deprecated in 2.0, use ``StorageLevel.MEMORY_ONLY_2`` instead.""" StorageLevel.MEMORY_AND_DISK_SER = StorageLevel.MEMORY_AND_DISK """.. note:: Deprecated in 2.0, use ``StorageLevel.MEMORY_AND_DISK`` instead.""" StorageLevel.MEMORY_AND_DISK_SER_2 = StorageLevel.MEMORY_AND_DISK_2 """.. note:: Deprecated in 2.0, use ``StorageLevel.MEMORY_AND_DISK_2`` instead."""
取消持久化
使⽤unpersist函数对RDD进⾏持久化:
1
2
kvRDD1.unpersist()
整理回顾
哇,有关pyspark的RDD的基本操作就是上⾯这些啦,想要了解更多的盆友们可以参照官⽹给出的官⽅⽂档:/docs/latest/api//pyspark.html#pyspark.RDD 今天主要介绍了两种RDD,基本的RDD和Key-Value形式的RDD,介绍了他们的⼏种“转换”运算和“动作”运算,整理如下:
refer:。