Spark
优秀博客链接:
https://blog.youkuaiyun.com/weixin_42263032/article/details/107191349
RDD
💛Spark RDD APPlication开发步骤:
1 通过findspark找到并引用pyspark
2 创建sparkContext对象
3 读取数据源,生成RDD对象
4 对RDD进行transframtion操作
5 对RDD进行actoin操作
import findspark
findspark.init()
from pyspark import SparkContext
sc = SparkContext()
read_RDD = sc.textFile(r'filePath')
🚗Action算子
- count() : 返回RDD的行数
- first() : 返回RDD中第一行数据
- take(n) : 以列表(list)形式返回前n行数据
- collect() : 以列表(list)形式返回RDD中所有行的数据
🚙 Transformation算子
- map(): 一一映射
# 示例: 将所有单词改为小写后返回
wordsRDD.map(lambda x : x.lower()).collect()
# 示例2:将sentencesRDD每一行单词的首字母都改为大写,并返回
sentencesRDD.map(lambda x : x.title()).collect()
- flatMap():一对多映射
# 示例:用各个单词的小写形式及长度构成元祖,并返回
sentencesRDD.flatMap(lambda x: [(i.lower(),len(i)) for i in x.split(' ')]).collect()
- filter():过滤
# 示例:去掉表头
firstLine = salesRDD.first()
noHeaderRDD = salesRDD.filter(lambda x : x != firstLine)
注释:获取字符串中以“2022”开头的
lambda x:x.startswith('2022')
-
并集-》
-
union():不去重
-
distinct() :去重
-
交集-》
-
intersection: 自动去重
-
差集-》
-
subtract: 不去重
-
reduceByKey()
rdd.map(lambda x:(x.split(' '),1)).reduceByKey(lambda x,y :x+y).clooect()
-
groupyKey()
-
mapValues()
#两个在一起用
from statistics import mean
rdd.map(lambda x:(x['sex'],x['score'])).groupByKey().mapValues(mean).collect()
✈️Pair RDD Transformation算子
- sortByKey()
rdd.sortByKey(keyfunc = lambda x : x.lower(),ascending=False).collect()
**(keyfunc = fn,ascending=False/True)
- join()
x = sc.parallelize([("a", 1), ("b", 4)])
y = sc.parallelize([("a", 2), ("a", 3)])
x.join(y).collect()
#获得结果:
[('a', (1, 2)), ('a', (1, 3))]
- leftOuterJoin
- rightOuterJoin
🛰Pair RDD Action算子
-
collectAsMap()
rdd = sc.parallelize([(1, 2), (3, 4),(1,4)]) # key相同时,后面key对应的value会覆盖前面的value rdd.collectAsMap() # 结果 {1: 4, 3: 4}
-
countByKey()
rdd.countByKey()
有两个方法,分别是 keys(),values(),可以取到对应的列表
使用 items(),可以得到(键,值)元组列表
🥇分区
默认分区个数与cpu逻辑处理服务器个数一直
查看分区:
- getNumPartitions()
查看分区上的数据:
- glom().collect()
如何控制分区数量?
🅰️实例化SparkContext时指定参数:
sc = SparkContext("local[2]")
# 默认改变全局配置,
# 可以使用 sc.parallelize(text,4) 指定4个分区,可以有效,但是全局不变
🅱️读取数据时:
⚔️parallelize()
list01RDD = sc.parallelize(list01, 4) # 4表示生成4个分区
list01RDD.getNumPartitions()
🗡textFie(‘path’,min_num):
通过textFile()只能指定最小分区数,无法指定具体的分区数量
◀️专门的分区Transformation算子
-
repartitioin(n):可以增加或减少(减少时建议使用
coalesce()
方法) -
coalesce(n):减少分区至n个
-
partitionBy():针对Pair RDD。将Key相同的放到同一个分区
pairRDDRe = pairRDD.partitionBy(2, lambda x : x == 'b' ) # 将key等于'b'的放在一个分区
pairRDDRe.glom().collect()
# 结果:[[('a', 1), ('c', 1), ('a', 3), ('c', 2)], [('b', 2), ('b', 4)]]
- repartitionAndSortWithPartitions(n):针对Pair RDD。根据针对Key的规则对数据进行分区,并根据Key排序
pairRDD = sc.parallelize([(1, "a"), (5, "b"), (6, "c"), (4, "e"), (2, "f"), (9, "g"), (11, "w"), (19, "x"), (15, "z")])
pairRDDRe = pairRDD.repartitionAndSortWithinPartitions(3, lambda x : x % 3, False)
pairRDDRe.glom().collect()
# 根据'3的余数'分区,分区内key降序
为啥要使用分区?
(1)数据量大时,尽量增加分区
(2)数据量小时,不要增加分区,避免cpu和内存的空分配和回收
(3)优化后续的groupBy 等过程(例如:partitionBy())
(4)决定保存的文件的数量
Shuffle
啥是Shuffle?
进行实际的Transformation操作时,一个分区中的数据被移动到多个分区中
哪些Transformation不需要Shuffle?
map,filter, flatmap, mapValues都不需要shuffle。这意味着各个partition可以并行执行(各自执行各自的操作,谁也不用等谁)。
这类Transformation被称为Narrow Transformation(窄依赖)
有哪些Transformation也需要Shuffle
Shuffle意味着无法再并行执行,需要等待所有partition都执行完毕,才能继续往下进行
这类Transformation被称为Wide Transformation(宽依赖)
reduceByKey、union、coalesce
持久化
- persist()
1).为什么要使用持久化?
每次进行Action操作,都是重新开始,即从读取数据开始,效率低下。因此,当需要对一个RDD进行多次Action操作之前,应该先进行持久化 。
2). 持久化的级别有哪些? 课本P54
StorageLevel(useDisk, useMemory, useOffHeap, deserialized, replication=1)
总而言之:可以缓存到JVM内存中、非JVM内存中、磁盘上
3). 使用建议:
尽可能使用MEMORY_ONLY。 如果内存不够大,再尝试使用MEMORY_ONLY_SER,最后考虑磁盘
-
Cache():仅仅能缓存到内存中,因此rdd.cache() 等价于 rdd.persist(MEMORY_ONLY)
-
检查点
持久化操作仍存在丢失的风险,所以可以使用checkpoint,设置一个还原点(相当于持久化磁盘)
也可以在Shuffle之后设置,从而避免还原丢失的RDD再次进行Shuffle操作
查看血统
distinctRdd = rdd.distinct()
distinctRdd.toDebugString().decode().split('\n') # 设置检查点之前,血统是这样的
设置检查点
sc.setCheckPointDir('filePath') # 设置checkpoint目录
distinctRdd.cache() # 缓存rdd,避免下句调用checkpoint时,重新计算rdd
distinctRdd.checkpoint() #设置检查点
结论:设置检查点后,就可以从检查点开始恢复某些丢失的rdd了,因此就不需要之前的血统了。即,设置检查点后将删除其对父RDD的引用。
DataFrame
DataFrame编程步骤:
step 1: 引入相关模块
step 2: 导入并创建SparkSession对象
step 3: 通过SparkSession对象读取数据源,生成DataFrame对象
step 4: 对DataFrame进行Transformation操作(有两种方式)
方式(1) 通过DataFrame API 提供的方法
方式(2) 通过Spark SQL
step 5: 对DataFrame进行Action操作
import findspark
findspark.init()
from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
Df = spark.createDataFrame(data,['id','name','score','province'],header = False,inferSchema=True)
🖤注: 在创建SparkSession对象时,也已经创建了SparkContext对象,因此,需要从SparkSession对象中获得SparkContext对象
想要使用,可以这样获得
sc = spark.sparkContext
🌩Action 操作
- show()
- head()
- tail()
- take()
- collect()
- count()
- write.csv(‘csv_path’,sep=‘#’) :保存为 csv 文件
- write.text(‘fileName’)
# text为非结构化数据,先把列合并
from pyspark.sql.functions import concat_ws
df_1_new = df_1.select(concat_ws(",", df_1.student_name, df_1.subject_name, df_1.subject_score).alias("new_col")) #合并所有列
df_1_new.coalesce(1).write.text("txt_result01")
-
**printSchema()**输出表结构
-
**colums()**输出列名
-
创建 Dataframe
通过***toDF()***方法将RDD转变到DataFrame
通过 createDataFrame 方法
通过 spark.read.json 或 spark.read.csv
从 关系型数据库创建(mysql):
spark = SparkSession \ .builder \ .config("spark.driver.extraClassPath", "mysql-connector-java-8.0.28.jar") \ .getOrCreate() dataframe_mysql = spark.read\ .format("jdbc") \ .option("url", "jdbc:mysql://localhost/chapter01") \ .option("driver", "com.mysql.jdbc.Driver") \ .option("dbtable", "student").option("user", "root") \ .option("password", "root").load() ## 需要根据自己情况对如下配置进行修改: ## dftest: 数据库名称 ## users: 数据库表名 ## root: 用户名 ## sa123456.: 密码 dataframe_mysql.show() ### pd.read_sql(), pd.read_sql_table(), pd.read_sql_query()
⬛️Transformation操作
列操作
1、选择某些列
- select()
- drop()
四种写法等价
dfSelect = df.select("co1","co2")
dfSelect = df.select(['co1','co2'])
from pysprk.sql.functoins import col
dfSelect = df.select(col("co1"),col("co2"))
dfSelect = df.select(df["co1"],df["co2"])
#选择所有列
df.select('*')
- 通过spark SQL
df.createOrReplaceTempView('view_name')
dfSql = spark.sql("select co1,co2 from view_name")
2、重命名
- withColumnRenamed
dfRenamed = dfCsvFile.withColumnRenamed('rep', 'new name').withColumnRenamed('region', 'newName area')
- selectExpr
dfCsvFile_selectCols = dfCsvFile.selectExpr("rep name", "region area")
- Spark SQL
dfCsvFile.createOrReplaceTempView('dfCsvFile_view')
spark.sql("SELECT rep AS name, region area FROM dfCsvFile_view").show()
3、添加新列
from pyspark.sql.functions import year, round, format_number
dfSelected05 = dfCsvFile.select("region", "item", round(dfCsvFile.Units * dfCsvFile.UnitCost, 2).alias('Totals'))
dfSelected01 = dfCsvFile.withColumn('Day of Year', year('OrderDate')) ### df['新列名'] = [], df.assign()
dfSelected02 = dfCsvFile.withColumn('discount', col('UnitCost') * 0.9)
4、更改列类型
from pyspark.sql.types import DateType
dfCasted = dfCsvFile.withColumn('OrderDate', dfCsvFile['OrderDate'].cast(DateType()))
dfCsvFile02 = dfCsvFile.selectExpr("cast(OrderDate as date)", "cast (Units as double)")
dfCsvFile.createOrReplaceTempView("dfCsvFile_view")
dfModifyType = spark.sql("SELECT CAST(OrderDate as date), CAST (Units as double) FROM dfCsvFile_view")
5、根据某些列排序
orderedDF02 = dfCsvFile.orderBy('OrderDate', ascending = False)
6、分组
from pyspark.sql.functions import round, mean, sum, col
dfCsvFile.groupBy('Region').count().show()
dfCsvFile.groupBy('Region').sum().show()
dfSelected.groupBy('Year').sum('Total').show()
dfSelected.groupBy('Year').agg(round(sum("Total"),2).alias("Total")).show()
行操作
1、过滤行
- filter()
- where()
dfCsvFile.filter((dfCsvFile['UnitCost'] > 5) & (dfCsvFile['UnitCost'] < 10)).show()
dfCsvFile.where((dfCsvFile['UnitCost'] > 5) & (dfCsvFile['UnitCost'] < 10)).show(
2、去重
- distinct()
- countDistinct()
3、统计
from pyspark.sql.functions import mean, max, min
dfCsvFile.select(round(mean('UnitCost'), 2)).show() ### df.mean(), df.max(), df.sum(), df.count()等
dfCsvFile.select(max('UnitCost'), min('UnitCost')).show()
缺失值
- dropna()
df.dropna(submit=['co1','co2'])
- fillna()
dfMissing.fillna(0)
两个DataFrame 之见的操作
- union():相当于堆叠
- join()
- left_outer()
- right_outer()
Spark Streaming
Spark Streaming编程步骤
step1:引入相关模块,创建SparkContext对象
step2:导入并实例化 StreamingContext(sc,time)
step3:读取流数据
方式一:以RDD读取
方式二:以DataFrame 形式
step4:进行transformation 操作
step5:进行输出操作
step6:start()
step7:控制处理时间
if ssc.awaitTerminationOrTimeOut(30) ==False
step8: stop()
import findspark
findspark.init()
from pyspark import SparkContext
sc = SparkContext()
from pyspark.streaming import StreamingContext
ssc = StreamingContext(sc,5)
inputStream = ssc.queueStream([rdd1,rdd2,rdd3])
操作
- updateStateByKey( fuc*)
针对 PairDStream,By Key操作
- window(size,splide): 设置滑动窗口
pairDStream.window(15,10).saveAsTextFiles('./window/output')
-
countByWindow(size,splide): 得到窗口中块个数
-
countByValueAndWindow(size,splide):
inputDStream.countByValueAndWindow(15, 5).pprint()
- reduceByKeyAndWindow(lambda , ,size,splide)
inputDStream.reduceByKeyAndWindow(lambda x,y: x if x > y else y ,None, 15, 5).pprint()
- textFileStream()流文件
文件修改日期必须晚于start()执行日期
textFileDStream = ssc.textFileStream('StreamingData01')
reduceDStream = textFileDStream.filter(lambda x : 'is' in x)
reduceDStream.pprint()
Graph
一、图的重要概念
图是由顶点的非空有限集和边的有限集构成,记作G=<V,E>
(1) 顶点、边
(2) 无向图、有向图
(3) 度、入度、出度
度 有向图和无向图都有;
入度和出度只有有向图存在
(4) 连通图、强连通图
连通图 针对无向图;
强连通图 针对有向图;
极大连通子图指 再加上一个点就不连通了的子图;
(5) 连通分量、强连通分量
无向图G的极大连通子图称为G的连通分量,可以存在多个;
强连通分量 针对有向图而言;
(6) 简单图、多重图、伪图
多重图指存在平行的边;
伪图指图存在自环;
二、Spark GraphFrame的编程步骤
step1、引入相关模块
step2、创建SparkSession对象
step3、创建 Vertices Frame和Edge Frame,表示图中的节点和边
step4、创建GraphFrame对象,表示图数据结构
step5、transformatoin 和 action 操作
import findspark
findspark.init()
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.config('spark.jars.packages', 'graphframes:graphframes:0.8.2-spark3.0-s_2.12') \
.getOrCreate()
# 创建顶点DataFrame实例
data_V = [('a', 'Alice', 34), ('b', 'Bob', 36), ('c', 'Charlie', 30), ('d', 'David', 29), ('e', 'Esther', 32), ('f', 'Fanny', 36), ('g', 'Gabby', 60)]
cols_V = ['id', 'name', 'age']
vertices = spark.createDataFrame(data_V, cols_V)
# 创建边DataFrame实例
data_E = [('a', 'b', 'friend'), ('b', 'c', 'follow'), ('c', 'b', 'follow'), ('f', 'c', 'follow'), ('e', 'f', 'follow'), ('e', 'd', 'friend'),
('d', 'a', 'friend'), ('a', 'e', 'friend')]
cols_E = ['src', 'dst', 'relationship']
edges = spark.createDataFrame(data_E, cols_E)
# 创建图对象
from graphframes import GraphFrame
graph = GraphFrame(vertices, edges)
三、创建图实例
(1) 创建顶点DataFrame对象:必须有id列,作为顶点的唯一标识
(2) 创建边DataFrame对象:必须有src和dst列
(3) 根据顶点和边DataFrame实例,创建图实例