当前位置 飘雪电影 资讯 正文

【IMDB top10000】电影排行榜数据分析

影视热点

近日电影催泪弹相继发射,《神秘巨星》、《奇迹男孩》、《无问西东》或励志、或煽情,目前三者都已取得不错的口碑和票房表现。

影视热点

那么,人们对电影的评价有着怎样的规律呢?今天,小科就为大家带来了K-Lab平台上的人气项目——「IMDB top10000电影排行榜数据分析」。让我们带着数据科学家的眼光,来探究电影评分背后的秘密吧!本文中出现的所有代码片段都可以在K-Lab上实现哦。!(^^)!

数据整理和探索

本项目翻译自:Companion to Lecture 4 of Harvard CS109: Data Science | Prepared by Chris Beaumont

#关闭警告信息
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pdimport numpy as np
#tell pandas to display wide tables as pretty HTML tables
pd.set_option('display.width', 500)
pd.set_option('display.max_columns', 100)
def remove_border(axes=None, top=False, right=False, left=True, bottom=True): """    
   Minimize chartjunk by stripping out unnecesasry plot borders and axis ticks
   
   The top/right/left/bottom keywords toggle whether the corresponding plot border is drawn
   """ ax = axes or plt.gca() ax.spines['top'].set_visible(top) ax.spines['right'].set_visible(right) ax.spines['left'].set_visible(left) ax.spines['bottom'].set_visible(bottom) #turn off all ticks ax.yaxis.set_ticks_position('none') ax.xaxis.set_ticks_position('none') #now re-enable visibles if top: ax.xaxis.tick_top() if bottom: ax.xaxis.tick_bottom() if left: ax.yaxis.tick_left() if right: ax.yaxis.tick_right()

我想为Python的探索性数据分析的早期阶段提供一个基本的数据整理标准。这不是普遍适用的,但它覆盖了在几个数据分析上下文中重现的许多模式。 在遇到新数据集时记住这个流程很有用。

基本工作流程如下:

  1. 构建数据框:理想情况下,把所有数据放入这个数据框中。
  2. 清洗数据:对构建的数据框进行数据清理。
  3. 探索全局特征:通过直方图,散点图,聚合函数等获得一个数据的全局的了解。
  4. 探索分组特征:通过一些分组操作分析数据集。

(译者注:可以参考Hadley Wickham提出的整洁数据 「Tidy Data 整洁的数据」)

这几步数据整理后,数据整理成更容易分析的方式,也增加了对数据特征的基本了解,同时也可以帮助发现问题,提出新想法,指导下面的分析过程。

1.构建数据框

data=pd.read_csv("../input/movielens/imdb10000.csv")
data.head() # print the first 5 rows

影视热点

2. 清洗数据

这时候的数据有几个问题:

  1. 电影时长不是数值,而是字符串
  2. 电影流派不是原子数据,很难提取特定的流派做分析
  3. 上映年份在名称和时间上是重复的
  • 修正电影时长

下面这段代码可以把字符串“142 mins”,转换成数值142。

dirty = '142 mins.'
number, text = dirty.split(' ')
clean = int(number)
print(number)

可以以列表解析的方式实现

clean_runtime = [float(r.split(' ')[0]) for r in data.runtime]
data['runtime'] = clean_runtime
data.head()

影视热点

  • 分割流派信息

我们可以使用指示变量的概念将流派列分割成许多列。每个新的列将对应于一个单一的流派,每个单元格将为True或False。

#determine the unique genres
genres = set()
for m in data.genres: genres.update(g for g in m.split('|'))
genres = sorted(genres)
#make a column for each genre
for genre in genres: data[genre] = [genre in movie.split('|') for movie in data.genres] data.head()

影视热点

  • 从名称里移除年份信息

可以通过移除最后7个字符的方式实现

data['title'] = [t[0:-7] for t in data.title]
data.head()

影视热点

3. 探索全局特征

下一步,我们就可以对一些全局特征进行基本的探索了。

通过 describe方法了解

data[['score', 'runtime', 'year', 'votes']].describe()

影视热点

#发现电影时长有为0的,看看一共有多少?
print((len(data[data.runtime == 0])))

#应该把这些标记为NAN。
data.runtime[data.runtime==0] = np.nan

282

将损坏的数据 标记完成后再看一下。

data.runtime.describe()

影视热点

  • 做一些基本的可视化
# 最近几年的电影很多, 但是更近一点的不是很多(是不是投票时间不足?)
plt.hist(data.year, bins=np.arange(1950, 2013), color='#cccccc')
plt.xlabel("Release Year")
remove_border()

影视热点

plt.hist(data.score, bins=20, color='#cccccc')
plt.xlabel("IMDB rating")
remove_border()

影视热点

plt.hist(data.runtime.dropna(), bins=50, color='#cccccc')
plt.xlabel("Runtime distribution")
remove_border()

影视热点

#时间更近的电影评分较低,这是真实的情况还是选择偏差?
plt.scatter(data.year, data.score, lw=0, alpha=.08, color='k')
plt.xlabel("Year")
plt.ylabel("IMDB Rating")
remove_border()

影视热点

plt.scatter(data.votes, data.score, lw=0, alpha=.2, color='k')
plt.xlabel("Number of Votes")
plt.ylabel("IMDB Rating")
plt.xscale('log')
remove_border()

影视热点

  • 寻找异常点
# 评价较低但是投票数高。
data[(data.votes > 9e4) & (data.score < 5)][['title', 'year', 'score', 'votes', 'genres']]

影视热点

# 最低评分的电影
data[data.score == data.score.min()][['title', 'year', 'score', 'votes', 'genres']]

影视热点

# 最高评分的电影
data[data.score == data.score.max()][['title', 'year', 'score', 'votes', 'genres']]

