PySpark实战(五)——PySpark ETL实战(包括数据的抽取、转换、加载及numpy、matplotlib、pandas的综合使用)

目录

认识资料单元格式

观察资料

折线图

直方图

多图示例

绘制3D图形

观察资料示例

选择、筛选与聚合

存储数据

Spark存储数据到SQL Server


ETL是指 Extract、Transfrom 和 Load 的简称。

用来描述将数据从数据源经过抽取、转换、加载至终端的一系列处理过程。


认识资料单元格式

在  MovieLens | GroupLens   下载一个精简示例数据集  ml-latest-small.zip

README.txt                查看一下,看看都保存什么数据

  • ratings.csv        电影评分记录
    • userId        用户ID
    • movieId        电影ID
    • rating       用户给电影的打分
    • timestamp        用户给出评价的时间戳

下载在windows

在python文件目录下打开cmd,使用   pyhon 文件名.py  运行python文件

extract01.py

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.sql.functions import to_timestamp

# 创建 sc 对象
spark = SparkSession.builder \
    .master('local[*]') \
    .appName('PySpark ETL') \
    .getOrCreate()
sc = spark.sparkContext

#
sqlContext = SQLContext(sc)

# 相对路径,文件包含标题行
# header=True 表示第一行是标题行
df = spark.read.csv('/export/data/PySpark_ETL/ml-latest-small/ratings.csv', header=True)

# 打印默认的字段类型信息
# 具体内容 & 资料单元格格式
print(df.printSchema())

# 打印前20条数据
# 观察数据
print(df.show())

# 打印总行数
# 了解数据集大小
print(df.count())

自动类型推断的抽取数据   extract02.py

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.sql.functions import to_timestamp

# 创建 sc 对象
spark = SparkSession.builder \
    .master('local[*]') \
    .appName('PySpark ETL') \
    .getOrCreate()
sc = spark.sparkContext

# inferSchema=True  自动推断数据类型
df = spark.read.csv('C:/Users/Administrator/Downloads/ml-latest-small/ml-latest-small/ratings.csv',  header=True, inferSchema=True)
print(df.printSchema())
print(df.show(5))
print(df.count())

print('\n')
df2 = spark.read.csv('C:/Users/Administrator/Downloads/ml-latest-small/ml-latest-small/tags.csv',  header=True, inferSchema=True)
print(df2.printSchema())
print(df2.show(5))
print(df2.count())

print('\n')
df3 = spark.read.csv('C:/Users/Administrator/Downloads/ml-latest-small/ml-latest-small/movies.csv',  header=True, inferSchema=True)
print(df3.printSchema())
print(df3.show(5))
print(df3.count())

print('\n')
df4 = spark.read.csv('C:/Users/Administrator/Downloads/ml-latest-small/ml-latest-small/links.csv',  header=True, inferSchema=True)
print(df4.printSchema())
print(df4.show(5))
print(df4.count())

观察资料

主要用到三个库

numpy、matplotlib、Pandas

折线图

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# 设置支持中文格式
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']

# 准备数据
t = np.arange(0.0, 2.0, 0.01)
s = 1 + np.sin(2*np.pi*t)
fig, ax = plt.subplots()

# 设置窗口标题
fig.canvas.setWindowTitle('折线图示例')

# 绘图,折线图
ax.plot(t, s)

# 坐标轴设置
ax.set(xlabel='时间(s)', ylabel='电压(mv)', title='折线图')
ax.grid()

# 当前目录下保存图片
fig.savefig('line.png')

# 显示图片
plt.show()

直方图

由一系列的高度不等的纵向条纹或线段表示 数据分布情况

一般横轴表示数据类型,纵轴表示分布情况

是数值数据分布的精确图形表示,是一个连续变量的概率分布估计

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# 设置支持中文格式
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']

# 设置随机数生成器种子参数
np.random.seed(20170907)

# 准备数据
mu = 100        # 均值
sigma = 15      # 标准差
# 随机生成 1024 个范围在[0,1]之间的数据
x = mu + sigma * np.random.randn(1024)
num_bins = 50

# 从plt上创建一个图形对象fig
fig, ax = plt.subplots()

# 设置格式
# 设置窗口标题
fig.canvas.setWindowTitle('直方图示例')

# 直方图数据
n, bins, patches = ax.hist(x, num_bins, density=1)

# 添加'best fit'线
y = ((1/(np.sqrt(2 * np.pi) * sigma)) *
     np.exp(-0.5 * (1/sigma * (bins - mu)) ** 2))

