Python-PySpark案例实战:Spark介绍、库安装、编程模型、RDD对象、flat Map、reduce By Key、filter、distinct、sort By方法、分布式集群运行

版本说明

当前版本号[20230825]。

版本修改说明
20230825初版

目录

知识总览图

image-20230825160117913

PySpark案例实战

前言介绍

Spark是什么

定义:Apache Spark是用于大规模数据(large-scala data)处理的统一(unified)分析引擎

image-20230731194359814

​ 简单来说,Spark是一款分布式的计算框架,用于调度成百上千的服务器集群,计算TB、PB乃至EB级别的海量数据。

Python On Spark

Spark作为全球顶级的分布式计算框架,支持众多的编程语言进行开发。

而Python语言,则是Spark重点支持的方向。

PySpark

Spark对Python语言的支持,重点体现在,Python第三方库:PySpark之上。

PySpark是由Spark官方开发的Python语言第三方库。

Python开发者可以使用pip程序快速的安装PySpark并像其它三方库那样直接使用。

image-20230731194726283

Why PySpark

Python应用场景和就业方向是十分丰富的,其中,最为亮点的方向为:大数据开发 和 人工智能

基础准备

PySpark库的安装

同其它的Python第三方库一样,PySpark同样可以使用pip程序进行安装。

在”CMD”命令提示符程序内,输入:

pip install pyspark

或使用国内代理镜像网站(清华大学源)

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark

构建PySpark执行环境入口对象

想要使用PySpark库完成数据处理,首先需要构建一个执行环境入口对象。Spark的功能都是从SparkContext对象作为开始。

PySpark的执行环境入口对象是:类 SparkContext 的类对象

#导包
from pyspark import SparkConf, SparkContext
#创建SparkConf类对象
conf = SparkConf().setMaster("local[*]").setAppName("test_spark_app")
#这段话等同于:
#conf = SparkConf()
#conf.setMaster("local[*]")
#conf.setAppName("test_spark_app")

#基于SparkConf类对象创建SparkContext对象
sc = SparkContext(conf=conf) 
#打印PySpark的运行版本
print(sc.version)
#停止SparkContext对象的运行(停止PySpark程序)
sc.stop()

PySpark的编程模型

SparkContext类对象,是PySpark编程中一切功能的入口。

PySpark的编程,主要分为如下三大步骤:

image-20230731195219240

image-20230731195635213

•数据输入:通过SparkContext完成数据读取

•数据计算:读取到的数据转换为RDD对象,调用RDD的成员方法完成计算

•数据输出:调用RDD的数据输出相关成员方法,将结果输出到list、元组、字典、文本文件、数据库等**

数据输入

RDD对象

如图可见,PySpark支持多种数据的输入,在输入完成后,都会得到一个:RDD类的对象

RDD全称为:弹性分布式数据集(Resilient Distributed Datasets)

PySpark针对数据的处理,都是以RDD对象作为载体,即:

  • 数据存储在RDD内
  • 各类数据的计算方法,也都是RDD的成员方法
  • RDD的数据计算方法,返回值依旧是RDD对象

PySpark的编程模型(左图)可以归纳为:

  • 准备数据到RDD -> RDD迭代计算 -> RDD导出为list、文本文件等
  • 即:源数据 -> RDD -> 结果数据

Python数据容器转RDD对象

PySpark支持通过SparkContext对象的parallelize成员方法,将:

  • list
  • tuple
  • set
  • dict
  • str

转换为PySpark的RDD对象

image-20230731200607139

注意:

•字符串会被拆分出1个个的字符,存入RDD对象

•字典仅有key会被存入RDD对象

读取文件转RDD对象

PySpark也支持通过SparkContext入口对象的textFile成员方法,来读取文件构建出RDD对象。

image-20230731200956293

#演示通过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())
# print(rdd4.collect())
# print(rdd5.collect())

