豆瓣影视数据分析

最近学习了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
# 导入我们事先准备好的csv,dataframe命名为data
data = pd.DataFrame(pd.read_csv('douban2016all.csv'))
# 因为虽然打上了2016的标签,但是部分影视剧不是2016年的,用一行代码过滤时间,仅保留2016年的影视剧
data = data[data.year == '(2016)']
data = data[np.isfinite(data['rate'])] #去除没有评分的影片
data.reset_index(drop=True, inplace=True) #重置index
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
#生成新的dataframe,命名为douban,保留"title","year","actors", "director", "genre", "rate","audience","area"
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 日本
1
len(douban) #查看总体数目
2520

数据清洗整理

1
2
3
4
5
6
# 将dataframe直接转换成json
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
# 这是一个update函数,用于更新douban_json中的actors, director, genre键下的值
#data是指我们要导入的数据,item就是指我们要更新的值的键,比如"actors","director"这种
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
# 更新后的json命名为new_data
new_data = update(douban_json,"actors")
new_data = update(douban_json,"director")
new_data = update(douban_json,"genre")
1
2
# 查看任意一个new_data中的值
new_data[2510]
{'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
# 把清理过后的josn格式的数据new_data转换为pandas dataframe,命名为new_data_frame
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的dataframe,只保留"title","rate", "audience"
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
# 计算index指数
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
# 将总表转化为演员,导演和电影类型的dataframe
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"])
1
2
# 查看我们之前生成的actor_frame
actor_frame.head()
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 [寒山令]
1
2
# 查看我们之前生成的director_frame
director_frame.head()
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 [泡沫之夏]
1
2
# 查看我们之前生成的genre_frame
genre_frame.head()
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
# 计算index指数
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
#导演的index指数
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
#导演的index指数排名
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
# 电影类型的index指数及index指数排名
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电影指数我们可以得出以下结论:

  1. 2016年度华语影视圈最佳影片:《美人鱼》
  2. 2016年度华语影视圈最佳演员:张涵予
  3. 2016年度华语影视圈最佳导演:孔笙
  4. 2016年度华语观众最喜欢的影视类型:犯罪(难道是大家心里都有一颗想要犯罪的心?)