影视热点

↑ 两部最高评分的电影:《肖申克的救赎》和《教父》

对一些行或列,使用聚合函数如 sum 进行分析

哪个流派出现频次最高?

#流派计数,排序
genre_count = np.sort(data[genres].sum())[::-1]
pd.DataFrame({'Genre Count': genre_count})

平均一部电影有多少个流派标记?

genre_count = data[genres].sum(axis=1) print(("Average movie has %0.2f genres" % genre_count.mean()))
genre_count.describe()

Average movie has 2.75 genres

4. 探索分组特征

让我们将电影按年代划分

decade = (data.year // 10) * 10
tyd = data[['title', 'year']]
tyd['decade'] = decade
tyd.head()

GroupBy 可以将电影按年代分组

#每个年代的平均分
decade_mean = data.groupby(decade).score.mean()
decade_mean.name = 'Decade Mean'
print(decade_mean)
plt.plot(decade_mean.index, decade_mean.values, 'o-', color='r', lw=3, label='Decade Average')
plt.scatter(data.year, data.score, alpha=.04, lw=0, color='k')
plt.xlabel("Year")
plt.ylabel("Score")
plt.legend(frameon=False)
remove_border()

影视热点

我们还可以进一步看下每年评分的分散情况。

grouped_scores = data.groupby(decade).score
mean = grouped_scores.mean()
std = grouped_scores.std()
plt.plot(decade_mean.index, decade_mean.values, 'o-', color='r', lw=3, label='Decade Average')
plt.fill_between(decade_mean.index, (decade_mean + std).values, (decade_mean - std).values, color='r', alpha=.2)
plt.scatter(data.year, data.score, alpha=.04, lw=0, color='k')
plt.xlabel("Year")
plt.ylabel("Score")
plt.legend(frameon=False)
remove_border()

影视热点

你也可以在GroupBy对象上进行迭代。每次迭代生成两个变量:group key和与group key对应的数据子集。 找到每年最流行的电影:

for year, subset in data.groupby(‘year’): print((year, subset[subset.score == subset.score.max()].title.values))

影视热点

  • Small multiples

让我们按照流派划分数据,看看发行时间,时长,评分是如何分布的。 全部电影数据的分布作为灰色背景展示。 这不是标准的groupby操作, 所以不能使用groupby函数。

#create a 4x6 grid of plots.
fig, axes = plt.subplots(nrows=4, ncols=6, figsize=(12, 8), 
                         tight_layout=True)

bins = np.arange(1950, 2013, 3)
for ax, genre in zip(axes.ravel(), genres):
    ax.hist(data[data[genre] == 1].year, 
            bins=bins, histtype='stepfilled', normed=True, color='r', alpha=.3, ec='none')
    ax.hist(data.year, bins=bins, histtype='stepfilled', ec='None', normed=True, zorder=0, color='#cccccc')
    
    ax.annotate(genre, xy=(1955, 3e-2), fontsize=14)
    ax.xaxis.set_ticks(np.arange(1950, 2013, 30))
    ax.set_yticks([])
    remove_border(ax, left=False)
    ax.set_xlabel('Year')

影视热点

这里可以发现一些微妙的特征:

  1. 西部片和音乐剧有更水平的分布。
  2. 黑色电影在五六十年代更受欢迎。
data[data[genre] == 1].runtime=data[data[genre] == 1].runtime.dropna()
fig, axes = plt.subplots(nrows=4, ncols=6, figsize=(12, 8), tight_layout=True)

bins = np.arange(30, 240, 10)

for ax, genre in zip(axes.ravel(), genres):
    ax.hist(data[data[genre] == 1].runtime.dropna(), 
            bins=bins, histtype='stepfilled', color='r', ec='none', alpha=.3, normed=True)
               
    ax.hist(data.runtime.dropna(), bins=bins, normed=True,
            histtype='stepfilled', ec='none', color='#cccccc',
            zorder=0)
    
    ax.set_xticks(np.arange(30, 240, 60))
    ax.set_yticks([])
    ax.set_xlabel("Runtime [min]")
    remove_border(ax, left=False)
    ax.annotate(genre, xy=(230, .02), ha='right', fontsize=12)

影视热点

  1. 传记和历史题材电影时间更长
  2. 动画电影较短
  3. 黑色电影和总数据的均值相同,但更集中在100分钟时长左右
  4. 音乐剧和总数据的均值相同, 但时间更为分散
fig, axes = plt.subplots(nrows=4, ncols=6, figsize=(12, 8), tight_layout=True)

bins = np.arange(0, 10, .5)

for ax, genre in zip(axes.ravel(), genres):
    ax.hist(data[data[genre] == 1].score, 
            bins=bins, histtype='stepfilled', color='r', ec='none', alpha=.3, normed=True)
               
    ax.hist(data.score, bins=bins, normed=True,
            histtype='stepfilled', ec='none', color='#cccccc',
            zorder=0)
    
    ax.set_yticks([])
    ax.set_xlabel("Score")
    remove_border(ax, left=False)
    ax.set_ylim(0, .4)
    ax.annotate(genre, xy=(0, .2), ha='left', fontsize=12)
  1. 黑色电影,历史和传记电影有更高的评分
  2. 恐怖和成人电影评分较低

原文链接:https://www.kesci.com/apps/home/#!/lab/58ca1a1697c4b112cbb6ab5d/v/latest/code

一个电影评分数据集,折射出的是60年间的电影百态。除了IMBD,中国的豆瓣评分也已经登陆K-Lab,与大家见面啦!想从中发掘更多有趣结论吗?赶快到【IMDB电影数据集】或【豆瓣电影评分数据集】下建立你的项目,开始你的发现之旅吧!

联系我们

联系我们

0898-88881688

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部