#用过textFile方法,读取文件数据加载到Spark内,成为RDD对象
rdd = sc.textFile("F:/bili.txt")
print(rdd.collect())

sc.stop()

数据计算

map方法

PySpark的数据计算,都是基于RDD对象来进行的,那么如何进行呢?

自然是依赖,RDD对象内置丰富的:成员方法(算子)

image-20230731201330306

image-20230731201340460

image-20230731201401219

  1. map算子(成员方法)
  • 接受一个处理函数,可用lambda表达式快速编写
  • 对RDD内的元素逐个处理,并返回一个新的RDD
  1. 链式调用
  • 对于返回值是新RDD的算子,可以通过链式调用的方式多次调用算子。

演示代码:

#演示RDD的map成员方法的使用

from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/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(func)

#print(rdd2.collect())
#(T) -> U
#(T) -> T

#链式调用
rdd3 = rdd.map(lambda x: x * 10).map(lambda x: x + 5)
print(rdd3.collect())

image-20230731201438396

flat Map方法

image-20230731201720272

image-20230731201726268

image-20230731201732286

.flatMap算子

  • 计算逻辑和map一样
  • 可以比map多出,解除一层嵌套的功能

演示代码:

#演示RDD的flatMap成员方法的使用

from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

#准备一个RDD
rdd = sc.parallelize(["pikaqiu houboshi 666", "pikaqiu jialebi hashiqi", "python iloveyou"])

#需求:将RDD数据里一个个单词提取出来
rdd2 = rdd.map(lambda x: x.split(" "))
print("rdd2.collect()内容为:", rdd2.collect())
rdd3 = rdd.flatMap(lambda x: x.split(" "))
print("rdd3.collect()内容为:", rdd3.collect())

reduce By Key方法

image-20230731201845079

image-20230731201850636

image-20230731201917052

image-20230731201925665

image-20230731201953859

  1. reduceByKey算子
  • 接受一个处理函数,对数据进行两两计算

演示代码:

#演示RDD的reduceByKey成员方法的使用

from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

#准备一个RDD
rdd = sc.parallelize([('男', 99), ('男', 88), ('女', 99), ('女', 77)])
#求男生和女生两个组的成绩之和
rdd2 = rdd.reduceByKey(lambda a, b: a+b)
print(rdd2.collect())

练习案例1

WordCount案例

使用学习到的内容,完成:

  • 读取文件
  • 统计文件内,单词的出现数量

演示代码:

#练习案例:单词计数统计
#1、构建执行环境入口对象
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

#2、读取数据文件
rdd = sc.textFile("E:/AA学习路线/Python+大数据/Python/课件/第15章资料/资料/hello.txt")
#print(rdd.collect())

#3、取出全部单词
rdd2 = rdd.flatMap(lambda x: x.split(" "))
#print(rdd2.collect())

#4、将所有单词转换成二元元组,单词为key,value设置为1
rdd3 = rdd2.map(lambda x: (x, 1))

#5、分组并求和
rdd4 =rdd3.reduceByKey(lambda a, b: a+b)

#6、输出结果
print(rdd4.collect())

filter方法

image-20230731202150540

image-20230731202157497

  1. filter算子
  • 接受一个处理函数,可用lambda快速编写
  • 函数对RDD数据逐个处理得到True的保留至返回值的RDD中

演示代码:

#演示RDD的filter成员方法的使用
#构建执行环境入口对象
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/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 x: x % 2 == 0)

#输出结果
print(rdd2.collect())

distinct方法

image-20230731202255116

image-20230731202301897

  1. distinct算子
  • 完成对RDD内数据的去重操作

演示代码:

#演示RDD的distinct成员方法的使用
#构建执行环境入口对象
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

#准备一个RDD
rdd =sc.parallelize([1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 5, 6])

#对RDD的数据进行去重
rdd2 = rdd.distinct()

#输出结果
print(rdd2.collect())

sort By方法