# 绘图
ax.plot(bins, y, '--')

# 坐标轴设置
ax.set_xlabel('人数')
ax.set_ylabel('概率密度')
ax.set_title(r'$\mu=100$, $\sigma=15$')

# 调整间距防止ylabel剪切
fig.tight_layout()

# 当前目录下保存
fig.savefig('histogram.png')

# 显示图片
fig.show()

多图示例

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec

# 设置支持中文
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决保存图像时负号'-'显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False

# plt.rcParams['savefig.dpi'] 参数用来设置图片质量高低
# plt.rcParams['savefig.dpi'] = 300
# plt.rcParams['figure.dpi'] = 300
# 分配 2×2 个区域进行绘图
fig, ((ax1, ax2), (ax3,ax4)) = plt.subplots(2, 2)

# 设置窗口标题
fig.canvas.setWindowTitle('多图示例')

# 第一个图绘制
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)

X, Y = np.meshgrid(x, y)
z1 = np.exp(-X**2 - Y**2)
z2 = np.exp(-(X-1)**2 - (Y-1)**2)
z = (z1-z2) * 2
CS = ax1.contour(X, Y, z)
ax1.clabel(CS, inline=1, fontsize=10)
ax1.set(title='等高线')

# 第二个图绘制
x = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
y1 = [6, 5, 8, 5, 6, 6, 8, 9, 8, 10]
y2 = [5, 3, 6, 4, 3, 4, 7, 4, 4, 6]
y3 = [4, 1, 2, 1, 2, 1, 6, 2, 3, 2]
# 柱状图
ax2.bar(x, y1, label='label11', color='red')
ax2.bar(x, y2, label='label12', color='orange')
ax2.bar(x, y3, label='label13', color='green')
ax2.set(title='柱状图', ylabel='数量', xlabel='类型')

# 第三个图绘制
w = 3
Y, X = np.mgrid[-w:w:100j, -w:w:100j]
U = -1 - X**2 + Y
V = 1 + X - Y**2
speed = np.sqrt(U**2 + V**2)
gs = gridspec.GridSpec(nrows=3, ncols=2, height_ratios=[1, 1, 2])

# streamplot
ax3.streamplot(X, Y, U, V, density=[0.5, 1])
ax3.set_title('密度')

# 第四个图绘制
np.random.seed(20170907)
N = 100
r0 = 0.6
x = 0.9 * np.random.rand(N)
y = 0.9 * np.random.rand(N)
area = (20 * np.random.rand(N)) ** 2
c = np.sqrt(area)
r = np.sqrt(x**2 + y**2)
area1 = np.ma.masked_where(r < r0, area)
area2 = np.ma.masked_where(r >= r0, area)
ax4.scatter(x, y, s=area1, marker='^', c=c)
ax4.scatter(x, y, s=area2, marker='o', c=c)
theta = np.arange(0, np.pi/2, 0.01)
ax4.plot(r0 * np.cos(theta), r0 * np.sin(theta))
ax4.set_title('分类示例')
fig.tight_layout()
fig.savefig('mulPlot.png')
plt.show()

绘制3D图形

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.gridspec as gridspec

# 支持中文,避免乱码
plt.rcParams['font.family'] = ['sans-serif']
plt.rcParams['font.sans-serif'] = ['SimHei']

# 解决保存图像时负号'-'显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False


# 洛伦兹吸引子(Lorenz attractor)用于混沌现象 —— 蝴蝶效应
def lorenz(x, y, z, s=10, r=28, b=2.667):
    x_dot = s*(y-x)
    y_dot = r*x - y - x*z
    z_dot = x*y - b*z
    return x_dot, y_dot, z_dot

dt = 0.01
num_steps = 20000
xs = np.empty(num_steps + 1)
ys = np.empty(num_steps + 1)
zs = np.empty(num_steps + 1)

# 初始值
xs[0], ys[0], zs[0] = (0., 1., 1.05)
for i in range(num_steps):
    x_dot, y_dot, z_dot = lorenz(xs[i], ys[i], zs[i])
    xs[i + 1] = xs[i] + (x_dot*dt)
    ys[i + 1] = ys[i] + (y_dot * dt)
    zs[i + 1] = zs[i] + (z_dot * dt)

# 绘制
fig = plt.figure()
plt.subplot()

