1,读取文件
from pyspark import SparkContext
sc = SparkContext('local', 'pyspark')
a,text = sc.textFile(“file:///d:/test.txt”)
b,rdd = sc.parallelize([1,2,3,4,5])
2,RDD的操作
大家还对python的list comprehension有印象吗,RDDs可以进行一系列的变换得到新的RDD,有点类似那个过程,我们先给大家提一下RDD上最最常用到的transformation:
- map() 对RDD的每一个item都执行同一个操作
- flatMap() 对RDD中的item执行同一个操作以后得到一个list,然后以平铺的方式把这些list里所有的结果组成新的list
- filter() 筛选出来满足条件的item
- distinct() 对RDD中的item去重
- sample() 从RDD中的item中采样一部分出来,有放回或者无放回
- sortBy() 对RDD中的item进行排序
如果你想看操作后的结果,可以用一个叫做collect()的action把所有的item转成一个Python list。
一个简单的例子
numbersRDD = sc.parallelize(range(1,10+1))
print(numbersRDD.collect())
squaresRDD = numbersRDD.map(lambda x: x**2) # Square every number
print(squaresRDD.collect())
filteredRDD = numbersRDD.filter(lambda x: x % 2 == 0) # Only the evens
结果
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[2, 4, 6, 8, 10]
flatMap()与Map()的关系相当于,Python中list的extend和append的关系。
前面提到的Transformation,可以一个接一个地串联,比如:
def doubleIfOdd(x):
if x % 2 == 1:
return 2 * x
else:
return x
resultRDD = (numbersRDD # In parentheses so we can write each
.map(doubleIfOdd) # transformation in one line
.filter(lambda x: x > 6)
.distinct())
resultRDD.collect()
结果:
[8, 10, 18, 14]
3,RDD间的操作
如果你手头上有2个RDD了,下面的这些操作能够帮你对他们以个种方式组合得到1个RDD:
- rdd1.union(rdd2): 所有rdd1和rdd2中的item组合
- rdd1.intersection(rdd2): rdd1 和 rdd2的交集
- rdd1.substract(rdd2): 所有在rdd1中但不在rdd2中的item(差集)
- rdd1.cartesian(rdd2): rdd1 和 rdd2中所有的元素笛卡尔乘积
如下:
numbersRDD = sc.parallelize([1,2,3])
moreNumbersRDD = sc.parallelize([2,3,4])
numbersRDD.union(moreNumbersRDD).collect()
结果:
[1, 2, 3, 2, 3, 4]
特别注意:Spark的一个核心概念是惰性计算。当你把一个RDD转换成另一个的时候,这个转换不会立即生效执行!!!
Spark会把它先记在心里,等到真的需要拿到转换结果的时候,才会重新组织你的transformations(因为可能有一连串的变换)
这样可以避免不必要的中间结果存储和通信。
刚才提到了惰性计算,那么什么东西能让它真的执行转换与运算呢? 是的,就是我们马上提到的Actions,下面是常见的action,当他们出现的时候,表明我们需要执行刚才定义的transform了:
- collect(): 计算所有的items并返回所有的结果到driver端,接着 collect()会以Python list的形式返回结果
- first(): 和上面是类似的,不过只返回第1个item
- take(n): 类似,但是返回n个item
- count(): 计算RDD中item的个数
- top(n): 返回头n个items,按照自然结果排序
- reduce(): 对RDD中的items做聚合
reduce的例子:
rdd = sc.parallelize(range(1,10+1))
rdd.reduce(lambda x, y: x + y)
结果:
55
5,针对更复杂结构的transform和action
有时候我们会遇到更复杂的结构,比如非常非常经典的是以元组形式组织的k-v对(key, value)我们把它叫做pair RDDs,而Sark中针对这种item结构的数据,定义了一些transform和action:
- reduceByKey(): 对所有有着相同key的items执行reduce操作
- groupByKey(): 返回类似(key, listOfValues)元组的RDD,后面的value List 是同一个key下面的
- sortByKey(): 按照key排序
- countByKey(): 按照key去对item个数进行统计
- collectAsMap(): 和collect有些类似,但是返回的是k-v的字典
简单的词频统计的例子:
rdd = sc.parallelize(["Hello hello", "Hello New York", "York says hello"])
resultRDD = (
rdd
.flatMap(lambda sentence: sentence.split(" ")) # split into words
.map(lambda word: word.lower()) # lowercase
.map(lambda word: (word, 1)) # count each appearance
.reduceByKey(lambda x, y: x + y) # add counts for each word
)
resultRDD.collect()
结果:
[('says', 1), ('new', 1), ('hello', 4), ('york', 2)]
若将结果以k-v字典的形式返回:
result = resultRDD.collectAsMap()
结果:
{'hello': 4, 'new': 1, 'says': 1, 'york': 2}
如果你想要出现频次最高的2个词,可以这么做:
print(resultRDD
.sortBy(keyfunc=lambda (word, count): count, ascending=False)
.take(2))
结果:
[('hello', 4), ('york', 2)]
6,在给定2个RDD后,我们可以通过一个类似SQL的方式去join他们
如:
homesRDD = sc.parallelize([
('Brussels', 'John'),
('Brussels', 'Jack'),
('Leuven', 'Jane'),
('Antwerp', 'Jill'),
])
# Quality of life index for various cities
lifeQualityRDD = sc.parallelize([
('Brussels', 10),
('Antwerp', 7),
('RestOfFlanders', 5),
])
homesRDD.join(lifeQualityRDD).collect()
结果为:
[('Antwerp', ('Jill', 7)),
('Brussels', ('John', 10)),
('Brussels', ('Jack', 10))]
homesRDD.leftOuterJoin(lifeQualityRDD).collect()
结果为:
[('Antwerp', ('Jill', 7)),
('Brussels', ('John', 10)),
('Brussels', ('Jack', 10)),
('Leuven', ('Jane', None))]