image-20230731202347077

image-20230731202353107

  1. sortBy算子
  • 接收一个处理函数,可用lambda快速编写
  • 函数表示用来决定排序的依据
  • 可以控制升序或降序
  • 全局排序需要设置分区数为1

演示代码:

#演示RDD的sortBy成员方法的使用
#练习案例:单词计数统计
#1、构建执行环境入口对象
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

#2、读取数据文件
rdd = sc.textFile("E:/AA学习路线/Python+大数据/Python/课件/第15章资料/资料/hello.txt")
#print(rdd.collect())

#3、取出全部单词
rdd2 = rdd.flatMap(lambda x: x.split(" "))
#print(rdd2.collect())

#4、将所有单词转换成二元元组,单词为key,value设置为1
rdd3 = rdd2.map(lambda x: (x, 1))

#5、分组并求和
rdd4 =rdd3.reduceByKey(lambda a, b: a+b)

#6、对结果进行排序(ascending中,False为降序,True为升序)
final_rdd = rdd4.sortBy(lambda x: x[1], ascending=False, numPartitions=1)

#7、输出结果
print(final_rdd.collect())

练习案例2

需求,使用Spark读取文件进行计算:

  • 各个城市销售额排名,从大到小
  • 全部城市,有哪些商品类别在售卖
  • 北京市有哪些商品类别在售卖

演示代码:

#练习案例:单词计数统计
import json
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

# 需求1:城市销售额排名
#1.1 读取文件到RDD
file_rdd = sc.textFile("E:/AA学习路线/Python+大数据/Python/课件/第15章资料/资料/orders.txt")

#1.2 取出一个个JSON字符串
json_str_rdd = file_rdd.flatMap(lambda x: x.split("|"))

#1.3 将一个个JSON字符串转换为字典
dict_rdd = json_str_rdd.map(lambda x: json.loads(x))

#1.4 取出城市和销售额数据
#(城市,销售额)
city_with_money_rdd = dict_rdd.map(lambda x: (x['areaName'], int(x['money'])))

#1.5 按城市分组和销售额聚合
city_result_rdd = city_with_money_rdd.reduceByKey(lambda a, b: a + b)

#1.6 将销售额聚合结果进行排序
result1_rdd = city_result_rdd.sortBy(lambda x: x[1], ascending=False, numPartitions=1)
print("需求1的结果:", result1_rdd.collect())

# 需求2:全部城市有哪些商品类别在售卖
#2.1 取出全部的商品类别
category_rdd = dict_rdd.map(lambda x: x['category'])

#2.2 对全部商品类别进行去重
category_rdd_distinct = category_rdd.distinct()
print("需求2的结果:", category_rdd_distinct.collect())

# 需求3:北京市有哪些商品类别在售卖
#3.1 过滤北京市的数据
beijing_data_rdd = dict_rdd.filter(lambda x: x['areaName'] == '北京')

#3.2 取出全部商品类别
beijing_rdd = beijing_data_rdd.map(lambda x: x['category'])

#3.3 进行商品类别去重
result3_rdd = beijing_rdd.distinct()
print("需求3的结果:", result3_rdd.collect())

数据输出

输出为Python对象

image-20230731202744633

collect方法

image-20230731202924274

reduce方法

image-20230731203115563

take方法

image-20230731203157760

count方法

image-20230731203225867