# 设置窗口标题
fig.canvas.setWindowTitle('3D图')
# ax = fig.gca(projection='3d')
ax = fig.add_axes(Axes3D(fig))

# lw是linewidth缩写,线条宽度
ax.plot(xs, ys, zs, lw=1, color='purple')
ax.set_xlabel('X轴')
ax.set_ylabel('Y轴')
ax.set_zlabel('Z轴')
ax.set_title('3D图形')

fig.tight_layout()

# 当前目录下保存
fig.savefig('3d.png')
plt.show()

观察资料示例

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.sql.functions import to_timestamp

# 创建 sc 对象
spark = SparkSession.builder \
    .master('local[*]') \
    .appName('PySpark ETL') \
    .getOrCreate()
sc = spark.sparkContext

#
sqlContext = SQLContext(sc)

# 读取csv文件
df = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/ratings.csv', header=True, inferSchema=True)
print(df.show(10))
# 观察字段数据类型
print(df.printSchema())
# 将df对象中的数据进行统计分析
print(df.summary().show())

# df.select('rating').summary().show()
# 打印资料的行数量和列数量
print(df.count(), len(df.columns))

# 删除所有列的空值
dfNotNull = df.na.drop()
print(dfNotNull.count(), len(dfNotNull.columns))

# 创建视图movie
df.createOrReplaceTempView('movie')

#spark sql
# 按用户评分分组,并统计每个分组的用户评价记录数量
df2 = spark.sql('select count(*) as counter, rating from movie group by rating order by rating asc')
df2.show()


from matplotlib import pyplot as plt
# Pandas >= 0.19.2 must be installed
# 将DataFrames对象转换成Pandas对象格式
pdf = df2.toPandas()
x = pdf['rating']
y = pdf['counter']
plt.xlabel('rating')
plt.ylabel('counter')
plt.title('movie rating')

plt.bar(x, y)

# 显示数值标签
for x1, y1 in zip(x,y):
    plt.text(x1, y1+0.05, '%.0f' % y1, ha='center', va='bottom', fontsize=11)
plt.show()
sc.stop()

开启Hadoop

去/export/servers/hadoop-3.3.3/sbin(hadoop的sbin目录下)

输入 start-all.sh

上传数据文件至HDFS

cd /export/data/PySpark_ETL

hdfs dfs -put ml-latest-small

hdfs dfs -copyFromLocal /local/data /hdfs/data:将本地文件上传到 hdfs
上(原路径只能是一个文件)
hdfs dfs -put /tmp/ /hdfs/ :和 copyFromLocal 区别是,put 原路径可以是文件夹等
hadoop fs -ls / :查看根目录文件
hadoop fs -ls /tmp/data:查看/tmp/data目录
hadoop fs -cat /tmp/a.txt :查看 a.txt,与 -text 一样
hadoop fs -mkdir dir:创建目录dir
hadoop fs -rmr dir:删除目录dir

选择、筛选与聚合

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.sql.functions import from_unixtime, to_timestamp
spark = SparkSession.builder \
    .master('local[*]') \
    .appName('PySpark ETL') \
    .getOrCreate()

# 相对路径,文件包含标题行
df = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/ratings.csv', header=True)

# 转换rating类型
# 读取数据时,没有类型推断,使用df.rating.cast('double')进行类型转换
df = df.withColumn('rating', df.rating.cast('double'))
# 新增一列
df = df.withColumn('date', from_unixtime(df.timestamp.cast('bigint'), 'yyyy-MM-dd'))

df = df.withColumn('date', df.date.cast('date'))

# 删除 timestamp 列
df = df.drop('timestamp')

df2 = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/movies.csv', header=True)
# 'movieId'不明确,需要指定
df3 = df.join(df2, df.movieId == df2.movieId, 'inner') \
    .select('userId', df.movieId, 'title', 'date', 'rating')

from pyspark.sql.functions import udf

# 定义一个普通函数
def isLike(v):
    if v > 4:
        return True
    else:
        return False

# 创建udf函数
from pyspark.sql.types import BooleanType
# 创建了一个自定义函数,将普通函数 isLike 封装成 udf 函数,返回类型为 BooleanType
udf_isLike = udf(isLike, BooleanType())
# 利用函数isLike对rating值进行计算,保存到新列isLike中
df3 = df3.withColumn('isLike', udf_isLike(df3['rating']))
print(df3.show())

