数据可视化&分析实战
目录
前言
大家好✨,这里是bio🦖。点赞+关注不迷路。数据可视化在数据科学和数据分析中非常重要,例如论文中配色精美的结果图、PPT汇报中突出数据差异数据分析图等。通过可视化,我们可以直观地观察和理解数据的分布、趋势、异常值等特征。通过图表和图形,我们可以更轻松地发现数据中的模式和关联。
本文是对上一篇博客中获取的数据利用python进行可视化分析(沈腾参演电影数据获取),看完本文你将学会:
- 解决
matplotlib
不能绘制中文的问题 - 如何使用
matplotlib
包绘制柱状图、折线图、箱线图、饼图等 - 填充na的方法
- 衡量变量之间的关系以及建模
- 图(graph)可视化分析
本文所用的所有代码都在Github仓库,你可以下载在本地运行(提前安装好相关包~)。感兴趣的话可以star
我的主页。
1. 数据认知
在可视化开始之前,我们先对数据有一个基础的认识,了解我们的数据能做什么样的可视化分析,分析之后能得到什么样的结果。本文所用数据来源于博客沈腾参演电影数据获取,也可以使用积分从CSDN资源库下载下载链接。本文所用的全部代码已notebook
的格式存储在Github,建议下载使用。
首先读入文件,读入时记得使用gbk
编码方式,因为我们的数据中具有中文字符。而后将Rating_value
转换为浮点数,Year
转换为字符,便于后续绘图。
# time: 2023.07.27
# author: bio大恐龙
import pandas as pd
film_details_df = pd.read_csv('/mnt/c/Users/bio_dinosaur/Desktop/films_info.csv', encoding='gbk')
film_details_df['Rating_value'] = [float(i) for i in film_details_df['Rating_value']]
film_details_df['Year'] = [str(i) for i in film_details_df['Year']]
film_details_df.head()
然后看一下数据包含的变量,思考如何进行可视化展示~
Film_name URL Year Director Actors Genre Rating_count Rating_value IMDb Description Interesting_count Watched_count
0 超能一家人 https://movie.douban.com/subject/35228789/ 2023 宋阳 '艾伦', '沈腾', '陶慧', '张琪', '韩彦博', '白丽娜', '康晞娅', '... '喜剧', '家庭', '奇幻' 52844 4.0 tt12787014 郑前(艾伦 饰)新开发的APP被狡猾又诚实的反派乞乞科夫(沈腾 饰)盯上了。幸好郑前一家人意... 54367.0 36868.0
1 满江红 https://movie.douban.com/subject/35766491/ 2023 张艺谋 '沈腾', '易烊千玺', '张译', '雷佳音', '岳云鹏', '王佳怡', '潘斌龙'... '剧情', '喜剧', '悬疑', '古装' 993333 7.0 tt21148018 南宋绍兴年间,岳飞死后四年,秦桧率兵与金国会谈。会谈前夜,金国使者死在宰相驻地,所携密信也不... 1085283.0 71428.0
2 独行月球 https://movie.douban.com/subject/35183042/ 2022 张吃鱼 '沈腾', '马丽', '常远', '李诚儒', '黄才伦', '李嘉琦', '郝瀚', '... '喜剧', '科幻' 677450 6.6 tt14557302 人类为抵御小行星的撞击,拯救地球,在月球部署了月盾计划。陨石提前来袭,全员紧急撤离时,维修工... 815477.0 40755.0
3 四海 https://movie.douban.com/subject/35337517/ 2022 韩寒 '刘昊然', '刘浩存', '沈腾', '尹正', '乔杉', '周奇', '张宥浩', '... '喜剧', '动作', '爱情' 250478 5.3 tt14509614 在码头做摩托车特技表演顺便拉客的年轻人吴仁耀(刘昊然 饰),他多年不见的浪荡父亲吴仁腾(沈腾... NaN NaN
4 我和我的父辈 https://movie.douban.com/subject/35294995/ 2021 吴京 '吴京', '章子怡', '徐峥', '沈腾', '吴磊', '黄轩', '袁近辉', '任... '剧情' 265552 6.5 tt15465312 继2019年《我和我的祖国》、2020年《我和我的家乡》,国庆三部曲之《我和我的父辈》接棒定... NaN NaN
2. 数据可视化
2.1 解决matplotlib不能绘制中文字符的问题
如果我们没有中文字体,使用matplotlib
绘图时,中文字符将会以空方块展示出来。为了解决这一问题,首先使用下面的代码输出当前你具有的字体。(如果你能够绘制中文字符,请跳到2.2)
from matplotlib.font_manager import FontManager
mpl_fonts = set(f.name for f in FontManager().ttflist)
print('matplotlib.font_manager内的所有字体:')
for f in sorted(mpl_fonts):
print('\t' + f)
我的输出结果如下,如果含有中文字体这可以通过拼音拼出来,显然,我的字体库里面并没有中文字体(防止占用过多的篇幅,我删除了部分字体的展示,不会影响理解)。
matplotlib.font_manager内的所有字体:
DejaVu Sans
DejaVu Sans Display
STIXGeneral
STIXSizeThreeSym
STIXSizeTwoSym
cmb10
没有中文字体就需要去下载中文字体,推荐字体下载网站自由字体,里面有很多字体都是可以免费下载的,本文选择的阿里妈妈东方大楷
,下载链接为阿里妈妈东方大楷。当然你也可以下载你喜欢的字体用于图片绘制。
而后运行下列代码,得到字体存储路径。
import matplotlib
print(matplotlib.matplotlib_fname())
结果如下,我们需要到返回结果的上级目录,即/home/ouyangkang/software/anaconda3/envs/ML/lib/python3.9/site-packages/matplotlib/mpl-data/
,在该目录下找到fonts
文件夹,然后进入fonts
文件夹找到ttf
文件夹,打开ttf
文件夹,我们能看到很多以.ttf
为后缀的字体文件。将我们下载阿里妈妈东方大楷
字体中以.ttf
结尾的文件移入ttf
文件夹中。
/home/bio_dinosaur/software/anaconda3/envs/ML/lib/python3.9/site-packages/matplotlib/mpl-data/matplotlibrc
而后运行下面的代码,会返回一个目录的路径,在该目录下找到缓存的fontlist-vxxx.json
文件(xxx是数字),将其删除,而后重启vscode
或者你使用的编译器,会重新生成该文件。
matplotlib.get_cachedir()
最后再一次运行输出所有字体的代码。
from matplotlib.font_manager import FontManager
mpl_fonts = set(f.name for f in FontManager().ttflist)
print('matplotlib.font_manager内的所有字体:')
for f in sorted(mpl_fonts):
print('\t' + f)
结果如下(防止占用过多的篇幅,我删除了部分字体的展示,不会影响理解),可以看到第一个便是阿里妈妈东方大楷
。
matplotlib.font_manager内的所有字体:
Alimama DongFangDaKai
DejaVu Sans
DejaVu Sans Display
DejaVu Sans Mono
DejaVu Serif
DejaVu Serif Display
STIXGeneral
STIXNonUnicode
STIXSizeFiveSym
2.2 折线图
我们的数据中包括电影的名称以及电影的评分,可以绘制折线图或者柱状图来展示各个电影的评分,但是柱状图不适用于数据项过多的情况,当数据项过多时,柱状图可能会显得拥挤,不利于数据的清晰表达。因此,在这里我们选择折线图来展示各个电影的评分。
plt.plot()
绘制折线图,输入数据为x轴数据, y轴数据,数据格式可以是DataFrame
或者List
plt.text()
绘制文本,即下图中各个电影的分数。输入数据x轴数据, y轴数据, lable信息。其中x轴数据, y轴数据用来对应,可视化lable信息。
可以看到如果我们不对数据进行任何处理,输出折线图波动较大,不利于观察最大值与最小值(同时能够发现囧妈的“囧”字没有展示出来,说明阿里妈妈东方大楷中不包含这个字)。
import matplotlib.pyplot as plt
movie_score = film_details_df[['Film_name', 'Rating_value']]
labels = movie_score['Rating_value'].tolist()
# 图片大小
plt.figure(figsize=(18, 6))
# 设置中文字体
plt.rc("font", family='Alimama DongFangDaKai')
plt.rcParams['font.size'] = 12
plt.plot(movie_score['Film_name'], movie_score['Rating_value'])
# 字体倾斜
plt.xticks(rotation=45)
# 绘制对应的分数
for i in range(len(movie_score['Film_name'].tolist())):
plt.text(movie_score['Film_name'].tolist()[i], movie_score['Rating_value'].tolist()[i], labels[i], ha='center', va='bottom')
为了解决这一问题,我们对数据进行排序,根据得分对数据进行升序排序。其结果更加平滑,能够容易地发现《日不落酒店》是该批数据中评分最低的电影,《夏洛特烦恼》是该批数据中评分最高的电影。在这一步中,我们仅仅是使用sort_values()
就完成对数据框完成排序,不得不再次感叹pandas的强大。
import matplotlib.pyplot as plt
movie_score = film_details_df[['Film_name', 'Rating_value']].sort_values(by='Rating_value')
labels = movie_score['Rating_value'].tolist()
plt.figure(figsize=(18, 6))
# 设置中文字体
plt.rc("font", family='Alimama DongFangDaKai')
plt.rcParams['font.size'] = 12
plt.plot(movie_score['Film_name'], movie_score['Rating_value'])
plt.xticks(rotation=45)
for i in range(len(movie_score['Film_name'].tolist())):
plt.text(movie_score['Film_name'].tolist()[i], movie_score['Rating_value'].tolist()[i], labels[i], ha='center', va='bottom')
2.3 柱状图绘制
数据当中包含年份信息,可以统计每年参演电影的数量以及电影的平均分,从而分析演员参演的电影的一般在什么水平。利用pandas
的groupby()
函数将相同年份的数据合并到同一个类别中,而后对同类别中的电影评分计算平均分,电影数量。
plt.bar()
绘制柱状图,输入数据为x轴数据,这里是年份;y轴数据,这里是统计的电影数量。
plt.plot()
绘制折线图,同2.2。
plt.legend()
绘制图例,以plt.bar(), plt.plot()
中的label
为信息。
plt.title()
绘制标题。
通过解读图片可知,2020年该演员参演电影数量最多,达到了5部。同时该演员的参演电影的平均评分为5.48。在2014、2015年,这两年的电影平均分超过了7分。总体而言,该演员的参演的电影平均评分呈现下降的趋势,数量上呈现上升的趋势。
import matplotlib.pyplot as plt
year_analysis = film_details_df.groupby('Year').agg({'Rating_value':'mean', 'Film_name':'count'})
plt.bar(year_analysis.index, year_analysis['Film_name'], label='Film count', color='greenyellow')
plt.plot(year_analysis.index, year_analysis['Rating_value'], label='Averge score', color='orange')
# add score
for i in range(len(year_analysis.Rating_value)):
plt.text(year_analysis.index.tolist()[i], year_analysis['Rating_value'].tolist()[i], round(year_analysis['Rating_value'].tolist()[i], 2), ha='center', va='bottom')
# add legend
plt.legend()
# add title
plt.title('Averge Score And Count of Films Each Year')
2.4 箱线图绘制
对电影的评分对电影划分等级,按照五星档 8.5-10分、四星档 7-8.5分、三星档 6-7分、二星档 4.5-6分、一星档 2-4.5分的标准分级。这部分代码大部分是数据处理的代码,如果有问题可以留言或私信~
箱线图是一种用于显示数据分布情况的统计图表,它通过展示数据的中位数、四分位数、最大值和最小值等统计信息,帮助我们了解数据的集中趋势、离散程度以及是否存在异常值。
plt.boxplot()
绘制箱线图,输入数据是分级数据,这里的数据格式是嵌套的列表,类似于[[level_1_1, level_1_2], [...]], [...]
。
plt.plot()
绘制点图,同2.2,这里选择ro
,故不会连接且为红色的圆,alpha
是透明度的设置。
从图中可知,该演员没有五星档的电影,哪怕是人尽皆知的《夏洛特烦恼》也只有四星档。同时在一星档数据、二星档数据中数据较为离散;三星档数据、四星档数据中数据较为集中。箱线图上的透明圆点在三星档最为富集,因此该演员参演的电影评分大部分位于三星档。
def level_assignment(score):
if score >= 8.5:
return 'Five'
elif score >= 7:
return 'Four'
elif score >= 6:
return 'Three'
elif score >= 4.5:
return 'Two'
else:
return 'One'
film_details_df['Level'] = list(map(level_assignment, film_details_df['Rating_value'].tolist()))
# draw box figure
level_analysis = [film_details_df[film_details_df['Level'] == level]['Rating_value'].tolist() for level in ["One", "Two", "Three", "Four", "Five"]]
labels = ["One Star", "Two Star", "Three Star", "Four Star", "Five Star"]
colors = ['pink', 'lightblue', 'lightgreen', 'turquoise', 'mediumslateblue']
bplot = plt.boxplot(level_analysis, labels=labels, patch_artist=True, vert=True)
# fill color
for patch, color in zip(bplot['boxes'], colors):
patch.set_facecolor(color)
# add dots to box
for num, level in enumerate(["One", "Two", "Three", "Four", "Five"]):
plt.plot([num + 1]*len(film_details_df[film_details_df['Level'] == level]['Rating_value'].tolist()), film_details_df[film_details_df['Level'] == level]['Rating_value'].tolist(), 'ro', alpha=0.2)
2.5 饼图
通过电影所属类别可以判断出该演员属于什么类型的演员,例如动作演员、喜剧演员等等。这里采用饼图来可视化。饼图是一种用于展示分类数据占比关系的图表,它通过将数据分成几个扇形区域,每个扇形区域的大小表示对应类别在总体中的占比。
plt.subplots()
函数来创建一个具有特定大小和等宽等高比的子图,参数figsize
设置图片大小,参数subplot_kw
是传递给子图的关键字参数。
pli.pie()
绘制饼图,输入数据需要是一维数据。
ax.legend()
为子图添加图例,输入数据同样为一维数据。
通过对饼图的可视化可知,该演员参演的大部分电影类型为喜剧,其次分别是剧情和爱情。基于获取的数据可以将该演员定义为喜剧演员。
import re
genre_dic = {}
total_num = 0
for i in film_details_df.Genre:
genre = re.sub('\'', '', i)
genre = [k.strip() for k in genre.split(',')]
for g in genre:
genre_dic[g] = genre_dic.get(g, 0) + 1
total_num += 1
fig, ax = plt.subplots(figsize=(12,6), subplot_kw=dict(aspect='equal'))
wages, text=plt.pie((pd.DataFrame(genre_dic.items(), columns=['Genre', 'Count']).Count / total_num).tolist(),
labels=pd.DataFrame(genre_dic.items(), columns=['Genre', 'Count']).Genre,
)
ax.legend(wages, pd.DataFrame(genre_dic.items(), columns=['Genre', 'Count']).Genre, loc='center left', bbox_to_anchor=(1, 0, 0.5, 1))
同时,本文还给出了绘制升级版饼图的代码,简称为饼图plus(大饼)。可以自己尝试去解读一下代码~,遇到不会的可以print()
看看到底是什么妖魔鬼怪。饼图plus参考连接。
import numpy as np
fig, ax = plt.subplots(figsize=(24, 12), subplot_kw=dict(aspect="equal"))
wedges, texts = ax.pie(pd.DataFrame(genre_dic.items(), columns=['Genre', 'Count']).Count.tolist(),
wedgeprops=dict(width=0.5), startangle=-40)
bbox_props = dict(boxstyle='square, pad=0.3', fc='w', ec='k', lw=0.72)
kw = dict(arrowprops=dict(arrowstyle='-'),
bbox=bbox_props,
zorder=0,
va='center')
for i, p in enumerate(wedges):
ang = (p.theta2 - p.theta1) / 2 + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
horizontalaligment = {-1:'right', 1:'left'}[int(np.sign(x))]
connectionstyle = f"angle, angleA=0, angleB={ang}"
kw['arrowprops'].update({'connectionstyle':connectionstyle})
ax.annotate(pd.DataFrame(genre_dic.items(), columns=['Genre', 'Count']).Genre.tolist()[i],
xy=(x, y),
xytext=(1.35*np.sign(x), 1.4*y),
horizontalalignment=horizontalaligment,
**kw)
plt.show()
3. Na值处理及相关性分析
3.1 相关性分析
在数据获取阶段,部分数据存在缺失的情况。如何处理缺失数据是数据处理一直一来面临的问题,最简单同时也是最容易的操作方法是将缺失值填充为平均数、中位数之类的数据。但是这种方式并没有考虑不同变量之间的相关性,例如一部电影评分为9.0分,这部电影的评论人数为10万,另一部电影的评分为6.0分,评论人数为5万,但缺失评论人数的电影评分为5.3分,其评论人数填充为平均值7.5万,显然是不合理的操作。
因此,可以考虑使用模型预测缺失值。这里首先将数据转换为浮点数,便于后续模型的拟合与预测。同时将数据划分为不含缺失值部分、含缺失值部分。使用seaborn
库中的pairplot()
函数绘制不含缺失值部分数据两两变量之间的相关性。在本文中缺失的数据为Interesting_count
、Watched_count
。
从绘制的图中可以看出Interesting_count
与Rating_count
、Rating_value
之间相关性较好;而Watched_count
与Rating_count
、Rating_value
之间相关性较差。因此可以考虑使用线性模型拟合Interesting_count
,用非线性模型拟合Watched_count
。
# data pre-procession
fit_data = film_details_df[['Rating_count', 'Rating_value', 'Interesting_count', 'Watched_count']]
# transform data format from str to float
fit_data['Rating_count'] = list(map(float, fit_data['Rating_count'].tolist()))
fit_data['Interesting_count'] = list(map(float, fit_data['Interesting_count'].tolist()))
fit_data['Watched_count'] = list(map(float, fit_data['Watched_count'].tolist()))
# split data by Na
known_data = fit_data.dropna(subset=['Interesting_count', 'Watched_count'])
unknown_data = fit_data[fit_data.isna().any(axis=1)]
# visualization
import seaborn as sns
# correlation analysis
sns.pairplot(known_data, kind="reg")
如果你对于上诉相关性的分析不是很了解,可以从下图中更好了解两两变量之间的相关性。如Rating_value
与Interesting_count
之间的相关性是0.68,Rating_value
与Watched_count
之间的相关性仅为0.33。与上诉相关性描述一致。
这里首先使用pandas
中的corr()
计算变量之间的相关性,而后使用seaborn
的heatmap()
绘制热图。同时热图是一种很好展示相关性的可视化方法~
sns.heatmap(known_data.corr(), annot=True, fmt='.2f', cmap=sns.cubehelix_palette(as_cmap=True), center=0)
3.2 Na值处理
如3.1所述,本文使用线性模型拟合Interesting_count
,用非线性模型拟合Watched_count
。这里采用多元线性模型以及支持向量机模型分别拟合两个变量。所采用的包为sklearn
,一个强大机器学习包,只需要简单的使用fit()
、predict()
就可以完成数据的拟合与预测。这里就不多做介绍,感兴趣的小伙伴可以关注或订阅我的专栏机器学习(持续更新中~)。
#################### linear model ####################
from sklearn import linear_model
# interesting count model
reg_interesting = linear_model.LinearRegression()
reg_interesting.fit(known_data[['Rating_count', 'Rating_value']], known_data['Interesting_count'])
# slope
print(reg_interesting.coef_)
# intercept
print(reg_interesting.intercept_)
# fill na
unknown_data['Interesting_count'] = list(reg_interesting.predict(unknown_data[['Rating_count', 'Rating_value']]))
#################### svm model ####################
from sklearn.svm import SVR
# from sklearn.metrics import mean_squared_error
x_train = known_data[['Rating_count', 'Rating_value', 'Interesting_count']]
y_train = known_data['Watched_count']
svr_model = SVR(kernel='linear').fit(x_train, y_train)
# fill na
unknown_data['Watched_count'] = svr_model.predict(unknown_data[['Rating_count', 'Rating_value', 'Interesting_count']])
非线性模型不可以可视化,由于数据不多,不太好反映预测数据的准确性,当然这里这是将它作为一个示例来介绍使用模型预测缺失值的方法。但是线性模型可以可视化,将真实数据画为点,预测数据绘制为线条,可视化透明区带作为0.95的置信区间。从图中可知仅仅只有两个点在0.95置信区间之外,说明线性模型拟合的非常好~
#################### linear model visualization ####################
import numpy as np
import scipy.stats as stats
coef = reg_interesting.coef_
intercept = reg_interesting.intercept_
x_test = known_data[['Rating_count', 'Rating_value']]
y_pred = np.dot(x_test, coef) + intercept
y_test = known_data['Interesting_count']
# confidence area
confidence = 0.95
alpha = 1 - confidence
residuals = y_test - y_pred
std_err = np.std(residuals, ddof=1)
margin_of_error = std_err * stats.t.ppf(1 - alpha / 2, df=len(y_pred) - 1)
confidence_lower = y_pred - margin_of_error
confidence_upper = y_pred + margin_of_error
sorted_indices = np.argsort(y_pred)
y_pred_sorted = y_pred[sorted_indices]
confidence_lower_sorted = confidence_lower[sorted_indices]
confidence_upper_sorted = confidence_upper[sorted_indices]
plt.scatter(y_pred_sorted, np.array(y_test)[sorted_indices], color='blue', label='Actual')
plt.plot([min(y_pred_sorted), max(y_pred_sorted)], [min(y_test), max(y_test)], color='red', linestyle='--', linewidth=2, label='Predicted')
plt.fill_between(y_pred_sorted, confidence_lower_sorted, confidence_upper_sorted, color='blue', alpha=0.2, label='95% Confidence Interval')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.legend()
plt.show()
4. 图(graph)可视化分析
图是指以图(或网络)的形式来表示和组织的数据,并不是我们平常所理解的图片。在图数据中,数据元素被表示为节点(vertices)和边(edges),它们之间的关系用边来连接。 图数据能够自然地表示实体之间的复杂关系,如社交网络中的朋友关系、物流网络中的运输路径等。
在本文中,使用Fruchterman-Reingold
算法来展示演员-(联系)-演员
关系(软件:Gephi
),使用Neo4j
可视化导演-(导演)>电影-(包括)>演员
,Fruchterman-Reingold
(简称FR)是一种用于图布局的算法,用于在平面上绘制一个图的节点和边,以便节点之间的连接关系能够以一种视觉上易于理解的方式呈现出来。
下面是数据处理的部分,处理方式比较单一,相当于为数据添加了一个唯一编号,这个编号就代表的这个数据。不详细介绍~,如有问题可以留言或私信交流。
import pandas as pd
film_details_df = pd.read_csv('/mnt/c/Users/ouyangkang/Desktop/films_info.csv', encoding='gbk')
import re
import random
# set seed to reproduce result
random.seed(1026)
#################### actor procession ####################
actor_list = []
# remove blank and single quotes
for i in film_details_df.Actors:
actors = re.sub(r'\'', '', i).split(',')
actor_list += [i.strip(' ') for i in actors]
# remove duplication
actor_list = list(set(actor_list))
# create unicode
ac_dict = {}
uni_code = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
for actor in actor_list:
ac_dict[actor] = 'AC' + str(random.randint(100, 999)) + random.choice(uni_code) + str(random.randint(10, 99))
# checking whether unicodes are unique
print(len(set(ac_dict.values())) == len(actor_list))
# save
pd.DataFrame(ac_dict.items(), columns=['Actor', 'Unicode']).to_csv('/mnt/c/Users/ouyangkang/Desktop/actor_unicode.csv', index=None, encoding='gbk')
#################### director processiong ####################
dire_list = list(set(film_details_df.Director.tolist()))
random.seed(1026)
dire_dict = {}
# create unicode
for director in dire_list:
dire_dict[director] = 'DI' + str(random.randint(100, 999)) + random.choice(uni_code) + str(random.randint(10, 99))
# checking whether unicodes are unique
print(len(set(dire_dict.values())) == len(dire_list))
# save
pd.DataFrame(dire_dict.items(), columns=['Director', 'Unicode']).to_csv('/mnt/c/Users/ouyangkang/Desktop/director_unicode.csv', index=None, encoding='gbk')
#################### film processiong ####################
film_dict = {}
for film in film_details_df.Film_name.tolist():
film_dict[film] = 'Film' + str(random.randint(100, 999)) + random.choice(uni_code) + str(random.randint(10, 99))
print(len(set(film_dict.values())) == len(film_details_df.Film_name.tolist()))
pd.DataFrame(film_dict.items(), columns=['Film', 'Unicode']).to_csv('/mnt/c/Users/ouyangkang/Desktop/film_unicode.csv', index=None, encoding='gbk')
#################### connection between film, director and actor ####################
f_d_a_dict = {}
for film in film_details_df.iterrows():
actors = re.sub(r'\'', '', film[1].Actors).split(',')
actors = [i.strip(' ') for i in actors]
f_d_a_dict[film[1].Film_name] = {film[1].Director: actors}
flat_data = []
for film, term in f_d_a_dict.items():
for director, actor_list in term.items():
for actor in actor_list:
flat_data.append([film, director, actor])
f_d_a_df = pd.DataFrame(flat_data, columns=['Film', 'Director', 'Actors'])
f_d_a_df.to_csv('/mnt/c/Users/ouyangkang/Desktop/film_dir_ac.csv', index=None, encoding='gbk')
#################### connection between actors ####################
st_connection = []
for film in film_details_df.iterrows():
actors = re.sub('\'', '', film[1]['Actors']).split(',')
actors = [i.strip(' ') for i in actors]
for i in range(len(actors)):
for k in range(i+1, len(actors)):
st_connection.append([actors[i], actors[k]])
pd.DataFrame(st_connection, columns=['Actor_1', 'Actor_2']).to_csv('/mnt/c/Users/ouyangkang/Desktop/connection.csv', encoding='gbk', index=None)
在Neo4j Desktop
中使用下述Cypher
语言构建导演-(导演)>电影-(包括)>演员
关系图谱。因为对Cypher
语言还不够熟练,并没有分析出具有意义的结果,见谅~
# NEO4J COMMAND
CREATE CONSTRAINT actor_unicode FOR (a:actor) REQUIRE a.unicode IS UNIQUE
CREATE CONSTRAINT director_unicode FOR (d:director) REQUIRE d.unicode IS UNIQUE
CREATE CONSTRAINT film_unicode FOR (f:film) REQUIRE f.unicode IS UNIQUE
LOAD CSV WITH HEADERS FROM 'file:///actor_unicode.csv' AS row MERGE (a:actor {name:row.Actor, unicode:row.Unicode})
LOAD CSV WITH HEADERS FROM 'file:///director_unicode.csv' AS row MERGE (d:director {name:row.Director, unicode:row.Unicode})
LOAD CSV WITH HEADERS FROM 'file:///film_unicode.csv' AS row MERGE (f:film {name:row.Film,unicode:row.Unicode})
LOAD CSV WITH HEADERS FROM 'file:///film_dir_ac.csv' AS row MERGE (d:director {name:row.Director}) MERGE (f:film {name:row.Film}) MERGE (d)-[r:Directs]->(f)
LOAD CSV WITH HEADERS FROM 'file:///film_dir_ac.csv' AS row MERGE (f:film {name:row.Film}) MERGE (a:actor {name:row.Actors}) MERGE (f)-[r:Including]->(a)
LOAD CSV WITH HEADERS FROM 'file:///connection.csv' AS row MERGE (a:actor {name:row.Actor_1}) MERGE (b:actor {name:row.Actor_2}) MERGE (a)-[r:cooperation]-(b)
同时,使用Gephi
对演员关系使用Fruchterman-Reingold
算法可视化,发现参演不同电影的演员被分成了一个有一个聚类群,而共同参演多部电影的演员在图中间形成复杂的网络结构,而主角沈腾则位于最中心,成为最红的点,连接每一个演员。较粗的线条表示沈腾与该演员在多部电影中合作过,例如马丽。
总结
本文对获取的电影数据做了相关的可视化分析,包括柱状图、折线图、箱线图、饼图以及相关性热图;使用模型预测Na值,包括线性模型、支持向量机模型;以及图可视化工具分析导演-电影-演员,演员-演员之间的复杂网络关系。希望看完本文你能够学会如何对数据进行合理的可视化、使用模型预测Na值以及图可视化复杂的网络关系。
最后感谢大家的观看,点点关注不迷路~。