2.8 案例:PySpark 处理数据并进行图表分

2.8 案例:PySpark 处理数据并进行图表分

【实验目的】

1.学习PySpark的一些算子

2.结合Python的一些包进行图表分析

【实验原理】

PySpark 是 Spark 为 Python 开发者提供的 API ,位于 $SPARK_HOME/bin 目录,其依赖于 Py4J。

大体用下面这张图来表示PySpark的实现机制:

https://adminmanage.ipieuvre.com/cs/doc/3/161/exper/31704857103420011301802/img/01.jpg

在python driver端,SparkContext利用Py4J启动一个JVM并产生一个JavaSparkContext。Py4J只使用在driver端,用于本地python与java SparkContext objects的通信。大量数据的传输使用的是另一个机制。

RDD在python下的转换会被映射成java环境下PythonRDD。在远端worker机器上,PythonRDD对象启动一些子进程并通过pipes与这些子进程通信,以此send用户代码和数据。

【实验环境】

Anolis8.8

Java 1.8.0

Hadoop-3.0.0

Pycharm

Eclipse-JEE 2022.03

spark 3.5.0

PySpark

IPython Notebook

【实验内容】

下面我通过PySpark对真实的数据集进行处理,并作图形来分析。首先我需要介绍下数据集以及数据处理的环境。

MovieLens数据集是由Minnesota大学的GroupLens Research Project对电影评分网站(movielens.umn.edu)收集的,数据集包含了1997年9月19日到1998年4月22日间共七个月的数据。这些数据已经被处理过了(清除了那些评分次数少于20次以及信息没有填写完整的数据)

MovieLens数据集:

MovieLens数据集,用户对自己看过的电影进行评分,分值为1~5。MovieLens包括两个不同大小的库,适用于不同规模的算法.小规模的库是943个独立用户对1682部电影作的100000次评分的数据(我是用这个小规模作数据处理和分析);通过对数据集分析,为用户预测他对其他未观看的电影的打分,将预测分值高的电影推荐给用户,认为这些电影是用户下一步感兴趣的电影。

数据集结构:

1.943个用户对1682场电影评分,评判次数为100000次,评分标准:1~5分。

2.每位用户至少评判20场电影。

3.简单地统计了用户的一些信息 (age, gender, occupation, zip)

【实验步骤】

1.打开终端,转变操作用户

su vmuser

使用下面命令新建/data/pyspark1目录。

mkdir -p /data/pyspark1

2.切换到/data/pyspark1目录下,再使用wget命令,从网址 http://pyspark1/目录下,将实验所需数据下载到linux本地/data/pyspark1目录下。

cd /data/pyspark1

wget http://pyspark1/ml-100k.zip

sudo chown -R vmuser:vmuser /data/pyspark1/ml-100k.zip

3.使用unzip命令解压ml-100k.zip文件到/data/pyspark1目录下。

unzip ml-100k.zip

4.切换到/apps/hadoop/sbin目录下,开启Hadoop。

cd /apps/hadoop/sbin

./start-all.sh

5.切换到/apps/spark/sbin目录下,开启Spark。

cd /apps/spark/sbin

./start-all.sh

6.使用jps命令查看Hadoop与Spark是否开启成功。

jps

7.在hdfs上新建/input/pyspark1/ml-100k目录。

hadoop fs -mkdir -p /input/pyspark1/ml-100k

8.将Linux本地的/data/pyspark1/ml-100k目录下的所有文件,加载到HDFS上的/input/pyspark1/ml-100k目录下。