from pyspark.sql.functions import pandas_udf, PandasUDFType
from pyspark.sql.types import StringType
# 定义pandas udf函数,用于Grouped data
# 对函数fmerge进行修饰,表示定义了一个Pandas UDf函数
@ pandas_udf(StringType(), PandasUDFType.GROUPED_AGG)
def fmerge(v):
    return ','.join(v)
df5 = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/tags.csv', header=True)
df5 = df5.drop('timestamp')

# groupBy聚合
df7 = df5.groupBy(['userId', 'movieId']).agg(fmerge(df5['tag']))
df7 = df7.withColumnRenamed('fmerge(tag)', 'tags')

# select选择
df6 = df3.join(df7, (df3.movieId == df7.movieId) & (df3.userId == df7.userId)) \
    .select(df3.userId, df3.movieId, 'title', 'date', 'tags', 'rating', 'isLike') \
    .orderBy(['date'], acending=[0])
#filter筛选
df6 = df6.filter(df.date>'2015-10-25')
df6.show(20)
# df6.show(20, False)
spark.stop()

存储数据

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.sql.functions import from_unixtime, to_timestamp
spark = SparkSession.builder \
    .master('local[*]') \
    .appName('PySpark ETL') \
    .getOrCreate()

# 相对路径,文件包含标题行
df = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/ratings.csv', header=True).cache()

# 转换rating类型
# 读取数据时,没有类型推断,使用df.rating.cast('double')进行类型转换
df = df.withColumn('rating', df.rating.cast('double'))
# 新增一列
df = df.withColumn('date', from_unixtime(df.timestamp.cast('bigint'), 'yyyy-MM-dd'))

df = df.withColumn('date', df.date.cast('date'))

# 删除 timestamp 列
df = df.drop('timestamp')

df2 = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/movies.csv', header=True).cache()
# 'movieId'不明确,需要指定
df3 = df.join(df2, df.movieId == df2.movieId, 'inner') \
    .select('userId', df.movieId, 'title', 'date', 'rating').cache()

from pyspark.sql.functions import udf

# 定义一个普通函数
def isLike(v):
    if v > 4:
        return True
    else:
        return False

# 创建udf函数
from pyspark.sql.types import BooleanType
# 创建了一个自定义函数,将普通函数 isLike 封装成 udf 函数,返回类型为 BooleanType
udf_isLike = udf(isLike, BooleanType())
# 利用函数isLike对rating值进行计算,保存到新列isLike中
df3 = df3.withColumn('isLike', udf_isLike(df3['rating']))

from pyspark.sql.functions import pandas_udf, PandasUDFType
from pyspark.sql.types import StringType
# 定义pandas udf函数,用于Grouped data
# 对函数fmerge进行修饰,表示定义了一个Pandas UDf函数
@pandas_udf(StringType(), PandasUDFType.GROUPED_AGG)
def fmerge(v):
    return ','.join(v)
df5 = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/tags.csv', header=True).cache()
df5 = df5.drop('timestamp')
print('aaaa')
# groupBy聚合
df7 = df5.groupBy(['userId', 'movieId']).agg(fmerge(df5['tag'])).cache()
df7 = df7.withColumnRenamed('fmerge(tag)', 'tags')

# select选择
df6 = df3.join(df7, (df3.movieId == df7.movieId) & (df3.userId == df7.userId)) \
    .select(df3.userId, df3.movieId, 'title', 'date', 'tags', 'rating', 'isLike') \
    .orderBy(['date'], acending=[0]).cache()
#filter筛选
df6 = df6.filter(df.date>'2015-10-25').cache()
df6.show(20)
# df6.show(20, False)


######################################################################################
# 存储数据
# saveAsTextFile 进行数据存储时,如果目录存在,会报错
# 数据量大时,应该存在 HDFS 系统上
# 存在过多的小任务的时候,可以通过 RDD.coalesce方法,收缩合并分区,减少分区的个数,减小任务调度成本,避免Shuffle导致
df6.rdd.coalesce(1).saveAsTextFile('movie-out')

# 存储数据CSV格式
# write.format,指定基础输出数据源,字符串,数据源的名称,例如‘json’, ‘parquet’
df6.coalesce(1).write.format('csv') \
    .option('header', 'true').save('movie-out-csv')

# parquet格式
df6.write.format('parquet').save('movie-out-parquet')

# json格式
df6.coalesce(1).write.format('json').save('movie-out-json')

spark.stop()

Spark存储数据到SQL Server

import findspark
findspark.init()