总结

  1. Spark的编程流程就是:
  • 将数据加载为RDD(数据输入
  • 对RDD进行计算(数据计算)
  • 将RDD转换为Python对象(数据输出
  1. 数据输出的方法
  • collect:将RDD内容转换为list
  • reduce:对RDD内容进行自定义聚合
  • take:取出RDD的前N个元素组成list
  • count:统计RDD元素个数

演示代码:

import json
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"

conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
sc = SparkContext(conf=conf)

#collect原子:输出RDD为list对象
rdd_list = sc.parallelize({1, 2, 3, 4, 5})
print(rdd_list.collect())

#reduce原子,对RDD进行两两聚合
rdd_kist = rdd_list.reduce(lambda a, b: a+b)
print(rdd_kist)

#take原子,取出RDD前N个元素,组成list返回
take_list = rdd_list.take(3)
print(take_list)

#count原子,统计rdd内有多少条数据,返回值为数字
num_count = rdd_list.count()
print(f"rdd内共有{num_count}个元素")

sc.stop()

输出到文件中

saveAsTextFile方法

image-20230731203434665

注意事项

调用保存文件的算子,需要配置Hadoop依赖

  • 下载Hadoop安装包

•http://archive.apache.org/dist/hadoop/common/hadoop-3.0.0/hadoop-3.0.0.tar.gz

  • 解压到电脑任意位置

  • 在Python代码中使用os模块配置:os.environ[‘HADOOP_HOME’] = ‘HADOOP解压文件夹路径’

  • 下载winutils.exe,并放入Hadoop解压文件夹的bin目录内

•https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop-3.0.0/bin/winutils.exe

  • 下载hadoop.dll,并放入:C:/Windows/System32 文件夹内

•https://raw.githubusercontent.com/steveloughran/winutils/master/hadoop-3.0.0/bin/hadoop.dll

修改rdd分区为1个

方式1,SparkConf对象设置属性全局并行度为1

image-20230731203639272

方式2,创建RDD的时候设置(parallelize方法传入numSlices参数为1

image-20230731203657322

总结

  1. RDD输出到文件的方法
  • rdd.saveAsTextFile(路径)
  • 输出的结果是一个文件夹
  • 有几个分区就输出多少个结果文件
  1. 如何修改RDD分区
  • SparkConf对象设置conf.set(“spark.default.parallelism”, “1”)
  • 创建RDD的时候,sc.parallelize方法传入numSlices参数为1

综合案例

搜索引擎日志分析

读取文件转换成RDD,并完成:

  • 打印输出:热门搜索时间段(小时精度)Top3
  • 打印输出:热门搜索词Top3
  • 打印输出:统计黑马程序员关键字在哪个时段被搜索最多
  • 将数据转换为JSON格式,写出为文件

代码如下:

import json
from pyspark import SparkConf, SparkContext
import os
os.environ['PYSPARK_PYTHON'] = "E:/python/python 3.11.3/python.exe"
os.environ['HADOOP_HOME'] = "E:/hadoop-3.3.1/hadoop-3.3.1"
conf = SparkConf().setMaster("local[*]").setAppName("test_spark")
conf.set("spark.default.parallelism", "1")
sc = SparkContext(conf=conf)

#读取文件转换成RDD
file_rdd = sc.textFile("E:/AA学习路线/Python+大数据/Python/课件/第15章资料/资料/search_log.txt")

#需求1 : 热门搜索时间段Top3(小时精度)
#1.1 取出全部的时间并转换为小时
#1.2 转换成(小时,1) 的二元元组
#1.3 key分组聚合value
#1.4 排序(降序)
#1.5 取前3
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
#2.1 取出全部的搜索词
#2.2 (词,1)二元元组
#2.3 分组聚合
#2.4 排序
#2.5 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 :统计黑马程序员关键字在什么时候搜索的最多
#3.1 过滤内容,只保留黑马程序员关键词
#3.2 转换为(小时,1)的二元元组
#3.3 Key分组聚合为Value
#3.4 排序(降序)
#3.5 取值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格式,写出到文件中
#4.1 转换为JSON格式的RDD
#4.2 导出为文件
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("F:/output_json")

分布式集群运行

image-20230731204111525

提交命令:

bin/spark-submit --master yarn --num-executors 3 --queue root.teach --executor-cores 4 --executor-memory 4g /home/hadoop/demo.py

image-20230731204138585

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放下华子我只抽RuiKe5

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值