https://dblab.xmu.edu.cn/blog/2400/
环境搭建
数据预处理
本次项目使用的数据集来自知名数据网站 Kaggle 的 tmdb-movie-metadata 电影数据集,该数据集包含大约 5000 部电影的相关数据。本次实验使用数据集中有关电影的数据表 tmdb_5000_movies.csv 进行实验。数据包含以下字段:
由于数据中某些字段包含 json 数据,因此直接使用 Dataframe 进行读取会出现分割错误,所以如果要创建 Dataframe,需要先直接读取文件生成 RDD,再将 RDD 转为 Dataframe。过程中,使用 python3 中的 csv 模块对数据进行解析和转换。
为了更方便的对 csv 文件转化的 RDD 进行处理,需要首先去除csv文件的标题行。完成后,将处理好的文件 tmdb_5000_movies.csv 存储到 HDFS 上方便进一步的处理,使用下面命令将文件上传至 HDFS:
此时文件在 HDFS 上的路径为 /user/hadoop/tmdb_5000_movies.csv。之后在程序中,使用下面语句即可读取该文件:
使用 Spark 将数据转为 Dataframe
关键路径:
为了创建 Dataframe,首先需要将 HDFS 上的数据加载成 RDD,再将 RDD 转化为 Dataframe。下面代码段完成从文件到 RDD 再到 Dataframe 的转化:
下面代码段完成从文件到 RDD 再到 Dataframe 的转化:
上述代码完成 4 件事:
首先,创建 SparkSession 和 SparkContext 对象。
然后,为 RDD 转为 Dataframe 制作表头 (schema)。schema 是一个 StructType 对象,该对象使用一个 StructField 数组创建。
每一个 StructField 都代表结构化数据中的一个字段,构造 StructField 需要 3 个参数
- 字段名称
- 字段类型
- 字段是否可以为空
下面是这些对象的结构关系:
接着,开始创建用于转为 Dataframe 的 RDD。这个过程首先读取 HDFS 上的数据文件,然后为了将 RDD 转为 Dataframe,还需要将数据的每一行转为一个 Row 对象。
这个过程首先使用 csv 模块进行解析,得到一个包含每个字段的迭代器:
然后使用 next 函数将迭代器中的数据读取到数组中:
最后使用 * 将数组转为 Row 对象的构造函数参数,创建 Row 对象:
至此,moviesRdd 中每一行为一个 Row 对象。
最后,通过 SparkSession 接口 createDataframe ,使用准备好的表头 (schema) 和 RDD 创建 Dataframe:
至此完成 Dataframe 的创建。
使用 Spark 进行数据分析
下面使用通过 Spark 处理得到的 Dataframe mdf 进行数据分析,首先对数据中的主要字段单独进行分析(概览小节),然后再分析不同字段间的关系(关系小节)。
为了方便进行数据可视化,每个不同的分析,都将分析结果导出为 json 文件由 web 页面读取并进行可视化。导出直接使用下面的 save 函数:
该函数向 path 中写入 data。
下面分别介绍各个分析的生成过程。
1.概览
这个部分对数据进行整体的分析。
1. TMDb 电影中的体裁分布
从上面的数据字典描述可以看出,电影的体裁字段是一个 json 格式的数据,因此,为了统计不同体裁的电影的数量,需要首先解析 json 数据,从中取出每个电影对应的体裁数组,然后使用词频统计的方法统计不同体裁出现的频率,即可得到电影的体裁分布。
首先实现一个函数 countByJson(field) ,该函数实现解析 json 格式字段从中提取出 name 并进行词频统计的功能:
该函数返回一个 RDD,整个过程如下所示。
基于这个函数实现 countByGenres 用来生成不同体裁的电影数统计结果:
这个函数调用 countByJson 得到频率统计结果,并将其转为 json 数据格式并返回,方便进行可视化。最终函数返回数据格式如下:
接着,使用下面代码进行数据导出至 genres.json 方便之后进行可视化
2. 前 100 个常见关键词
该项分析电影关键词中出现频率最高的前一百个。由于关键词字段也是 json 格式数据,因此调用 countByJson 进行频率统计,同时对于统计结果进行降序排序并取前 100 项即可:
最终该函数返回 json 数据格式如下:
接着,使用下面代码将数据导出至 keywords.json 方便之后进行可视化
3. TMDb 中最常见的 10 种预算数
这一项探究电影常见的预算数是多少,因此需要对电影预算进行频率统计,代码如下:
首先,需要对预算字段进行过滤,去除预算为 0 的项目,然后根据预算聚合并计数,接着根据计数进行排序,并将结果导出为 json 字符串,为了统一输出,这里将 json 字符串转为 python 对象,最后取前 10 项作为最终的结果。
最终该函数返回 json 数据格式如下:
接着,使用下面代码进行数据导出至 budget.json 方便之后进行可视化
4. TMDb 中最常见电影时长 (只展示电影数大于 100 的时长)
这一项统计 TMDb 中最常见的电影时长,首先,需要过滤时长为 0 的电影,然后根据时长字段聚合并计数,接着过滤掉出现频率小于 100 的时长 (这一步是为了方便可视化,避免过多冗余信息)得到最终的结果。
最终该函数返回 json 数据格式如下:
接着,使用下面代码进行数据导出至 runtime.json 方便之后进行可视化
5. 生产电影最多的 10 大公司
这一项统计电影产出最多的 10 个公司,同样使用 countByJson 对 JSON 数据进行频率统计,然后进行降序排列取前 10 项即可。
最终该函数返回 JSON 数据格式如下:
接着,使用下面代码进行数据导出至 company_count.json 方便之后进行可视化
6. TMDb 中的 10 大电影语言
该项统计 TMDb 中出现最多的语言,与前面类似,该字段也是 JSON 数据,因此首先对每个项目进行词频统计,然后过滤掉语言为空的项目,最后排序取前十即可。
最终该函数返回 json 数据格式如下:
接着,使用下面代码进行数据导出至 language.json 方便之后进行可视化
2.关系
这个部分考虑数据之间的关系。
1. 预算与评价的关系
这部分考虑预算与评价之间的关系,因此对于每个电影,需要导出如下的数据:
[电影标题,预算,评价]
基于 Dataframe 对数据进行字段过滤即可:
这里还要注意过滤掉预算为空的数据,同时,结果只保留了投票数大于 100 的数据确保公平。
得到的数据存储在 budget_vote.json 中:
2. 发行时间与评价的关系
这部分考虑发行时间与评价之间的关系,因此对于每个电影,需要导出如下的数据:
[电影标题,发行时间,评价]
基于 Dataframe 对数据进行字段过滤即可:
这里还是要注意过滤掉发行时间为空的数据,保留投票数大于 100 的数据。
得到的数据存储在 date_vote.json 中:
3. 流行度和评价的关系
这部分考虑流行度与评价之间的关系,因此对于每个电影,需要导出如下的数据:
[电影标题,流行度,评价]
基于 Dataframe 对数据进行字段过滤即可:
同时,过滤掉流行度为 0 的数据,保留投票数大于 100 的数据。
得到的数据存储在 pop_vote.json 中:
4. 公司生产的电影平均分和数量的关系
这部分计算每个公司生产的电影数量及这些电影的平均分分布。首先,需要对数据进行过滤,去掉生产公司字段为空和评价人数小于 100 的电影,然后对于每一条记录,得到一条如下形式的记录:
[公司名,(评分,1)]
接着将所有记录的评分和计数累加,最后用总评分除以计数得到一个公司的平均评分及电影数,整个过程如下所示。
将得到的数据存储在 movies_vote.json 中:
5. 电影预算和营收的关系
这部分考虑电影的营收情况,因此对于每个电影,需要导出如下的数据:
[电影标题,预算,收入]
基于 Dataframe 对数据进行字段过滤即可:
过滤掉预算,收入为 0 的数据。
得到的数据存储在 budget_revenue.json 中:
3.整合调用
最后,将上面的过程整合起来方便进行调用,因此在 analyst.py 中添加 main 函数:
上面代码将所有的函数整合在变量 m中,然后通过循环调用上述所有方法并导出json文件。
4.完整代码
5.数据分析结果
数据可视化
数据可视化基于阿里开源的数据可视化工具 G2 实现。G2 是一套基于可视化编码的图形语法,以数据驱动,具有高度的易用性和扩展性,用户无需关注各种繁琐的实现细节,一条语句即可构建出各种各样的可交互的统计图表。下面以 TMDb 中电影的体裁分布为例说明可视化过程。
首先使用 python Web 框架 bottle 访问可视化页面方便进行 json 数据的读取。使用下面代码web.py 可以实现一个简单的静态文件读取:
bottle 对于接收到的请求进行路由
- 对于 web 服务启动目录中 static 文件夹下的文件,直接返回对应文件名的文件;
- 对于启动目录下的 html 文件,也返回对应的页面。
- 直接访问本机的 9999 端口,则返回主页。
最后,将 web 服务绑定到本机的 9999 端口。根据上面的实现,对于 web 页面 (html 文件),直接放在服务启动的目录下,对于 Spark 分析的结果,则保存在 static 目录下。
接下来实现主页文件 index.html。
每个图表通过一个 iframe 引入到主页中。对于每一个图表,主页中都包含标题和图表所在的页面的 iframe。对于 TMDb 中的体裁分布分析结果,在 genres.html 中实现,下面对该文件进行实现。