from pyspark.sql import SparkSession
from pyspark.sql.context import SQLContext
from pyspark.sql.functions import from_unixtime, to_timestamp
spark = SparkSession.builder \
    .master('local[*]') \
    .appName('PySpark ETL') \
    .getOrCreate()

# 相对路径,文件包含标题行
df = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/ratings.csv', header=True).cache()

# 转换rating类型
# 读取数据时,没有类型推断,使用df.rating.cast('double')进行类型转换
df = df.withColumn('rating', df.rating.cast('double'))
# 新增一列
df = df.withColumn('date', from_unixtime(df.timestamp.cast('bigint'), 'yyyy-MM-dd'))

df = df.withColumn('date', df.date.cast('date'))

# 删除 timestamp 列
df = df.drop('timestamp')

df2 = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/movies.csv', header=True).cache()
# 'movieId'不明确,需要指定
df3 = df.join(df2, df.movieId == df2.movieId, 'inner') \
    .select('userId', df.movieId, 'title', 'date', 'rating').cache()

from pyspark.sql.functions import udf

# 定义一个普通函数
def isLike(v):
    if v > 4:
        return True
    else:
        return False

# 创建udf函数
from pyspark.sql.types import BooleanType
# 创建了一个自定义函数,将普通函数 isLike 封装成 udf 函数,返回类型为 BooleanType
udf_isLike = udf(isLike, BooleanType())
# 利用函数isLike对rating值进行计算,保存到新列isLike中
df3 = df3.withColumn('isLike', udf_isLike(df3['rating']))

from pyspark.sql.functions import pandas_udf, PandasUDFType
from pyspark.sql.types import StringType
# 定义pandas udf函数,用于Grouped data
# 对函数fmerge进行修饰,表示定义了一个Pandas UDf函数
@pandas_udf(StringType(), PandasUDFType.GROUPED_AGG)
def fmerge(v):
    return ','.join(v)
df5 = spark.read.csv('D:/Python项目/Big_Data/Spark_/ch05/ml-latest-small/ml-latest-small/tags.csv', header=True).cache()
df5 = df5.drop('timestamp')
print('aaaa')
# groupBy聚合
df7 = df5.groupBy(['userId', 'movieId']).agg(fmerge(df5['tag'])).cache()
df7 = df7.withColumnRenamed('fmerge(tag)', 'tags')

# select选择
df6 = df3.join(df7, (df3.movieId == df7.movieId) & (df3.userId == df7.userId)) \
    .select(df3.userId, df3.movieId, 'title', 'date', 'tags', 'rating', 'isLike') \
    .orderBy(['date'], acending=[0]).cache()
#filter筛选
df6 = df6.filter(df.date>'2015-10-25').cache()
df6.show(20)
# df6.show(20, False)


######################################################################################
# 存储数据
# 在对应的数据库中建表,表结构如下图所示
db_url = 'odbc:Mysql://localhost:3306;databaseName=数据库名;user=用户名;password=连接密码'
db_table = 'mm'

# overwrie重新生成表
df6.write.mode('overwrite').jdbc(db_url, db_table)

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
ETL是英文Extract、Transform、Load的缩写,它是数据仓库建设中最基础的环节之一,主要用于数据抽取、清洗、转换加载ETL的实现可以将来自不同数据源的数据进行统一处理和分析。 实现数据抽取转换的过程通常包括以下步骤: 1. 抽取数据:从不同的数据源中提取需要的数据,如关系型数据库、文件、Web服务、API等。 2. 清洗数据:对抽取数据进行清洗,如去除重复数据、空数据数据格式转换数据合并等,以确保数据的准确性和完整性。 3. 转换数据:对清洗过的数据进行转换操作,如数据结构的转换数据计算、数据聚合等。 这一步的目的是为了将不同数据源的数据统一到一个数据模式下,方便数据分析和处理。 4. 加载数据:将转换后的数据加载数据仓库中,如数据集市、数据仓库等,以供后续的分析和应用。 ETL的实现需要依赖一些工具和技术,如数据仓库工具、ETL工具、ETL脚本、SQL等。目前比较流行的ETL工具有Informatica、IBM DataStage、Microsoft SQL Server Integration Services等。使用这些工具可以实现大规模数据ETL处理和管理,提高数据处理效率和数据质量。 ETL实现数据抽取转换数据仓库建设的重要一步,通过ETL的清洗和转换操作,可以将来自不同数据源的数据整合到一个数据模型下,提高数据的一致性和准确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值