python 第3阶段
前言介绍
**Spark:**Apache Spark 是用于大规模数据处理的统一分析引擎。简单来说,Spark是一款分布式的计算框架,用于调度成百上千的服务器集群,计算TB、PB乃至EB级别的海量数据。
**PySpark:**Spark对python语言的支持,体现在python第三方库:PySpark上。可以作为python库进行数据处理;也可提交至Spark集群进行分布式集群计算。
基础准备
PySpark库的安装
在命令提示符程序内,输入:pip install pyspark
或:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark
构建PySpark执行环境入口对象
使用PySpark库完成数据处理,首先需要构建一个执行环境入口对象。
PySpark的执行环境入口对象是:类 SparkContext 的类对象。
# 导包 from pyspark import SparkConf,SparkContext # 创建SparkConf类对象 conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app") # 基于SparkConf类对象创建SparkContext类对象 sc = SparkContext(conf=conf) # 打印PySpark的运行版本 print(sc.version) # 停止SparkContext对象的运行(停止PySpark程序) sc.stop()
PySpark的编程模型
SparkContext类对象,是PySpark编程中一切功能的入口。
PySpark的编程,主要分为三大步骤:
- 数据输入:通过SparkContext类对象的成员方法,完成数据的读取操作,读取后得到RDD类对象
- 数据处理计算:通过RDD类对象的成员方法,完成各种数据计算的需求
- 数据输出:将处理完成后的RDD对象,调用各种成员方法完成写出文件、转换为list、元组、字典、文本文件、数据库等操作
数据输入
RDD对象
pyspark支持多种数据的输入,在输入完成后,会得到一个:RDD类的对象
RDD全称:弹性分布式数据集
pyspark针对数据的处理,都是以RDD对象作为载体,即:RDD是pyspark中数据计算的载体
- 数据存储在RDD内
- 各类数据的计算方法,也都是RDD的成员方法
- RDD的数据计算方法,返回值依旧是RDD对象(RDD迭代计算)
如何输入数据到spark(即得到RDD对象)
- 通过SparkContext的parallelize成员方法,将python数据容器转换为RDD对象
- 通过SparkContext的textFile成员方法,读取文本文件得到RDD对象
python数据容器转RDD对象
pyspark支持通过sparkcontext对象的parallelize成员方法,将:
- list
- tuple
- set
- dict
- str
转换为pyspark的RDD对象
from pyspark import SparkConf,SparkContext conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf=conf) rdd = sc.parallelize(数据容器对象) # 输出RDD的内容 print(rdd.collect())
注意:
- 字符串会被拆分出1个个的字符,存入RDD对象
- 字典仅有key会被存入RDD对象
读取文件转RDD对象
pyspark也支持通过sparkcontext入口对象,来读取文件,来构建出RDD对象
from pyspark import SparkConf,SparkContext conf = SparkConf().setMaster("local[*]").setAppName("test_spark") sc = SparkContext(conf=conf) rdd = sc.textFile(文件路径) # 打印RDD内容 print(rdd.collect())
# 演示pyspark代码加载数据,即数据输入
from pyspark import SparkConf,SparkContext
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 通过parallelize方法将python对象加载到spark内,成为RDD对象
rdd1 = sc.parallelize([1,2,3,4,5])
rdd2 = sc.parallelize((1,2,3,4,5))
rdd3 = sc.parallelize("abcdefg")
rdd4 = sc.parallelize({1,2,3,4,5})
rdd5 = sc.parallelize({"key1":"value1","key2":"value2"})
# 如果要查看RDD里面有什么内容,需要用collect()方法
print(rdd1.collect())
print(rdd2.collect())
print(rdd3.collect()) # 字符串会被拆分出1个个的字符,存入RDD对象
print(rdd4.collect())
print(rdd5.collect()) # 字典仅有key会被存入RDD对象
# 通过textfile方法,读取文件数据加载到spark内,成为RDD对象
rdd = sc.textFile("D:/python 代码/python 第1阶段/测试.txt")
print(rdd.collect())
sc.stop()
数据计算
map方法
pyspark的数据计算,都是基于RDD对象进行的,依赖RDD对象内置丰富的:成员方法(算子)
map算子
功能:map算子,是将RDD的数据一条条处理(处理的逻辑 基于map算子中接收的处理函数),返回新的RDD
语法:
rdd.map(func) # func : f:(T) -> U # f: 表示这是一个函数(方法) # (T) -> U 表示的是方法的定义: # ()表示传入参数,(T) 表示传入1个参数,()表示没有传入参数 # T 是泛型的代称,在这里表示任意类型 # U 也是泛型代称,在这里表示任意类型 # -> U 表示返回值 # (T) -> U 意思是:这是一个方法,这个方法接受一个参数传入,传入参数类型不限,返回一个返回值,返回值类型不限 # (A) -> A 意思是:这是一个方法,这个方法接受一个参数传入,传入参数类型不限,返回一个返回值,返回值和传入参数类型一致
# 演示RDD的map成员方法的使用
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 准备一个RDD
rdd = sc.parallelize([1,2,3,4,5])
# 通过map方法将全部数据都乘以10
# def func(data):
# return data * 10
rdd2 = rdd.map(lambda x:x*10)
print(rdd2.collect())
# (T) -> U 意思是:这是一个方法,这个方法接受一个参数传入,传入参数类型不限,返回一个返回值,返回值类型不限
# (A) -> A 意思是:这是一个方法,这个方法接受一个参数传入,传入参数类型不限,返回一个返回值,返回值和传入参数类型一致
# 链式调用
rdd2 = rdd.map(lambda x:x*10).map(lambda x:x+5)
print(rdd2.collect())
总结:
- map算子(成员方法)
- 接受一个处理函数,可用lambda表达式快速编写
- 对RDD内的元素逐个处理,并返回一个新的RDD
- 链式调用
- 对于返回值是新RDD的算子,可以通过链式调用的方法多次调用算子
flatMap方法
flatMap算子
功能:对RDD执行map操作,然后解除嵌套操作
- 计算逻辑和map一样
- 可以比map多出解除一层嵌套的功能
# 嵌套的list lst = [[1,2,3],[4,5,6],[7,8,9]] # 如果解除了嵌套 lst = [1,2,3,4,5,6,7,8,9]
# 演示RDD的flatMap成员方法的使用
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 准备一个RDD
rdd = sc.parallelize(["hello world 666","play sports","python is good"])
# 需求,将RDD数据里面的一个个单词提取出来
rdd2 = rdd.flatMap(lambda x:x.split(" "))
print(rdd2.collect())
reduceByKey方法
reduceByKey算子
功能:针对KV型 RDD,自动按照key分组,然后根据提供的聚合逻辑,完成组内数据(value)的聚合操作
用法:
rdd.reduceByKey(func) # func:(V,V) -> V # 接受2个传入参数(类型要一致),返回一个返回值,类型和传入要求一致
代码:
rdd = sc.parallelize([('男',99),('男',88),('女',99),('女',66)]) # 求男生和女生两个组的成绩之和 rdd1 = rdd.reduceByKey(lambda a,b:a+b) print(rdd1.collect()) # 结果 # [('男', 187), ('女', 165)]
注意:
- reduceByKey中接收的函数,只负责聚合,不理会分组
- 分组是自动
by key
来分组的
# RDD的reduceByKey成员方法使用
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 准备一个RDD
rdd = sc.parallelize([('男',99),('男',88),('女',99),('女',66)])
# 求男生和女生两个组的成绩之和
rdd1 = rdd.reduceByKey(lambda a,b:a+b)
print(rdd1.collect())
练习案例1
WordCount案例
# 练习案例:单词计数统计
# 使用学习内容,完成:
# 读取数据
# 统计文件内,单词的出现数量
# 构建执行环境入口对象
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 读取数据文件
rdd = sc.textFile("D:/python 代码/python 第3阶段/hello.txt")
# 取出全部单词
word_rdd = rdd.flatMap(lambda x:x.split(" "))
# 将所有单词都转化成二元元组,单词为key,value设置为1
word_with_one_rdd = word_rdd.map(lambda word:(word,1))
# 分组并求和
result_rdd = word_with_one_rdd.reduceByKey(lambda a,b:a+b)
# 打印输出结果
print(result_rdd.collect())
filter方法
Filter
功能:过滤想要的数据进行保留
语法:
rdd.filter(func) # func:(T) -> bool 传入1个参数进来随意类型,返回值必须是bool类型
**PS:**返回是True的数据被保留,False的数据被丢失
# 演示RDD的filter成员方法使用
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 准备一个rdd
rdd = sc.parallelize([1,2,3,4,5])
# 对rdd的数据进行过滤
rdd2 = rdd.filter(lambda num:num%2 == 0) # 偶数返回true,奇数返回false
print(rdd2.collect())
总结:
- filter算子接收一个处理函数,可用lambda快速编写
- filter算子,函数对RDD数据逐个处理,得到True的保留至返回值的RDD中(本质为:True的对应数据保留,False对应数据丢弃)
distinct方法
distinct算子
功能:对RDD数据进行去重,返回新的RDD
语法:
rdd.distinct()
# 无需传参
# 演示RDD的distinct成员方法使用
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 准备一个rdd
rdd = sc.parallelize([1,1,3,3,5,5,7,7,8,8,9,10])
# 对RDD的数据进行去重操作
rdd2 = rdd.distinct()
print(rdd2.collect())
sortBy方法
sortBy算子
功能:对RDD数据进行排序,基于你指定的排序依据
语法:
rdd.sortBy(func,ascending=False,numPartitions=1) # func:(T) -> U :告知按照rdd中的哪个数据进行排序,比如:lambda x:x[1]表示按照rdd中的第二列元素进行排序 # ascending True升序,False降序 # numPartitons: 用多少分区排序
# 演示sortBy方法
from pyspark import SparkConf,SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 读取数据文件
rdd = sc.textFile("D:/python 代码/python 第3阶段/hello.txt")
# 取出全部单词
word_rdd = rdd.flatMap(lambda x:x.split(" "))
# 将所有单词都转化成二元元组,单词为key,value设置为1
word_with_one_rdd = word_rdd.map(lambda word:(word,1))
# 分组并求和
result_rdd = word_with_one_rdd.reduceByKey(lambda a,b:a+b)
# 打印输出结果
print(result_rdd.collect())
# 对结果进行排序
final_add = result_rdd.sortBy(lambda x:x[1],ascending=False,numPartitions=1)
print(final_add.collect())
总结:sortBy算子
- 接收一个处理函数,可用lambda快速编写
- 函数表示用来决定排序的依据
- 可以控制升序或降序
- 全局排序需要设置分区为1
练习案例2
# 练习案例
# 需求,对文本内容使用Spark读取文件进行计算:
# 各个城市销售额排名,从大到小,降序
# 北京市有哪些商品类别在售卖
# D:/python 代码/python 第3阶段
from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 需求1:城市销售额排名
# 读取文件得到RDD
file_rdd = sc.textFile("D:/python 代码/python 第3阶段/orders.txt")
# 取出一个个json字符串
json_str_rdd = file_rdd.flatMap(lambda x:x.split("|"))
# 将一个个json字符串转换为字典
dict_rdd = json_str_rdd.map(lambda x:json.loads(x))
# 取出城市和销售额数据
city_with_money_rdd = dict_rdd.map(lambda x:(x['areaName'],int(x['money'])))
# 按城市分组按销售额聚合
city_result_rdd = city_with_money_rdd.reduceByKey(lambda a,b:a+b)
# 按销售额聚合结果进行排序
result1_rdd = city_result_rdd.sortBy(lambda x:x[1],ascending=False,numPartitions=1)
print("需求1的结果:",result1_rdd.collect())
# 需求2:全部城市有哪些商品类别在售卖
# 取出全部的商品类别
category_rdd = dict_rdd.map(lambda x:x['category']).distinct() # 对全部商品类别进行去重
print("需求2的结果:",category_rdd.collect())
# 需求3:北京市有哪些商品类别在售卖
# 过滤北京市的数据
beijing_dict_rdd = dict_rdd.filter(lambda x:x['areaName']=='北京')
# 取出全部商品类别
result3_rdd = beijing_dict_rdd.map(lambda x:x['category']).distinct() # 进行商品类别去重
print("需求3的结果:",result3_rdd.collect())
数据输出
输出为python对象
数据输出:
数据输入:
- sc.parallelize
- sc.textFile
数据计算:
- rdd.map
- rdd.flatMap
- rdd.reduceByKey
- …
collect算子
功能:将RDD各个分区内的数据,统一收集到Driver中,形成一个List对象
用法:
rdd.collect()
返回值是一个 list
reduce算子
功能:对RDD数据集按照传入的逻辑进行聚合
语法:
rdd.reduce(func) # func:(T,T) -> T # 2参数传入,1个返回值,返回值和参数要求类型一致
返回值等同于计算函数的返回值
take算子
功能:取RDD的前N个元素,组合成list返回给你
用法:
rdd.take(N)
count算子
功能:计算RDD有多少条数据,返回值是一个数字
用法:
rdd.count()
# 将RDD输出为python对象
from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)
# 准备RDD
rdd = sc.parallelize([1,2,3,4,5])
# collec算子,输出RDD为list对象
rdd_list:list = rdd.collect()
print(rdd_list)
print(type(rdd_list))
# reduce算子,对RDD进行两两聚合
num = rdd.reduce(lambda a,b:a+b)
print(num)
# take算子,取出RDD前N个元素,组成list返回
take_list = rdd.take(3)
print(take_list)
# count,统计rdd内有多少条数据,返回值为数字
num_count = rdd.count()
print(f"rdd内有{num_count}个元素")
sc.stop()
总结:
-
Spark的编程流程是:
- 将数据加载到RDD(数据输入)
- 对RDD进行计算(数据计算)
- 将RDD转换为python对象(数据输出)
-
数据输出的方法
- collect:将RDD内容转换为list
- reduce:对RDD内容进行自定义聚合
- take:取出RDD的前N个元素组成list
- count:统计RDD元素个数
输出到文件中
saveAsTextFile算子
功能:将RDD的数据写入文本文件中
支持本地写出,hdfs等文件系统
代码:
rdd.saveAsTextFile("文件地址")
输出的结果是一个文件夹,有几个分区就输出多少个结果文件
修改rdd分区为1个
# 方式1,SparkConf对象设置属性全局并行度为1: conf = SparkConf().setMaster("local[*]").setAppName("test_spark") conf.set("spark.default.parallelism","1") # 对所有创建rdd都有影响 sc = SparkContext(conf=conf) # 方式2,创建RDD的时候设置(parallelize方法传入numSlices参数为1),只会对当前rdd有影响 rdd1 = sc.parallelize([1,2,3,4,5],numSlices=1) # 可以带numSlices= rdd1 = sc.parallelize([1,2,3,4,5],1) # 也可不带numSlices=
# 将RDD输出到文件中
from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
os.environ['HADOOP_HOME'] = "D:/python/hadoop-3.0.0"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
# 修改rdd分区为1个 方式1:conf.set("spark.default.parallelism","1"),所有rdd均受影响
sc = SparkContext(conf=conf)
# 准备RDD1
# 修改rdd分区为1个 方式2:numSlices=1,只会对设置了的受影响
rdd1 = sc.parallelize([1,2,3,4,5],numSlices=1)
# 准备RDD2
rdd2 = sc.parallelize([("hello",3),("spark",5),("hi",7)],1)
# 准备RDD3
rdd3 = sc.parallelize([[1,3,5],[6,7,9],[11,13,11]])
# 输出到文件中
rdd1.saveAsTextFile("D:/python 代码/python 第3阶段/output1")
rdd2.saveAsTextFile("D:/python 代码/python 第3阶段/output2")
rdd3.saveAsTextFile("D:/python 代码/python 第3阶段/output3")
综合案例
搜索引擎日志分析
# 综合案例
# 读取文件转换成RDD,并完成:
# 打印输出:热门搜索时间段(小时精度)Top3
# 打印输出:热门搜索词Top3
# 打印输出:统计黑马程序员关键字在哪个时段被搜索最多
# 将数据转换为json格式,写出文件 D:\python 代码\python 第3阶段
from pyspark import SparkConf,SparkContext
import os
import json
os.environ['PYSPARK_PYTHON'] = "D:/python/python3.10.4/python.exe"
os.environ['HADOOP_HOME'] = "D:/python/hadoop-3.0.0"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
conf.set("spark.default.parallelism","1")
sc = SparkContext(conf=conf)
# 读取文件转换成RDD
file_rdd = sc.textFile("D:/python 代码/python 第3阶段/search_log.txt")
# 需求1:热门搜索时间段top3(小时精度)
# 取出全部的时间并转换为小时
# 转换为(小时,1)的二元元组
# key分组聚合value
# 排序(降序)
# 取前三
# file_rdd.map(lambda x:x.split("\t")).map(lambda x:x[0][:2]).map(lambda x:(x,1)).# 可以直接用一个map搞定
result1 = file_rdd.map(lambda x:(x.split("\t")[0][:2],1)).\
reduceByKey(lambda a,b:a+b).\
sortBy(lambda x:x[1],ascending=False,numPartitions=1).\
take(3)
print("需求1的结果:",result1)
# 需求2:热门搜索词top3
# 取出全部的搜索词
# (词,1)二元元组
# 分组聚合
# 排序
# top3
result2 = file_rdd.map(lambda x:(x.split("\t")[2],1)).\
reduceByKey(lambda a,b:a+b).\
sortBy(lambda x:x[1],ascending=False,numPartitions=1).\
take(3)
print("需求2的结果:",result2)
# 需求3:统计黑马程序员关键字在什么时段被搜索的最多
# 过滤内容,只保留黑马程序员关键词
# 转换为(小时,1)的二元元组
# key分组聚合value
# 排序(降序)
# 取前1
result3 = file_rdd.map(lambda x:x.split("\t")).\
filter(lambda x:x[2] == "黑马程序员").\
map(lambda x:(x[0][:2],1)).\
reduceByKey(lambda a,b:a+b).\
sortBy(lambda x:x[1],ascending=False,numPartitions=1).\
take(1)
print("需求3的结果:",result3)
# 需求4:将数据转换为json格式,写出到文件中
# 转换为json格式的RDD
# 写出文件
file_rdd.map(lambda x:x.split("\t")).map(lambda x:{"time":x[0],"user_id":x[1],"key_word":x[2],"rank1":x[3],"rank2":x[4],"url":x[5]}).\
saveAsTextFile("D:/python 代码/python 第3阶段/output_json")
博主声明:
本文知识点与代码皆出自黑马程序员课程
欢迎有兴趣的同学进行视频学习
链接:黑马程序员python教程