基于Spark的书籍推荐数据处理与分析

大数据学习路线图

【版权声明】版权所有,严禁转载,严禁用于商业用途,侵权必究。
作者:厦门大学计算机科学与技术系2023级研究生 吴聪霞
指导老师:厦门大学数据库实验室 林子雨 博士/副教授
时间:2024年6月
相关教材:林子雨、郑海山、赖永炫编著《Spark编程基础(Python版,第2版)》(访问教材官网
相关案例:基于Python语言的Spark数据处理分析案例集锦(PySpark)
数据集和代码下载:从百度网盘下载本案例数据集和代码。(提取码是ziyu)
本案例数据集来自Kaggle的书籍推荐数据集。采用pandas对数据进行清洗,采用分布式文件系统HDFS进行数据存储,采用Python编写Spark程序进行数据分析,使用Matplotlib进行数据可视化。

一、环境配置和数据集

Linux: Ubuntu16.04 及以上版本
Hadoop: 3.3.5
Spark:3.2.0
Python:3.6.4
开发工具:VScode

数据集来源:
https://www.kaggle.com/datasets/imtkaggleteam/book-recommendation-good-book-api
一共包含11127行数据,有12个属性列:
bookID — 图书的 ID
title — 书名
authors — 作者姓名
average_rating — 读者的平均评分
isbn — 国际标准书号 (ISBN) 是一种数字商业图书标识符,旨在是唯一的
isbn13 — 13 位 ISBN
language_code — 每本书的书面语言
num_pages — 主书页数
ratings_count — 收到的唯一评分数量
text_reviews_count — 该书收到的书面文本评论总数。
publication_date — 出版日期
publisher — 出版社/出版人

二、数据预处理

1.使用pandas读取csv文件

代码:导入相关的库,读取原始数据集csv文件

import pandas as pd
import numpy as np
dataFrame = pd.read_csv('Dataset/books.csv')

报错:说明存在多余的值

解决:增加error_bad_lines=False跳过有多余属性的行

输出结果:如图跳过了3350,4704,5879,8981行(数据集本身只有12列,这几行有13个属性值,跳过了就不会读取)

输出前10行的信息如下:

2.打开数据集文件发现有的列名前后有空格,这里做了空格去除操作,并查看了数据集的相关信息

代码:

输出结果:数据集一共有11123行,包含12个属性列。且属性列中有1个float64类型、5个int64,6个object类型。

3.删除空值

代码:

输出结果:删除空值后的数据集信息和原始数据集信息相同,说明原始数据集不存在空值。

4.删除重复值

代码:

输出结果:删除重复值后的数据集信息和原始数据集信息相同,说明原始数据集不存在重复值(也就是不存在重复的行)。

5.格式化时间列,由9/16/2006变成2006-9-16,并且再做一次删除空值操作

代码:

输出结果:与原始数据集相比,格式化时间后的数据集多了两个空值,这里删去了。

6.查看language_code的数据有没有异常值

代码:

输出结果:这些全部都是语言代码,例如eng代表English。无异常值。

7.将处理后的数据写入新的csv文件中

代码:

使用vscode预览数据集发现时间格式转换成功:

8.将数据上传到hdfs中

首先启动hadoop,使用jps命令查看是否启动成功。

创建input目录,将文件上传到hdfs中。如果原本有input目录可以先删除。

三、数据分析

代码:引入相关库,使用spark读取csv文件,创建dataframe,根据dataframe创建视图books。

输出结果:下图是读取数据集的前10行。

3.1 概览

1.前10本最受关注的书籍(text_reviews_count)

代码:通过spark.sql()方法执行SQL查询,构建的sql语句使用order by根据text_reviews_count(该书收到的书面文本评论总数)排序,设置DESC使其降序排列。然后使用show方法展示结果的前10行,即前10本最受关注的书籍,truncate=False表示不截断显示,长字符串也会完全显示出来,不会被缩短。最后使用DataFrame.write.csv()方法将结果保存到hdfs的result目录,设置mode='overwrite'表示如果目标文件已存在则覆盖原有的文件。

输出结果:输出了前10本书的信息。

2.前10本最长篇幅的书籍(num_pages)

代码:通过spark.sql()方法执行SQL查询,构建的sql语句使用order by根据num_pages(该书收到的书面文本评论总数)排序,设置DESC使其降序排列。然后使用show方法展示结果的前10行,即前10本最长篇幅的书籍。最后使用DataFrame.write.csv()方法将结果保存到hdfs的result目录。

输出结果:输出了前10本书的信息。

3.不同出版社出版的书籍数量

代码:通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY来根据publisher(出版社)进行分组,使用sql聚集函数COUNT计算每个分组里书的数量books_num,并使用ORDER BY根据书的数量降序排序。

输出结果:输出了20条结果信息。

4.不同语言的书籍数量

代码:通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY子句,对视图books根据language_code(语言代码)进行分组,使用sql聚集函数COUNT计算每个分组里书的数量books_num,并使用ORDER BY根据书的数量降序排序。

输出结果:输出了20条结果信息。

5.前10本最不受关注的高分书籍(评分在4.5分以上,评分人数超过1万,评论数少于200) —— 冷门高分书籍

代码:通过spark.sql()方法执行SQL查询,构建的sql语句使用了SUBSTRING_INDEX(authors, '/', 1) 来提取第一作者的信息,使用where子句设置了查询条件,使用ORDER by子句使其根据评论数升序排序。

输出结果:输出了10条书籍信息。

3.2 关系

6.出版书籍的数量与时间(年份)的关系

代码:由于books视图中书籍的出版如期包含年月日,这里首先创建了一个新的视图books_with_year,对于出版日期提取年份并创建一个新列year。然后通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY子句对视图books_with_year分组,使其根据年份分组,再使用COUNT函数计算每个分组的书籍的数量,并使用ORDER BY子句使结果按照年份升序排序。

输出结果:输出了出版书籍的数量与时间(年份)的关系前20条结果信息。

7.不同作者的书的平均评分

代码:提取第一作者创建新视图books_df_with_first_author。然后通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY对视图books_df_with_first_author进行分组,使其根据第一作者first_author进行分组,使用COUNT计算每个分组书籍的数量,使用聚集函数SUM来构造平均评分的计算(也就是平均评分×收到的唯一评分数量/收到的唯一评分数量)=(sum(average_rating*ratings_count)/sum(ratings_count)),并使用ORDER BY子句使结果根据书的数量(降序)、平均评分(降序)排序。

输出结果:输出了新视图books_df_with_first_author前10条信息。

输出了不同作者的书的平均评分的前20条结果。

8.前1000个最受关注的书籍数量与出版社的关系

代码:
根据视图books_df_with_first_author创建新视图top_1000_books_by_reviews,首先定义了一个滑动窗口WindowSpec,用于后续的排名计算。窗口按照text_reviews_count列(每本书的文本评论数量)降序排列。然后在books_df_with_first_author上新增一列rank,该列存储了每本书基于之前定义的窗口中的排名,使用rank()函数实现这一功能。最后筛选出排名列值小于等于1000的记录,并且从结果中移除rank列,因为后续分析不需要。
通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY对视图top_1000_books_by_reviews进行分组,使用COUNT函数计算每个分组的书籍数量,并使用ORDER BY使结果根据书籍数量排序。

输出结果:
视图top_1000_books_by_reviews的前10条结果。

输出了前1000个最受关注的书籍中出版社对应的书籍数量,展示了20条结果。

9.前1000个最受关注的书籍数量与语言的关系

代码:沿用前面的视图top_1000_books_by_reviews,通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY对视图进行分组,使其根据语言代码(language_code)进行分组,使用COUNT计算每个分组的书籍数量,并使用ORDER BY使结果按照书籍数量降序排序。

输出结果:输出了前1000个最受关注的书籍中语言代码对应的书籍数量。

10.不同作者的书的平均受关注程度

代码:沿用前面的视图books_df_with_first_author,通过spark.sql()方法执行SQL查询,构建的sql语句使用GROUP BY对视图books_df_with_first_author进行分组,使其根据第一作者first_author进行分组,使用COUNT计算每个分组书籍的数量,使用聚集函数SUM来构造平均受关注程度的计算(也就是评论总数/书籍数量)=(sum(text_reviews_count)/COUNT(*)),并使用ORDER BY子句使结果根据平均受关注程度(降序)、书的数量(降序)排序。

输出结果:输出了不同作者的书的平均受关注程度前20条结果。

四、数据可视化

将上一步分析的数据结果文件从hdfs复制到本地进行数据可视化。

1.前10本最受关注的书籍

代码:

输出结果:最受关注的书是Twilight。

2.前10个最长篇幅的书籍(num_pages)

代码:

输出结果:最长篇幅的书是The Complete Aubrey/Maturin Novels。

3.不同出版社出版的书籍数量,统计前50个

代码:

输出结果:出版最多书的出版社是Vintage,一共出版了318本。

4.不同语言的书籍数量

代码:

输出结果:这些书籍的书面语言大部分是eng(English)语言,一共有8907本。

5.前10本最不受关注的高分书籍(评分在4.5分以上,评分人数超过1万,评论数少于200) —— 冷门高分书籍

代码:

输出结果:冷门高分书籍中,评分最高的是Harry Potter Boxed Set Books 1-5。

6.出版书籍的数量与时间(年份)的关系

代码:

输出结果:出版书籍的数量主要集中在2000年和2020年之间,在2000年开始迅速增长。

7.不同作者的书的平均评分

代码:

输出结果:前50个结果中,不同作者的书的平均评分大多集中在4分左右。

8.前1000个最受关注的书籍数量与出版社的关系

代码:

输出结果:前10个出版社的书籍占比相差不会太远,但受关注的书籍数量最高的出版社是Penguin Books。

9.前1000个最受关注的书籍数量与语言的关系

代码:

输出结果:受关注的书籍的书面语言几乎都是eng(English),其余语言占比较低。

10.不同作者的书的平均受关注程度(sum(text_reviews_count)/COUNT(*))

代码:

输出结果:前50个结果中, Stephenie Meyer出版的书受关注程度最高。