spark使用
spark基本配置
python命令行
启动pyspark
cd /usr/local/spark
./bin/pyspark
统计文本的行数
lines = sc.textFile("file:///usr/local/spark/README.md")
lines.count()
* 在这里需要使用本地文件系统的绝对路径,因为在pyspark中,默认的根目录是localhost:9000,在这个目录下没有README.md文件,如果使用相对路径,可能会出现py4j.protocol.Py4JJavaError: An error occurred while calling z:org.apache.spark.api.python.PythonRDD.collectAndServe.
的问题
* 参考链接:https://www.cnblogs.com/yangzhang-home/p/6056133.html
配置spark,使其使用jupyter notebook
在.bashrc中添加
export PYSPARK_DRIVER_PYTHON=jupyter
export PYSPARK_DRIVER_PYTHON_OPTS=”notebook”source之后,启动pyspark即可,如果要修改启动时的文件夹,可以参考:https://www.zhihu.com/question/31600197,修改配置文件中的
notebooik_dir
即可。
RDD基础
- RDD(Resilient Distributed Dataset)是spark对数据的核心抽象,它是分布式的元素集合,在spark程序中,所有的操作都可以归类为3中:创建RDD、RDD的转化(transformation)以及RDD的行动操作(action)。
- spark在执行action的时候,才会真正地去计算
spark的程序流程
RDD的persisit方法会将该RDD对象持久化到内存中,对于可能会被重复调用的RDD对象,这种方法可以减少计算量,因此主要的流程为:
* 从外部数据创建出输入RDD
* 进行一些RDD对象的转化,同时创建一些新的RDD对象
* 对需要被重复计算的中间结果进行persist操作
* 使用行动操作(action)来触发一次计算
创建RDD
可以从外部数据集创建RDD,也可以直接在程序里对一个集合进行并行化
lines = sc.parallelize( ["first", "second"] ) print( lines.count() ) print( lines.first() )
RDD转化
- 之前使用的filter就是最常用的RDD转化操作,可以找出集合中满足条件的内容
- 转化操作可以操作任意数量的RDD
RDD执行
- 刚才在RDD转化时,其实并未真正计算,当RDD执行时,才会去计算
- count、first等都是常用的行动操作
RDD的谱系图
- RDD的谱系图是用来记录不同RDD之间的依赖关系,可以使用谱系图来实现按需要计算RDD,而非计算所有RDD,同时,当持久化的RDD的信息出现部分丢失的情况时,也会根据这种依赖关系来恢复丢失的数据。
持久化的一些说明
- 每次调用RDD的一个新的行动操作(action)时,都会从头开始计算RDD,为了减少计算量,我们可以对中间结果进行持久化。
常用的RDD转化与行动操作
针对各个元素的转化操作
- map:将一个函数应用于集合中的每个元素,将函数的返回结果作为结果RDD中的对应的值
filter:接受一个函数,将RDD中所有满足该函数的元素放入新的RDD中并返回。
nums = sc.parallelize([1,2,3,4]) mapRDD = nums.map( lambda x :x*x ) squared = mapRDD.collect() for num in squared: print( num ) filterRDD = nums.filter( lambda x : x>2 ) for num in filterRDD.collect(): print( num )
flatmap:对于输入RDD中的每个元素,会输出多个元素,最终将所有的输出元素放入新的RDD中并返回,这个最常用的就是在单词个数统计等任务中,即将文本中所有行的文本信息拆分为单词,然后再放入RDD中
lines = sc.parallelize(["hello world", "hi"]) words = lines.flatMap( lambda line : line.split(" ") ) print( words.count() ) print( words.first() )
伪集合操作
RDD对象虽然不是严格意义上的集合,但它支持一些基本的集合操作。RDD集合不具有唯一性,即RDD中的几何可能包含重复元素。针对这种现象,可以通过
distinct
操作去除重复元素,但是需要注意:这个函数的开销很大,因为它需要对所有网络数据进行数据混洗,因此尽量避免使用lines = sc.parallelize(["hello", "hi", "hello"]) disLines = lines.distinct() print( lines.count() ) print( disLines.count() )
union:将两个RDD进行合并,但是对于2个RDD中包含的相同的元素,生成的RDD会包含这些重复数据,即它不包含数据混洗的操作,因此如果有必要的话,需要通过其他方法进行去除。
intersection:找出2个RDD中相同的元素,重复的数据只会被计算一次,这会进行数据混洗,因此开销也很大
lines1 = sc.parallelize(["hello", "hi", "hello"]) lines2 = sc.parallelize(["hi", "test"]) ret = lines1.intersection( lines2 ) print( ret.count() )
- subtract:返回一个只存在于第一个RDD中耳不在第二个RDD中的所有元素组成的RDD,这也会进行数据混洗
- cartesian:求2个RDD的笛卡尔积,即返回所有的(a,b)的tuple,这个函数开销巨大。
行动操作
- reduce:接收一个函数作为参数,操作2个相同元素类型的RDD数据,并返回一个同类型的新元素
- fold:将RDD中的所有元素聚合为1个元素,它接收一个初始值参与计算,如果是加法,就是0,乘法的话,就是1。
- reduce与fold的返回值类型与RDD的元素类型都是相同的。
aggregate:它可以实现返回与RDD对象元素类型不同的元素类型,但是需要提供期待返回的类型的初始值,然后通过一个函数将RDD中的元素合并起来放入累加器。因为可能涉及到不同累加器之间的合并,因此需要提供一个函数将累加器之间两两合并。
# 求平均数 nums = sc.parallelize( range(1,10) ) sumCnt = nums.aggregate((0,0), (lambda acc, value : (acc[0]+value, acc[1]+1)), (lambda acc1, acc2: (acc1[0]+acc2[0], acc1[1]+acc2[1]))) avg = sumCnt[0] / float(sumCnt[1]) print( avg )
- collect:取出RDD中的所有元素,注意:这常常用于测试,在使用中,需要保证RDD中的所有对象能够一同被放入内存中
- take:接受一个数作为输入,返回这么多元素的对象列表,返回的顺序可能之前在RDD中的顺序不一样。
注意
- 有些转换的操作是专门针对的数值型RDD,有些是专门针对key-value型RDD,各有不同