Python大数据分析实战:豆瓣电影Top250中的最佳导演是谁?

在之前写的一篇文中中,已经采用urllib和BeautifulSoup的方式抓取了豆瓣电影TOP250的导演、编剧、演员、上映时间和地区、语言、短评数、影评数、多少人想看、多少人看过等22个字段。

接下来,我们要对这些数据进行分析、挖掘,得到有价值的信息。

下面是整个分析过程的思维导图:

豆瓣电影Top250 的最佳导演.png

一、获取数据

先从csv文件中读取数据,观察一下:

import pandas as pd

df = pd.read_csv('exercise_douban_movie_Top250.csv')
print(df.loc[0])

输出为:

QQ截图20190506164648.png
print(df.loc[0, 'directors'])
print(type(df.loc[0, 'directors']))

输出为:

['弗兰克·德拉邦特']
<class 'str'>

通过观察可以发现,这些数据不能直接使用,有些数据的格式不符合后续操作的要求。

比如:

  • 评分人数部分为字符串,且包含了汉字;
  • 多个字段的列表实际上被读取为字符串;
  • 百分比以字符串形式存储等
  • ……

所以我们要对数据进行清洗,使得格式满足后续分析的要求。

二、数据清洗

# 2. 数据清洗,把数据转化成需要的格式
#       评分人数:x人评价清洗为x,并调整为int类型
df['score_cnt'] = df['score_cnt'].map(lambda x: int(x[:-3]))

#       将字符串改为列表
df_tmp = df[['directors', 'writers', 'actors', 'types', 'dates', 'play_location', 'rating_per', 'betters', 'tags']]
df[['directors', 'writers', 'actors', 'types', 'dates', 'play_location', 'rating_per', 'betters', 'tags']] = \
    df_tmp.applymap(lambda x: eval(x))

#       上映年份由字符串转换为int类型
df['dates'] = df['dates'].map(lambda x: [int(i[:4]) for i in x])
df['year'] = df['dates'].map(lambda x: min(x))

#       五星比例/好评比例
df['five_star_rate'] = df['rating_per'].map(lambda x: float(x[0][:-1])/100)
df['favor_rate'] = df['rating_per'].map(lambda x:  (float(x[0][:-1])/100+ float(x[1][:-1])/100))

#       比百分之多少同类电影好
df['better_than'] = df['betters'].map(lambda x: sum([int(i.split('%')[0]) for i in x])/len(x))

先进行一个简单的清洗,后续分析过程中有什么需要再进行添加。

除了解决格式不符合要求的问题外,我们还额外创建了一些字段,比如五星比例、好多多少同类电影等,这些都是为后续分析提供帮助。

三、进行分析

先划定一个标准,那就是在TOP250中有不少于3部电影的导演,才可以参与最佳导演评选。

先看一下一共有多少位导演:

from functools import reduce

# 消灭空格
df['directors'] = df['directors'].map(lambda x: [i.strip() for i in x])

# reduce迭代获取所有导演的列表
director_list = reduce(lambda x, y: x + y, df.director)

print(len(director_list))

结果返回281,也就是说这250部影片有281位导演,存在联合执导的情况。那我们接着看一下影片数量大于3部的有哪些导演:

from collections import Counter

dire_counter = Counter(director_list)
dire_counter = sorted(dire_counter.items(), key=lambda x: x[1], reverse=True)
top_directors = list(filter(lambda x: x[1] >= 3, dire_counter))
print(top_directors)

输出为:

[('宫崎骏', 7),
 ('克里斯托弗·诺兰', 7),
 ('史蒂文·斯皮尔伯格', 6),
 ('王家卫', 5),
 ('李安', 4),
 ('大卫·芬奇', 4),
 ('詹姆斯·卡梅隆', 3),
 ('朱塞佩·托纳多雷', 3),
 ('刘镇伟', 3),
 ('弗朗西斯·福特·科波拉', 3),
 ('姜文', 3),
 ('彼得·杰克逊', 3),
 ('彼特·道格特', 3),
 ('昆汀·塔伦蒂诺', 3),
 ('理查德·林克莱特', 3),
 ('李·昂克里奇', 3),
 ('理查德·柯蒂斯', 3),
 ('吴宇森', 3),
 ('是枝裕和', 3)]

可以看到宫崎骏和诺兰的电影数最多。

但这样我们无法确定谁才是最佳导演,接下来我们用两种方法对他们进行排序

  • 以平均豆瓣评分来进行排序
  • 以平均榜单位置进行排序
from collections import defaultdict

top_dire_score = defaultdict(list)
top_dire_ind = defaultdict(list)
for name, cnt in top_directors:
    for index, row in df.iterrows():
        if name in row['director']:
            top_dire_score[name].append(row['score'])
            top_dire_ind[name].append(row['top_no'])
print(top_dire_score)
print(top_dire_ind)

输出为:

# 评分
defaultdict(list,
            {'宫崎骏': [9.3, 9.1, 9.0, 8.9, 8.8, 8.8, 8.5],
             '克里斯托弗·诺兰': [9.3, 9.2, 9.1, 8.8, 8.6, 8.6, 8.9],
             '史蒂文·斯皮尔伯格': [9.5, 8.9, 8.8, 8.7, 8.6, 8.5],
             '王家卫': [8.8, 8.7, 8.6, 8.6, 8.5],
             '李安': [9.0, 9.1, 8.7, 8.8],
             '大卫·芬奇': [9.0, 8.8, 8.8, 8.7],
             '詹姆斯·卡梅隆': [9.3, 8.6, 8.6],
             '朱塞佩·托纳多雷': [9.2, 9.1, 8.8],
             '刘镇伟': [9.2, 8.9, 8.7],
             '弗朗西斯·福特·科波拉': [9.2, 9.1, 8.8],
             '姜文': [9.2, 8.7, 8.8],
             '彼得·杰克逊': [9.1, 9.0, 8.9],
             '彼特·道格特': [8.9, 8.6, 8.7],
             '昆汀·塔伦蒂诺': [8.8, 8.6, 8.5],
             '理查德·林克莱特': [8.7, 8.8, 8.8],
             '李·昂克里奇': [8.6, 9.0, 8.8],
             '理查德·柯蒂斯': [8.5, 8.7, 8.6],
             '吴宇森': [8.6, 8.7, 8.4],
             '是枝裕和': [9.1, 8.7, 8.8]})

# 榜单位置           
defaultdict(list,
            {'宫崎骏': [7, 19, 36, 43, 88, 112, 191],
             '克里斯托弗·诺兰': [9, 18, 27, 65, 137, 145, 192],
             '史蒂文·斯皮尔伯格': [8, 70, 83, 118, 171, 222],
             '王家卫': [80, 91, 132, 159, 181],
             '李安': [30, 54, 94, 131],
             '大卫·芬奇': [35, 62, 64, 104],
             '詹姆斯·卡梅隆': [6, 96, 210],
             '朱塞佩·托纳多雷': [13, 29, 66],
             '刘镇伟': [15, 38, 101],
             '弗朗西斯·福特·科波拉': [17, 50, 155],
             '姜文': [32, 69, 87],
             '彼得·杰克逊': [33, 51, 52],
             '彼特·道格特': [37, 127, 173],
             '昆汀·塔伦蒂诺': [73, 174, 218],
             '理查德·林克莱特': [105, 113, 217],
             '李·昂克里奇': [127, 129, 158],
             '理查德·柯蒂斯': [140, 154, 231],
             '吴宇森': [141, 151, 223],
             '是枝裕和': [153, 206, 208]})

接下来我们求一下均值,并将入榜电影数作为一个权重加进去:

from math import log2
from math import sqrt
rank_score = []
rank_ind = []

for name, scores in top_dire_score.items():
    rank_score.append([name, sum(scores) / len(scores) * sqrt(log2(len(scores)))])

for name, indexes in top_dire_ind.items():
    rank_ind.append([name, sum(indexes) / sqrt(log2(len(scores))) /len(indexes)])
    
rank_score = sorted(rank_score, key=lambda x: x[1], reverse=True)
rank_ind = sorted(rank_ind, key=lambda x: x[1])
print(rank_score[:10])
print(rank_ind[:10])

输出为:

# 加权得分榜
[['克里斯托弗·诺兰', 14.959967098817579],
 ['宫崎骏', 14.936031151459467],
 ['史蒂文·斯皮尔伯格', 14.202073072976324],
 ['王家卫', 13.165523290477429],
 ['李安', 12.586500705120548],
 ['大卫·芬奇', 12.480434687942564],
 ['朱塞佩·托纳多雷', 11.372541542166006],
 ['弗朗西斯·福特·科波拉', 11.372541542166006],
 ['彼得·杰克逊', 11.330576444224434],
 ['刘镇伟', 11.24664624834129]]
 
# 加权位置榜
[['朱塞佩·托纳多雷', 28.59519121510834],
 ['彼得·杰克逊', 36.008759307914204],
 ['刘镇伟', 40.774624510432254],
 ['姜文', 49.776814337410805],
 ['大卫·芬奇', 52.6230949444702],
 ['宫崎骏', 56.282598582118],
 ['弗朗西斯·福特·科波拉', 58.77900416438936],
 ['李安', 61.36051448241997],
 ['克里斯托弗·诺兰', 67.28947774031447],
 ['詹姆斯·卡梅隆', 82.60833017697963]]

可以看到,在我们的加权得分算法下,诺兰以微弱优势胜出,夺得豆瓣最佳导演奖。然而在我们的加权榜单位置算法中,朱塞佩·托纳多雷的电影平均能获得更靠前的豆瓣排名,夺得桂冠,而宫崎骏和诺兰的排名则分列6、9位。

上面两个排名呈现出不同的结果,但是我更倾向于第一个。因为评分使用的是连续的数据,而位置数据是离散的,榜首和末尾的数据差了249,但是它们的实际表现并没有那么大的差距。

注意一点,这个排名数据可能会随着时间的推移而发生变化,因为电影的评分和排行榜会发生变化。

我们将上述代码中的列名调整下就可以得到演员的榜单,这一部分可以后续作为练习进行尝试。

总结

上面虽然是一个小的例子,但是它是一个完整的大数据分析过程,涉及到数据的读取,数据清洗和数据分析。可以把这个作为一个入门实战,然后在此基础上进行扩展,比如分析一下,哪一类电影更受豆瓣用户欢迎,可以做出词云图,可以在现有基础上为用户推荐电影。

参考资料:Python大数据分析实战:豆瓣人的电影口味重吗?

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,542评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,596评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,021评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,682评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,792评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,985评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,107评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,845评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,299评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,612评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,747评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,441评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,072评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,828评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,069评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,545评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,658评论 2 350

推荐阅读更多精彩内容