hadoop fs -put /data/pyspark1/ml-100k/* /input/pyspark1/ml-100k/

9.双击PyCharm,点击【新建项目】

在位置输入【/home/vmuser/PycharmProjects/pyspark1】,python解释器选择3.6版本,取消自动创建main.py的对勾,然后点击【创建】

10.添加依赖包。依次点击--->【文件】-- -> 【设置...】

进入配置界面,依次选择【项目:pyspark1】---> 【项目结构】,点击右边的“【+ 添加内容根】

将/apps/spark/python/lib目录下的py4j-0.10.7-src.zip和pyspark.zip选中添加进来,点击【确定】--->【确定】

11.配置运行环境。点击左上角红框--->【运行】--->【编辑配置】

进入设置页面,点击左下角红框内容

在弹出的页面中,选择 【Python】,点击环境变量的编辑按钮

点击加号按钮(“+”),设置三个环境变量。

设置为如下所示,点击【确定】--->【确定】--->【确定】

处理一:用户年龄统计分析

12.在pyspark1下,新建【python文件】,名为:user_analyse

13.通过对用户数据处理,获得用户信息中的年龄。然后对年龄进行统计并使用Python中的图形框架Matplotlib生成柱状图,最后通过柱状图分析观看电影的观众年龄分布趋势。具体代码如下:

from pyspark import SparkContext

import os

os.environ['JAVA_HOME'] = '/apps/java'

def age_analyse(user_data):

# 用"|"分割符分割每一行的数据,然后将数据返回到user_fields

user_fields = user_data.map(lambda line: line.split("|"))

# 统计总的用户数

num_users = user_fields.map(lambda fields: fields[0]).count()

# 统计性别的种类数,distinct()函数用来去重。

num_genders = user_fields.map(lambda fields: fields[2]).distinct().count()

# 统计职位种类数

num_occupations = user_fields.map(lambda fields: fields[3]).distinct().count()

# 统计邮政编码种类数

num_zipcodes = user_fields.map(lambda fields: fields[4]).distinct().count()

# 打印统计的这些信息

print("Users: {0}, genders: {1}, occupations: {2}, ZIP codes: {3}".format(num_users, num_genders, num_occupations, num_zipcodes))

# 统计用户年龄

ages = user_fields.map(lambda x: int(x[1])).collect()

# 通过python中的matplotlib生成图表提供给分析师分析

import matplotlib.pyplot as plt

plt.hist(ages, bins=20, color='lightblue', density=True)

fig = plt.gcf()

fig.set_size_inches(16, 10)

plt.savefig('/data/output1.png')#将生成的结果图片放到了此路径下

if __name__ == "__main__":

sc = SparkContext("local[2]", "test")

# 加载HDFS上面的用户数据

user_data = sc.textFile("hdfs://localhost:9000/input/pyspark1/ml-100k/u.user")

# 打印加载的用户信息第一条

print(user_data.first())

age_analyse(user_data)

代码直接复制进去会提示缺少matplotlib包,将鼠标放在红线单词处根据提示进行安装即可

14.运行结果为:

统计的HDFS上面的所有用户信息:总共943位用户、男女两种性别、21种职位、795个不同的邮政编码。

15.查看运行结果图片

点击底部的文件管理器/data/output1.png

【文件系统】 ---> 【data】 ---> 【output1.png】

结论:通过生成的柱状图我们可以看出这些电影观众年龄段趋于青年,并且大部分用户年龄都在15到35之间。

处理二:用户职位统计分析

16.在pyspark1下,新建python文件,名为:user_jobs_analyse

17.首先对用户数据处理,获得用户信息中的职位种类以及每种职位用户个数。然后对职位进行统计并使用Python中的图形框架Matplotlib生成柱状图,最后通过柱状图分析观看电影的观众职位以及人数分布趋势。具体代码如下:

from pyspark import SparkContext

import os

os.environ['JAVA_HOME'] = '/apps/java'

def occupation_analyse(user_data):

# 用"|"分割符分割每一行的数据,然后将数据返回到user_fields

user_fields = user_data.map(lambda line: line.split("|"))

#处理职位那一列,通过类似MapReduce经典例子WordCount处理过程处理职位

count_by_occupation = user_fields.map(lambda fields: (fields[3], 1)).reduceByKey(lambda x, y: x + y).collect()

# 导入numpy模块

import numpy as np

# 获取用户职位,并作为柱状图的x轴数据显示

x_axis1 = np.array([c[0] for c in count_by_occupation])

# 获取用户的各个职位数,并作为y轴数据显示

y_axis1 = np.array([c[1] for c in count_by_occupation])

# 让x轴类别的显示按照y轴中每种职位的个数升序排序

x_axis = x_axis1[np.argsort(y_axis1)]

# y轴也是升序

y_axis = y_axis1[np.argsort(y_axis1)]

# 设置柱状图中x轴范围以及width

pos = np.arange(len(x_axis))

width = 1.0

# 将统计的职位信息使用matplotlib生成柱状图

from matplotlib import pyplot as plt

ax = plt.axes()

ax.set_xticks(pos + (width / 2))

ax.set_xticklabels(x_axis)

plt.bar(pos, y_axis, width, color='lightblue')

plt.xticks(rotation=30)

fig = plt.gcf()

fig.set_size_inches(16, 10)

plt.savefig('/data/output2.png')

if __name__ == "__main__":

sc = SparkContext("local[2]", "test")

# 加载HDFS上面的用户数据

user_data = sc.textFile("hdfs://localhost:9000/input/pyspark1/ml-100k/u.user")

# 打印加载的用户信息第一条

print(user_data.first())

#age_analyse(user_data)

occupation_analyse(user_data)

18.运行结果:

点击文件夹查看新生成的图片,

用户职位信息统计并生成柱状图/data/output2.png:

结论:从最终生成的图表中,我们可以看出电影观众大部分都是student, educator, administrator, engineer和programmer。并且student的人数领先其他职位一大截

处理三:电影发布信息统计分析

19.在pyspark1项目下,新建python文件,名为:movie_info_analyse

20.首先对用户数据处理,获得用户评价的电影发布时间信息。然后以1998年为最高年限减去电影发布的年限(数据集统计的时间为1998年)得到的值作为x轴,接着通过Python中的图形框架Matplotlib生成柱状图,最后通过柱状图分析当时电影发布时间趋势。电影信息有一些脏数据,所以需要先作处理。具体代码如下

from pyspark import SparkContext,SparkConf

import os

os.environ['JAVA_HOME'] = '/apps/java'

conf = SparkConf().setMaster("local[2]").setAppName("movie_info_analyse")

sc = SparkContext(conf=conf)

# 从HDFS中加载u.item数据

movie_data = sc.textFile("hdfs://localhost:9000/input/pyspark1/ml-100k/u.item")

# 打印第一条数据,查看数据格式

print(movie_data.first())

#统计电影总数

num_movies = movie_data.count()

print("Movies: %d" % num_movies)

# 定义函数功能为对电影数据预处理,对于错误的年限,使用1900填补

def convert_year(x):

try:

return int(x[-4:])

except:

return 1900

# 使用"|"分隔符分割每行数据

movie_fields = movie_data.map(lambda lines: lines.split("|"))

# 提取分割后电影发布年限信息,并做脏数据预处理

years = movie_fields.map(lambda fields: fields[2]).map(lambda x: convert_year(x))

# 获取那些年限为1900的电影(部分为脏数据)

years_filtered = years.filter(lambda x: x != 1900)

# 统计每个年份的电影数量

movie_counts = years_filtered.countByValue()

# 对字典按照键(年份)进行排序,确保年份是以单调递增的方式排列

sorted_years = sorted(movie_counts.keys())

sorted_counts = [movie_counts[year] for year in sorted_years]

# 将年份作为 x 轴,电影数量作为 y 轴绘制柱状图

from matplotlib import pyplot as plt

plt.bar(sorted_years, sorted_counts, color='lightblue')

plt.xlabel('Year')

plt.ylabel('Number of Movies')

plt.title('Distribution of Movie Release Years')

plt.savefig('/data/output3.png')

21.运行结果:

从HDFS上加载电影数据并打印第一条数据查看数据格式和打印的电影总数

电影发布年限统计并生成柱状图:

点击文件夹查看新生成的图片/data/output3.png,

结论:从最终生成的图表中,我们可以看出绝大多数电影发布时间都在1988-1998年之间。

处理四:用户评分统计分析

22.在pyspark1项目下,新建python文件,名为:ScoreStatistics_analyse

23.对用户数据处理,获得用户对电影的评分数,统计评分1-5的每个评分个数,然后绘制图表供分析。具体代码如下图所示:

from pyspark import SparkContext,SparkConf

import numpy as np

import os

os.environ['JAVA_HOME'] = '/apps/java'

conf=SparkConf().setMaster("local[2]").setAppName("movie_info_analyse")

sc=SparkContext(conf=conf)

# 加载HDFS上面的用户数据

user_data = sc.textFile("hdfs://localhost:9000/input/pyspark1/ml-100k/u.user")

# 用"|"分割符分割每一行的数据,然后将数据返回到user_fields

user_fields = user_data.map(lambda line: line.split("|"))

# 统计总的用户数

num_users = user_fields.map(lambda fields: fields[0]).count()

#从HDFS中加载u.item数据

movie_data = sc.textFile("hdfs://localhost:9000/input/pyspark1/ml-100k/u.item")

#统计电影总数

num_movies = movie_data.count()

#从HDFS上面加载用户评分数据

rating_data = sc.textFile("hdfs://localhost:9000/input/pyspark1/ml-100k/u.data")

print(rating_data.first())

#统计评分记录总数

num_ratings = rating_data.count()

print("Ratings: %d" % num_ratings)

#使用"\t"符分割每行数据

rating_data = rating_data.map(lambda line: line.split("\t"))

#获取每条数据中的用户评分数集合

ratings = rating_data.map(lambda fields: int(fields[2]))

#获取最大评分数

max_rating = ratings.reduce(lambda x, y: max(x, y))

#获取最小评分数

min_rating = ratings.reduce(lambda x, y: min(x, y))

#获取平均评分数

mean_rating = ratings.reduce(lambda x, y: x + y) / num_ratings

#获取评分中位数

median_rating = np.median(ratings.collect())

#每位用户平均评分

ratings_per_user = num_ratings / num_users

#每位用户评了几场电影

ratings_per_movie = num_ratings / num_movies

#打印上面这些信息

print("Min rating: %d" % min_rating)

print("Max rating: %d" % max_rating)

print("Average rating: %2.2f" % mean_rating)

print("Median rating: %d" % median_rating)

print("Average # of ratings per user: %2.2f" % ratings_per_user)

print("Average # of ratings per movie: %2.2f" % ratings_per_movie)

#获取评分数据

count_by_rating = ratings.countByValue()

#x轴的显示每个评分(1-5)

x_axis = count_by_rating.keys()

#y轴显示每个评分所占概率,总概率和为1

y_axis = np.array([float(c) for c in count_by_rating.values()])

y_axis_normed = y_axis / y_axis.sum()

pos = np.arange(len(x_axis))

width = 1.0

#使用matplotlib生成柱状图

from matplotlib import pyplot as plt2

ax = plt2.axes()

ax.set_xticks(pos + (width / 2))

ax.set_xticklabels(list(x_axis))

plt2.bar(pos, y_axis_normed, width, color='lightblue')

plt2.xticks(rotation=30)

fig = plt2.gcf()

fig.set_size_inches(16, 10)

plt2.savefig('/data/output4.png')

24.运行结果:

评分记录总数:100000,最小评分为1,最大评分为5,平均评分为3.53,评分中位数为4,平均每位用户平均评分为106.04,平均每位用户评了电影场数为59.45

用户电影评价柱状图:

点击文件夹查看新生成的图片/data/output4.png,

结论:从图中我们可以看出电影的评分大都在3-5分之间

处理五:用户总评分统计分析

25.对用户数据处理,获得用户对电影的总评分数(每位至少评价20次,评分在1-5之间)然后绘制图表供分析。具体代码如下,将代码追加到ScoreStatistics_analyse.py文件中。

#获取用户评分次数和每次评分

user_ratings_grouped = rating_data.map(lambda fields: (int(fields[0]),int(fields[2]))).groupByKey()

#用户ID以及该用户评分总数

user_ratings_byuser = user_ratings_grouped.map(lambda pair: (pair[0],len(pair[1])))

#打印5条结果

print(user_ratings_byuser.take(5))

#生成柱状图

from matplotlib import pyplot as plt3

user_ratings_byuser_local = user_ratings_byuser.map(lambda pair:pair[1]).collect()

print(user_ratings_byuser_local)

plt3.hist(user_ratings_byuser_local, bins=200, color='lightgreen',density=True)

fig = plt3.gcf()

fig.set_size_inches(16,10)

plt3.savefig('/data/output5.png')

26.运行结果为:

打印用户5条处理后的结果:

关闭上一个图,生成每位用户评分总数分布图:

点击文件夹查看新生成的图片/data/output5.png,

结论:从图中可以看出总评分在100以内的占了绝大多数。当然,100到300之间还是有一部分的。

至此,实验结束!

posted @ 2024-06-05 10:33  jhtchina  阅读(38)  评论(0编辑  收藏  举报