豆瓣影视数据分析
最近学习了Pandas以及相关数据分析的知识,今天尝试使用Pandas对豆瓣影视数据进行分析,数据事先已经通过爬虫抓取,接下来对这个数据进行一些分析,尝试一下数据分析的过程。
定义排名的算法规则
排名规则主要由两个关键字段构成,一个是影评分数,一个是观影人数,通过对这两个值采用统计学的Z-score标准化方法,来计算排名。
这里我们根据rate和audience计算出一个指数index,我们将用index值来作为排序的依据,并将index作为新的column添加到movie_frame中
index值是由rate和audience相加得到的,我们对rate和audience都采用z-score 标准化,即(数值-平均值)/标准差,来保证两个数值都在相等一定范围内。
数据探索
1 2 3 4 5 6 7 8 9 10
| import json import pandas as pd import numpy as np data = pd.DataFrame(pd.read_csv('douban2016all.csv')) data = data[data.year == '(2016)'] data = data[np.isfinite(data['rate'])] data.reset_index(drop=True, inplace=True) data.head()
|
|
actors |
area |
audience |
director |
genre |
rate |
title |
year |
… |
0 |
[“李家鼎”, “谭玉瑛”] |
香港 |
73.0 |
[“林志荣”, “张洪翅”] |
[“真人秀”] |
9.0 |
阿爷厨房 阿爺廚房 |
(2016) |
{} |
1 |
[“薛皓文”, “詹思萌”, “马刚”, “言杰”, “任萧楠”, “苏芸盟”, “秦子婷”… |
中国大陆 |
69.0 |
[“蒋丛”] |
[“剧情”] |
3.2 |
屠门镇之破茧之子 |
(2016) |
{} |
2 |
[“吹石一惠”] |
日本 |
68.0 |
[“NHK”] |
[“纪录片”] |
7.6 |
纪实72小时~东京 铁塔下的新年祈愿 ドキュメント72時間 東京タワーで見る初夢は |
(2016) |
{} |
3 |
[] |
台湾 |
278.0 |
[“徐国峰”] |
[“动画”, “短片”] |
6.8 |
缺乏名字的场所 缺乏名字的場所 |
(2016) |
{} |
4 |
[] |
日本 |
68.0 |
[] |
[“纪录片”] |
8.0 |
纪实72小时 隆冬的东京 有一种温暖叫“花子” ドキュメント72時間 真冬の東京 その名は“… |
(2016) |
{} |
1 2 3
| douban = pd.DataFrame(data, columns=["title","year","actors", "director", "genre", "rate","audience","area"]) douban.head()
|
|
title |
year |
actors |
director |
genre |
rate |
audience |
area |
0 |
阿爷厨房 阿爺廚房 |
(2016) |
[“李家鼎”, “谭玉瑛”] |
[“林志荣”, “张洪翅”] |
[“真人秀”] |
9.0 |
73.0 |
香港 |
1 |
屠门镇之破茧之子 |
(2016) |
[“薛皓文”, “詹思萌”, “马刚”, “言杰”, “任萧楠”, “苏芸盟”, “秦子婷”… |
[“蒋丛”] |
[“剧情”] |
3.2 |
69.0 |
中国大陆 |
2 |
纪实72小时~东京 铁塔下的新年祈愿 ドキュメント72時間 東京タワーで見る初夢は |
(2016) |
[“吹石一惠”] |
[“NHK”] |
[“纪录片”] |
7.6 |
68.0 |
日本 |
3 |
缺乏名字的场所 缺乏名字的場所 |
(2016) |
[] |
[“徐国峰”] |
[“动画”, “短片”] |
6.8 |
278.0 |
台湾 |
4 |
纪实72小时 隆冬的东京 有一种温暖叫“花子” ドキュメント72時間 真冬の東京 その名は“… |
(2016) |
[] |
[] |
[“纪录片”] |
8.0 |
68.0 |
日本 |
2520
数据清洗整理
1 2 3 4 5 6
| douban_json = douban.to_dict(orient ='index') print(douban_json[2510]) {'title': '\xe9\xa9\xb4\xe5\xbe\x97\xe6\xb0\xb4', 'area': '\xe4\xb8\xad\xe5\x9b\xbd\xe5\xa4\xa7\xe9\x99\x86', 'audience': 229548.0, 'director': '["\xe5\x91\xa8\xe7\x94\xb3", "\xe5\x88\x98\xe9\x9c\xb2"]', 'rate': 8.3, 'actors': '["\xe4\xbb\xbb\xe7\xb4\xa0\xe6\xb1\x90", "\xe5\xa4\xa7\xe5\x8a\x9b", "\xe5\x88\x98\xe5\xb8\x85\xe8\x89\xaf", "\xe8\xa3\xb4\xe9\xad\x81\xe5\xb1\xb1", "\xe9\x98\xbf\xe5\xa6\x82\xe9\x82\xa3", "\xe9\x9f\xa9\xe5\xbd\xa6\xe5\x8d\x9a", "\xe5\x8d\x9c\xe5\x86\xa0\xe4\xbb\x8a", "\xe7\x8e\x8b\xe5\xa0\x83", "\xe9\xab\x98\xe9\x98\xb3", "\xe8\x8b\x8f\xe5\x8d\x83\xe8\xb6\x8a", "\xe6\x9f\xa5\xe5\xb0\x94\xe6\x96\xaf\xc2\xb7\xe4\xba\x9a\xe7\x91\x9f", "\xe7\x8e\x8b\xe5\xb3\xb0"]', 'year': '(2016)', 'genre': '["\xe5\x89\xa7\xe6\x83\x85", "\xe5\x96\x9c\xe5\x89\xa7"]'}
|
我们观察到actors, director,genre键所代表的值都是字符串,不是我们想要的list形式,我们需要把这些值转变变成一个list方便之后的读取。
举个例子,在导演这里,我们希望把’director’: ‘[“周申”, “刘露”]’转变成’director’: [‘周申’, ‘刘露’]
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| def update(data, item): for movie in data: aa = [] if item in data[movie]: subset = data[movie][item] sublist_new = subset[1:-1].split(",") for i in sublist_new: i = i.replace(" ", "") i = i.replace('"','') aa.append(i) data[movie][item] = aa return data
|
1 2 3 4
| new_data = update(douban_json,"actors") new_data = update(douban_json,"director") new_data = update(douban_json,"genre")
|
{'actors': ['\xe4\xbb\xbb\xe7\xb4\xa0\xe6\xb1\x90',
'\xe5\xa4\xa7\xe5\x8a\x9b',
'\xe5\x88\x98\xe5\xb8\x85\xe8\x89\xaf',
'\xe8\xa3\xb4\xe9\xad\x81\xe5\xb1\xb1',
'\xe9\x98\xbf\xe5\xa6\x82\xe9\x82\xa3',
'\xe9\x9f\xa9\xe5\xbd\xa6\xe5\x8d\x9a',
'\xe5\x8d\x9c\xe5\x86\xa0\xe4\xbb\x8a',
'\xe7\x8e\x8b\xe5\xa0\x83',
'\xe9\xab\x98\xe9\x98\xb3',
'\xe8\x8b\x8f\xe5\x8d\x83\xe8\xb6\x8a',
'\xe6\x9f\xa5\xe5\xb0\x94\xe6\x96\xaf\xc2\xb7\xe4\xba\x9a\xe7\x91\x9f',
'\xe7\x8e\x8b\xe5\xb3\xb0'],
'area': '\xe4\xb8\xad\xe5\x9b\xbd\xe5\xa4\xa7\xe9\x99\x86',
'audience': 229548.0,
'director': ['\xe5\x91\xa8\xe7\x94\xb3', '\xe5\x88\x98\xe9\x9c\xb2'],
'genre': ['\xe5\x89\xa7\xe6\x83\x85', '\xe5\x96\x9c\xe5\x89\xa7'],
'rate': 8.3,
'title': '\xe9\xa9\xb4\xe5\xbe\x97\xe6\xb0\xb4',
'year': '(2016)'}
由于我们此次探索的是国产影视数据,所以这里需要筛选掉非国产制作的影视剧,这里的规则是通过查找”area”是否包含“中国”,所以,严格意义上来说,合资也包含在内了。
1 2 3 4 5
| notchina = [movie for movie in new_data if new_data[movie]["area"].find("中国") != 0] for i in notchina: new_data.pop(i) len(new_data)
|
644
数据已经清理完毕,最终new_data里面保存了我们清理后的结果,接下来我们将对new_data进行数据分析
数据分析探索
1 2 3
| new_data_frame = pd.DataFrame.from_dict(new_data,orient='index') new_data_frame.head(10)
|
|
title |
area |
audience |
director |
rate |
actors |
year |
genre |
1 |
屠门镇之破茧之子 |
中国大陆 |
69.0 |
[蒋丛] |
3.2 |
[薛皓文, 詹思萌, 马刚, 言杰, 任萧楠, 苏芸盟, 秦子婷, 蒋瑞征] |
(2016) |
[剧情] |
14 |
手机芯战 |
中国大陆 |
165.0 |
[冯小刚] |
4.7 |
[冯小刚, 余文乐] |
(2016) |
[短片] |
15 |
爆笑先森 |
中国大陆 |
171.0 |
[崔志佳] |
5.0 |
[崔志佳, 潘斌龙, 肖旭] |
(2016) |
[] |
22 |
我的朋友陈白露小姐 |
中国大陆 |
1929.0 |
[陈昆晖] |
3.9 |
[陈娅安, 张天爱, 屈楚萧, 李子峰, 李茜, 明道, 谭凯, 周奕彤, 崔可法, 李卓霖… |
(2016) |
[剧情, 爱情] |
25 |
半面人 三剑豪之半面人 |
中国大陆 |
57.0 |
[洪德伟,唐妍,杨学浦,冯艾] |
6.3 |
[余志成, 李京, 谢松博] |
(2016) |
[剧情, 动作, 动画, 奇幻] |
26 |
年兽大作战 |
中国大陆 |
2099.0 |
[张扬] |
5.7 |
[雷佳音, 周冬雨, 陈赫, 陶虹, 郭涛, 郭子睿, 刘仪伟, 沈腾, 张一白, 王迅, … |
(2016) |
[喜剧, 动画, 奇幻] |
27 |
假如我有超能力 |
中国大陆 |
448.0 |
[刘迪洋] |
7.7 |
[王子睿, 刘语乔, 张歌, 杨舒枫] |
(2016) |
[喜剧, 爱情, 奇幻] |
28 |
诡娃 |
中国大陆 |
487.0 |
[蒋国权] |
2.4 |
[李抒航, 程媛媛, 孔维] |
(2016) |
[惊悚, 恐怖] |
30 |
超级女声2016 |
中国大陆 |
122.0 |
[周山] |
4.5 |
[史兆怡, 方圆, 张晓钰, 王金金, 黄汐源, 汪睿, 李雨, 袁子仪, 金雯昕, 强东玥… |
(2016) |
[音乐, 真人秀] |
34 |
中国好歌曲 第三季 |
中国大陆 |
2089.0 |
[] |
7.2 |
[刘欢, 陶喆, 范晓萱, 胡海泉, 陈羽凡] |
(2016) |
[音乐, 真人秀] |
最佳影片和最差影片
新建一个movie_frame的dataframe,只保留”title”,”rate”, “audience”
1 2 3
| movie_frame = pd.DataFrame(new_data_frame, columns = ["title","rate", "audience"]) movie_frame.head()
|
|
title |
rate |
audience |
1 |
屠门镇之破茧之子 |
3.2 |
69.0 |
14 |
手机芯战 |
4.7 |
165.0 |
15 |
爆笑先森 |
5.0 |
171.0 |
22 |
我的朋友陈白露小姐 |
3.9 |
1929.0 |
25 |
半面人 三剑豪之半面人 |
6.3 |
57.0 |
根据rate和audience计算出一个指数index,我们将用index值来作为排序的依据,并将index作为新的column添加到movie_frame中。
index值是由rate和audience相加得到的,我们对rate和audience都采用z-score 标准化,即(数值-平均值)/标准差,来保证两个数值都在相等一定范围内。
也可以采用(数值-最小值)/(最大值-最小值)来保证rate和audience标准化后的值在(0,1)范围内
1 2 3 4
| movie_frame['index'] = (movie_frame.rate - movie_frame.rate.mean())/movie_frame.rate.std() + (movie_frame.audience - movie_frame.audience.mean())/movie_frame.audience.std() movie_frame.head()
|
|
title |
rate |
audience |
index |
1 |
屠门镇之破茧之子 |
3.2 |
69.0 |
-1.826201 |
14 |
手机芯战 |
4.7 |
165.0 |
-1.009050 |
15 |
爆笑先森 |
5.0 |
171.0 |
-0.846053 |
22 |
我的朋友陈白露小姐 |
3.9 |
1929.0 |
-1.385304 |
25 |
半面人 三剑豪之半面人 |
6.3 |
57.0 |
-0.144325 |
分别根据index进行升序和倒序排列,找出最佳影片和最差影片
1 2 3
| movie_frame = movie_frame.sort_values(by='index') movie_frame.head()
|
|
title |
rate |
audience |
index |
233 |
渡灵人 |
2.2 |
257.0 |
-2.362700 |
68 |
绝战 |
2.3 |
443.0 |
-2.302330 |
415 |
功夫四侠 |
2.4 |
276.0 |
-2.253543 |
1314 |
越囧 |
2.4 |
320.0 |
-2.252099 |
117 |
诡梦凶铃 |
2.4 |
385.0 |
-2.249966 |
最佳影片
1 2 3
| movie_frame = movie_frame.sort_values(by='index', ascending=False) movie_frame.head()
|
|
title |
rate |
audience |
index |
2487 |
美人鱼 |
6.8 |
372061.0 |
12.333137 |
2503 |
湄公河行动 |
8.0 |
296258.0 |
10.497104 |
2510 |
驴得水 |
8.3 |
229548.0 |
8.471027 |
2514 |
七月与安生 |
7.6 |
210274.0 |
7.458745 |
2480 |
大鱼海棠 |
6.5 |
203513.0 |
6.639970 |
将总表转化为演员,导演和电影类型的dataframe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| def get_list(data, item): results = [] for movie in new_data: if item in new_data[movie]: for i in new_data[movie][item]: if len(i)>0: results.append(i) results = list(set(results)) return results actor_list = get_list(new_data, "actors") director_list = get_list(new_data, "director") genre_list = get_list(new_data, "genre") movie_list = get_list(new_data, "title") def classbyitem(listname, items): sum_list = [] for name in listname: movie_name = [] count = 0 total_rate = 0 total_auidence = 0 for movie in new_data: if name in new_data[movie][items]: if new_data[movie]["rate"] > 0 and new_data[movie]["audience"]>0: total_rate = total_rate + new_data[movie]["rate"] total_auidence = total_auidence + new_data[movie]["audience"] movie_name.append(new_data[movie]["title"]) count = count + 1 sum_list.append([name,count,total_rate,total_auidence,movie_name]) return sum_list genre_frame = pd.DataFrame(classbyitem(genre_list, "genre"),columns=["name", "count","total_rate","total_audience","movies"]) actor_frame = pd.DataFrame(classbyitem(actor_list, "actors"),columns=["name", "count","total_rate", "total_audience","movies"]) director_frame = pd.DataFrame(classbyitem(director_list, "director"),columns=["name", "count","total_rate", "total_audience","movies"])
|
|
name |
count |
total_rate |
total_audience |
movies |
0 |
郑国霖 |
2 |
10.9 |
64698.0 |
[老九门, 青云志] |
1 |
朱袁员 |
1 |
5.0 |
105.0 |
[毛泽东三兄弟] |
2 |
中村绘里子 |
1 |
7.1 |
1766.0 |
[从前有座灵剑山 霊剣山 星屑たちの宴] |
3 |
李曼筠 |
1 |
2.7 |
189.0 |
[少女龙婆] |
4 |
宋运成 |
1 |
5.3 |
77.0 |
[寒山令] |
|
name |
count |
total_rate |
total_audience |
movies |
0 |
余庆 |
1 |
6.3 |
509.0 |
[特殊嫌疑犯] |
1 |
马伟豪 |
1 |
4.9 |
7033.0 |
[我们的十年] |
2 |
深圳卫视 |
1 |
8.1 |
554.0 |
[深圳卫视“时间的朋友”2016跨年演讲] |
3 |
程丹 |
1 |
7.9 |
209.0 |
[中国文房四宝] |
4 |
赖俊羽 |
1 |
2.6 |
4214.0 |
[泡沫之夏] |
|
name |
count |
total_rate |
total_audience |
movies |
0 |
歌舞 |
8 |
54.8 |
3637.0 |
[江苏卫视2016跨年演唱会, 星动亚洲 第二季, 2016辽宁卫视春节联欢晚会, 极限公益… |
1 |
犯罪 |
33 |
197.5 |
1125388.0 |
[拐人 人さらい, 诡梦凶铃, 特殊嫌疑犯, 浴缸神探, 爱的追踪, 轻松+愉快, 十宗罪,… |
2 |
戏曲 |
1 |
7.1 |
116.0 |
[Lying man 第五季 lying man 第五季] |
3 |
脱口秀 |
37 |
270.2 |
85884.0 |
[有请老梁, 大学生来了, 电影最TOP, 暴走法条君, 黄小厨的春夏秋冬 第二季, 你正常… |
4 |
动作 |
49 |
268.3 |
1286648.0 |
[半面人 三剑豪之半面人, 全职法师, 绝战, 白雪公主和三只小猪, 喜乐长安, 看见我和你… |
按照我们对最佳影片添加index的方法,计算出actor表中的index指数
注意:因为一名演员2016年可能出演多部电影,所以在计算index指数时候要把total_rate和total_audience除以count转化为平均值
1 2 3 4 5
| actor_frame['rate'] = actor_frame.total_rate/actor_frame['count'] actor_frame['audience'] = actor_frame.total_audience/actor_frame['count'] actor_frame['index'] = (actor_frame.rate - actor_frame.rate.mean())/actor_frame.rate.std() + (actor_frame.audience - actor_frame.audience.mean())/actor_frame.audience.std() actor_frame.head()
|
|
name |
count |
total_rate |
total_audience |
movies |
rate |
audience |
index |
0 |
郑国霖 |
2 |
10.9 |
64698.0 |
[老九门, 青云志] |
5.45 |
32349.0 |
0.299686 |
1 |
朱袁员 |
1 |
5.0 |
105.0 |
[毛泽东三兄弟] |
5.00 |
105.0 |
-0.895912 |
2 |
中村绘里子 |
1 |
7.1 |
1766.0 |
[从前有座灵剑山 霊剣山 星屑たちの宴] |
7.10 |
1766.0 |
0.413805 |
3 |
李曼筠 |
1 |
2.7 |
189.0 |
[少女龙婆] |
2.70 |
189.0 |
-2.275758 |
4 |
宋运成 |
1 |
5.3 |
77.0 |
[寒山令] |
5.30 |
77.0 |
-0.716422 |
按照index指数,对出演一部以上节目的演员进行排序,找出指数最高的演员
1 2 3
| actor_frame = actor_frame[actor_frame['count'] > 1].sort_values(by='index', ascending=False) actor_frame.head()
|
|
name |
count |
total_rate |
total_audience |
movies |
rate |
audience |
index |
2795 |
张涵予 |
2 |
13.0 |
483216.0 |
[长城 The Great Wall, 湄公河行动] |
6.50 |
241608.0 |
6.934855 |
2377 |
陈宝国 |
2 |
15.1 |
296418.0 |
[信者无敌, 湄公河行动] |
7.55 |
148209.0 |
4.886045 |
2010 |
张雨绮 |
2 |
9.8 |
377109.0 |
[蒸发太平洋 Lost in the Pacific, 美人鱼] |
4.90 |
188554.5 |
4.451055 |
783 |
赵健 |
2 |
13.1 |
304126.0 |
[九州天空城, 湄公河行动] |
6.55 |
152063.0 |
4.395645 |
534 |
卢惠光 |
2 |
13.2 |
297207.0 |
[四平青年之浩哥大战古惑仔, 湄公河行动] |
6.60 |
148603.5 |
4.326433 |
导演的index指数
1 2 3 4 5
| director_frame['rate'] = director_frame.total_rate/director_frame['count'] director_frame['audience'] = director_frame.total_audience/director_frame['count'] director_frame['index'] = (director_frame.rate - director_frame.rate.mean())/director_frame.rate.std() + (director_frame.audience - director_frame.audience.mean())/director_frame.audience.std() director_frame.head()
|
|
name |
count |
total_rate |
total_audience |
movies |
rate |
audience |
index |
0 |
余庆 |
1 |
6.3 |
509.0 |
[特殊嫌疑犯] |
6.3 |
509.0 |
-0.105881 |
1 |
马伟豪 |
1 |
4.9 |
7033.0 |
[我们的十年] |
4.9 |
7033.0 |
-0.653301 |
2 |
深圳卫视 |
1 |
8.1 |
554.0 |
[深圳卫视“时间的朋友”2016跨年演讲] |
8.1 |
554.0 |
0.858370 |
3 |
程丹 |
1 |
7.9 |
209.0 |
[中国文房四宝] |
7.9 |
209.0 |
0.740731 |
4 |
赖俊羽 |
1 |
2.6 |
4214.0 |
[泡沫之夏] |
2.6 |
4214.0 |
-1.970680 |
1 2 3
| director_frame = director_frame.sort_values(by='index', ascending=False) director_frame.head()
|
|
name |
count |
total_rate |
total_audience |
movies |
rate |
audience |
index |
447 |
周星驰 |
1 |
6.8 |
372061.0 |
[美人鱼] |
6.8 |
372061.0 |
11.635786 |
464 |
林超贤 |
1 |
8.0 |
296258.0 |
[湄公河行动] |
8.0 |
296258.0 |
9.936759 |
207 |
周申 |
1 |
8.3 |
229548.0 |
[驴得水] |
8.3 |
229548.0 |
8.037109 |
143 |
刘露 |
1 |
8.3 |
229548.0 |
[驴得水] |
8.3 |
229548.0 |
8.037109 |
463 |
曾国祥 |
1 |
7.6 |
210274.0 |
[七月与安生] |
7.6 |
210274.0 |
7.067446 |
电影类型的index指数及index指数排名
1 2 3 4 5 6
| genre_frame['rate'] = genre_frame.total_rate/genre_frame['count'] genre_frame['audience'] = genre_frame.total_audience/genre_frame['count'] genre_frame['index'] = (genre_frame.rate - genre_frame.rate.mean())/genre_frame.rate.std() + (genre_frame.audience - genre_frame.audience.mean())/genre_frame.audience.std() genre_frame = genre_frame.sort_values(by='index', ascending=False) genre_frame.head()
|
|
name |
count |
total_rate |
total_audience |
movies |
rate |
audience |
index |
1 |
犯罪 |
33 |
197.5 |
1125388.0 |
[拐人 人さらい, 诡梦凶铃, 特殊嫌疑犯, 浴缸神探, 爱的追踪, 轻松+愉快, 十宗罪,… |
5.984848 |
34102.666667 |
3.119047 |
4 |
动作 |
49 |
268.3 |
1286648.0 |
[半面人 三剑豪之半面人, 全职法师, 绝战, 白雪公主和三只小猪, 喜乐长安, 看见我和你… |
5.475510 |
26258.122449 |
1.768434 |
19 |
纪录片 |
51 |
424.7 |
111765.0 |
[拐人 人さらい, 张梁记, 书房里的中国:纸寿千年, 米歇尔·福柯, 跑步日记, 聆听中国… |
8.327451 |
2191.470588 |
1.639117 |
21 |
奇幻 |
50 |
269.1 |
1257842.0 |
[半面人 三剑豪之半面人, 年兽大作战, 假如我有超能力, 孙悟空七打九尾狐, 暮夜传说, … |
5.382000 |
25156.840000 |
1.558814 |
13 |
传记 |
5 |
39.1 |
1853.0 |
[摇摇晃晃的人间, 毛泽东三兄弟, 创新之路, 海棠依旧, 木心物语] |
7.820000 |
370.600000 |
0.971681 |
根据Index电影指数我们可以得出以下结论:
- 2016年度华语影视圈最佳影片:《美人鱼》
- 2016年度华语影视圈最佳演员:张涵予
- 2016年度华语影视圈最佳导演:孔笙
- 2016年度华语观众最喜欢的影视类型:犯罪(难道是大家心里都有一颗想要犯罪的心?)