介绍

看电影是目前人们休闲娱乐,消遣时光的选择之一。我们都知道,有些电影的票房很高,有的电影票房却很低,那么决定票房的因素是什么呢?本次将介绍,如何根据电影上映前的一些信息来预测出该电影的票房。

知识点

  • 数据预处理
  • 建立预测模型

电影票房预测介绍

电影产业在 2018 年估计达到 417 亿美元,电影业比以往任何时候都更受欢迎。 那么电影可能跟哪些因素有关呢?我们可以联想到以下几个因素。

  • 导演
  • 演员
  • 预算
  • 预告片

那是否是这些因素决定了一部电影的最终票房呢?我们可以分析 Kaggle 提供的数据来回答这一问题。数据详情可以参考 Kaggle 官方页面 ,其主要是电影数据库中的 7000 多部过去电影的元数据。提供的数据信息包括演员,工作人员,情节关键词,预算,海报,发布日期,语言,制作公司和国家等。

本次的目的则是根据提供的这些信息来预测一部电影最终的票房。

数据导入

我们先导入数据,并查看数据的前 5 份。

链接:https://pan.baidu.com/s/1AsZBZNVEHZkoeGm8v2wtGg 提取码:76bx

import pandas as pd

df = pd.read_csv("TMDB.csv")
df.head()

可以看到数据总共含有 23 列,最后一列 revenue 为票房。现在查看数据的形状。

df.shape

可以看到数据总共含有 3000 份,查看数据的基本信息。

df.info()

df.describe()

查看数据集中字符串型特征的描述。

df.describe(include=['O'])

先来观察票房前 10 的都是哪些电影。

df.sort_values(by='revenue', ascending=False).head(10)[
['title', 'revenue', 'release_date']]

可以看到,票房最高的是 《The Avengers》 ,也就是我们常说的《复仇者联盟》。其次是《 Furious 7 》,即《速度与激情 7》。第三是《Avengers: Age of Ultron》,即《复仇者联盟:奥创纪元》。

数据预处理

数据预处理主要是对数据进行清洗,填充缺失值等操作。

上映时间

在数据集中,其中有一列为 release_date,即电影的上映时间,我们先来对该列进行处理。在处理时,将电影上的年、月、日这些信息分开,以便后续的分析。

def date_features(df):
df['release_date'] = pd.to_datetime(df['release_date']) # 转换为时间戳
df['release_year'] = df['release_date'].dt.year # 提取年
df['release_month'] = df['release_date'].dt.month # 提取月
df['release_day'] = df['release_date'].dt.day # 提取日
df['release_quarter'] = df['release_date'].dt.quarter # 提取季度
return df df = date_features(df)
df['release_year'].head()

查看一下数据是否存在异常值,即电影上映时间超过 2019 年,因为收集的数据是 2019 年之前的,所以不可能存在 2019 年之后上映的电影。因此将这些 2019 年之后上映的电影视为异常值。

import numpy as np
# 查看大于 2019 的数据
df['release_year'].iloc[np.where(df['release_year'] > 2019)][:10]

从上面的结果可以看出,的确存在不少异常值,现在对这些值进行处理。

# 大于 2019 的减去 100
df['release_year'] = np.where(
df['release_year'] > 2019, df['release_year']-100, df['release_year'])
df['release_year'].iloc[np.where(df['release_year'] > 2019)][:10]

处理完成之后,可以看到,已经不存在异常值。

现在查看一下关于日期的数据是否存在缺失值。

cols = ['release_year', 'release_month',
'release_day']
df[cols].isnull().sum()

从上面的结果可以看到,关于电影上映日期的数据不存在缺失值。

现在查看一下,每个月的平均电影票房。

from matplotlib import pyplot as plt
%matplotlib inline fig = plt.figure(figsize=(14, 4)) df.groupby('release_month').agg('mean')['revenue'].plot(kind='bar', rot=0)
plt.ylabel('Revenue (100 million dollars)')

从上图可以看到,电影的上映时间主要集中在 6 月和 12 月。这可能的原因是这两段时间都是假期,因此很多同学有更多的时间去电影院看电影。所以这两段时间的电影票房要高一点。

接下来再来看每年的电影平均票房数。

release_year_mean_data = df.groupby(['release_year'])['revenue'].mean()
fig = plt.figure(figsize=(14, 5)) # 设置画布大小
plt.plot(release_year_mean_data)
plt.ylabel('Mean revenue value') # 设置 y 轴的标签
plt.title('Mean revenue Over Years') # 设置标题

从上图可以看到,电影的每年平均票房都是逐年递增的,这可能跟我们的经济增长有关,因为人们越来越有钱了,花费在精神上的消费比例也越来越大了。

接下来看电影的时长跟年份的关系。

release_year_mean_data = df.groupby(['release_year'])['runtime'].mean()
fig = plt.figure(figsize=(14, 5)) # 设置画布大小
plt.plot(release_year_mean_data)
plt.ylabel('Mean popularity value') # 设置 y 轴的标签
plt.title('Mean popularity Over Years') # 设置标题

从上图中可以发现,在 1980 年之前,电影的平均时长都是不定的,而 1980 年之后,趋向于稳定,差不多是 100 多分钟。

收藏集

现在来看 belongs_to_collection 列,先打印该列的前 5 个数据来进行观察。

for i, e in enumerate(df['belongs_to_collection'][:5]):
print(i, e)
print(type(e))

从上面的结果可以看到,该列主要包括名字、海报等信息。同时还可以看到,存在许多值为 nan ,也就是缺失值。现在统计一下存在多少个缺失值。这里需要注意的是通过判断该列的值是否是字符串来判断是否存在值或为空值。

df['belongs_to_collection'].apply(
lambda x: 1 if type(x) == str else 0).value_counts()

从上面的结果看出,在 3000 份数据中,该列的缺失值就有 2396。我们从该列中提取 name 属性。且创建一列保存是否缺失。

df['collection_name'] = df['belongs_to_collection'].apply(
lambda x: eval(x)[0]['name'] if type(x) == str else 0)
df['has_collection'] = df['belongs_to_collection'].apply(
lambda x: 1 if type(x) == str else 0)
df[['collection_name', 'has_collection']].head()

电影类型

同样的方法,把 genres 列也处理一下。

for i, e in enumerate(df['genres'][:5]):
print(i, e)

从上可以看出,genres 列主要用来存放电影的类型,例如:喜剧、剧情等。我们可以统计每中类型的电影数量,先统计每部电影都含哪些类别。

list_of_genres = list(df['genres'].apply(lambda x: [i['name']
for i in eval(x)] if type(x) == str else []).values)
list_of_genres[:5]

计算每种电影类型出现的数量。

from collections import Counter

most_common_genres = Counter(
[i for j in list_of_genres for i in j]).most_common()
most_common_genres

绘制出图形。

fig = plt.figure(figsize=(10, 6))
data = dict(most_common_genres)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True),
values, tick_label=names, color='teal')
plt.xlabel('Count')
plt.title('Movie Genre Count')
plt.show()

从上图可知,电影数量最多的题材为剧情(Drama),其次是喜剧(Comedy)。我们还可以使用词图的方法来直观的画出。先安装 词云库 wordcloud

!pip install wordcloud

画出词云图。

from wordcloud import WordCloud

plt.figure(figsize=(12, 8))
text = ' '.join([i for j in list_of_genres for i in j])
# 设置参数
wordcloud = WordCloud(max_font_size=None, background_color='white', collocations=False,
width=1200, height=1000).generate(text)
plt.imshow(wordcloud)
plt.title('Top genres')
plt.axis("off")
plt.show()

在上面的词图中,词的字体越大,表示该词数量越多,即出现的频率越高。

在该列中,我们可以提取一部电影包含类型的数量,以及该电影所属的全部类型。

df['num_genres'] = df['genres'].apply(
lambda x: len(eval(x)) if type(x) == str else 0)
df['all_genres'] = df['genres'].apply(lambda x: ' '.join(
sorted([i['name'] for i in eval(x)])) if type(x) == str else '')
top_genres = [m[0] for m in Counter(
[i for j in list_of_genres for i in j]).most_common(15)]
for g in top_genres:
df['genre_' + g] = df['all_genres'].apply(lambda x: 1 if g in x else 0)
cols = [i for i in df.columns if 'genre_' in str(i)]
df[cols].head()

在上面显示的结果中,genre_Drama、 genre_Comedy 等列即是我们所提取的特征列,其表示的含义是如果一部电影属于该类型,则在该列中的值为 1 否则为 0。这种处理思路类似我们常见的 One-Hot 编码。

前面我们统计出来每种类型的电影数量。现在统计出类型与票房和上映年份的关系。这里我们会使用到 plotly 库来进行绘图,先安装相关的绘图工具库。

!pip install plotly

导入相关的库。

import plotly.graph_objs as go
import plotly.offline as py
py.init_notebook_mode(connected=False)

画出三者的关系图。

drama = df.loc[df['genre_Drama'] == 1, ]  # 得到所有电影类型为 Drama 的数据
comedy = df.loc[df['genre_Comedy'] == 1, ]
action = df.loc[df['genre_Action'] == 1, ]
thriller = df.loc[df['genre_Thriller'] == 1, ] drama_revenue = drama.groupby(['release_year']).mean()['revenue'] # 求出票房的平均值
comedy_revenue = comedy.groupby(['release_year']).mean()['revenue']
action_revenue = action_revenue = action.groupby(
['release_year']).mean()['revenue']
thriller_revenue = thriller.groupby(['release_year']).mean()['revenue'] revenue_concat = pd.concat([drama_revenue, # 将数据合并为一份
comedy_revenue,
action_revenue,
thriller_revenue],
axis=1) revenue_concat.columns = ['drama', 'comedy', 'action', 'thriller']
revenue_concat.index = df.groupby(['release_year']).mean().index data = [go.Scatter(x=revenue_concat.index, y=revenue_concat.drama, name='drama'),
go.Scatter(x=revenue_concat.index,
y=revenue_concat.comedy, name='comedy'),
go.Scatter(x=revenue_concat.index,
y=revenue_concat.action, name='action'),
go.Scatter(x=revenue_concat.index, y=revenue_concat.thriller, name='thriller')]
# 画出图形
layout = go.Layout(dict(title='Mean Revenue by Top 4 Movie Genres Over Years',
xaxis=dict(title='Year'),
yaxis=dict(title='Revenue'),
), legend=dict(
orientation="v")) py.iplot(dict(data=data, layout=layout))

从上图可以知道,在 2000 年之后,动作(Action)题材的电影的票房在逐渐增加,这也从侧面显示了动作电影越来越受观众的青睐。

制片公司

同样的方法,现在来看制片公司(production_companies)。

for i, e in enumerate(df['production_companies'][:5]):
print(i, e)

从上面可知,同一个电影可能来源于多个制片公司。现在来画出制片公司发行的电影数量。

list_of_companies = list(df['production_companies'].apply(
lambda x: [i['name'] for i in eval(x)] if type(x) == str else []).values)
# 得到每个公司的电影发行量
most_common_companies = Counter(
[i for j in list_of_companies for i in j]).most_common(20)
fig = plt.figure(figsize=(10, 6))
data = dict(most_common_companies)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True),
values, tick_label=names, color='brown')
plt.xlabel('Count')
plt.title('Top 20 Production Company Count')
plt.show()

从上图可知,Warner Bros 制作的电影最多。Warner Bros 也即是著名的华纳兄弟娱乐公司。

同样,我们现在要从该列中提取一些重要的信息。这里与电影类型的提取类似。

df['num_companies'] = df['production_companies'].apply(
lambda x: len(x) if type(x) == str else 0)
df['all_production_companies'] = df['production_companies'].apply(
lambda x: ' '.join(sorted([i['name'] for i in eval(x)])) if type(x) == str else '')
top_companies = [m[0] for m in Counter(
[i for j in list_of_companies for i in j]).most_common(30)]
for g in top_companies:
df['production_company_' +
g] = df['all_production_companies'].apply(lambda x: 1 if g in x else 0) cols = [i for i in df.columns if 'production_company' in str(i)]
df[cols].head()

在上面的提取结果中,production_company_Warner Bros、production_company_Universal Pictures 等列即是我们所提取的列,其表示的含义是如果一部电影属于该公司出产,那么该电影在该公司所对应的的列的值为 1 否则为 0。

进行上面的提取之后,我们现在来画出几个公司制作的电影票房数量。

Warner_Bros = df.loc[df['production_company_Warner Bros.'] == 1, ]
Universal_Pictures = df.loc[df['production_company_Universal Pictures'] == 1, ]
Twentieth_Century_Fox_Film = df.loc[
df['production_company_Twentieth Century Fox Film Corporation'] == 1, ]
Columbia_Pictures = df.loc[df['production_company_Columbia Pictures'] == 1, ] Warner_Bros_revenue = Warner_Bros.groupby(['release_year']).mean()['revenue']
Universal_Pictures_revenue = Universal_Pictures.groupby(
['release_year']).mean()['revenue']
Twentieth_Century_Fox_Film_revenue = Twentieth_Century_Fox_Film.groupby(
['release_year']).mean()['revenue']
Columbia_Pictures_revenue = Columbia_Pictures.groupby(
['release_year']).mean()['revenue'] prod_revenue_concat = pd.concat([Warner_Bros_revenue,
Universal_Pictures_revenue,
Twentieth_Century_Fox_Film_revenue,
Columbia_Pictures_revenue], axis=1)
prod_revenue_concat.columns = ['Warner_Bros',
'Universal_Pictures',
'Twentieth_Century_Fox_Film',
'Columbia_Pictures'] fig = plt.figure(figsize=(13, 5))
prod_revenue_concat.agg("mean", axis='rows').sort_values(ascending=True).plot(kind='barh',
x='Production Companies',
y='Revenue',
title='Mean Revenue (100 million dollars) of Most Common Production Companies')
plt.xlabel('Revenue (100 million dollars)')

现在来分析制片公司与年份和票房的关系。

data = [go.Scatter(x=prod_revenue_concat.index, y=prod_revenue_concat.Warner_Bros, name='Warner_Bros'),
go.Scatter(x=prod_revenue_concat.index,
y=prod_revenue_concat.Universal_Pictures, name='Universal_Pictures'),
go.Scatter(x=prod_revenue_concat.index,
y=prod_revenue_concat.Twentieth_Century_Fox_Film, name='Twentieth_Century_Fox_Film'),
go.Scatter(x=prod_revenue_concat.index, y=prod_revenue_concat.Columbia_Pictures, name='Columbia_Pictures'), ] layout = go.Layout(dict(title='Mean Revenue of Movie Production Companies over Years',
xaxis=dict(title='Year'),
yaxis=dict(title='Revenue'),
), legend=dict(
orientation="v"))
py.iplot(dict(data=data, layout=layout))

出版国家

上面一小节主要分析了制片公司,现在来分析一下电影的出版国家,即电影是哪一个国家搞出来的。

for i, e in enumerate(df['production_countries'][:5]):
print(i, e)

从上面可以看到,在 production_countries 中,name 表示的是国家的全称,而 iso_3166_1 表示的是国家的简称。现在我们来看一下哪个国家出产的电影更多。

list_of_countries = list(df['production_countries'].apply(
lambda x: [i['name'] for i in eval(x)] if type(x) == str else []).values)
most_common_countries = Counter(
[i for j in list_of_countries for i in j]).most_common(20) fig = plt.figure(figsize=(10, 6))
data = dict(most_common_countries)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True),
values, tick_label=names, color='purple')
plt.xlabel('Count')
plt.title('Country Count')
plt.show()

从上图可以看出,美国出版的电影最多;其次是英国;再次是法国。而中国香港的票房几乎与中国内地持平,这似乎有点出乎意料。

同样的方法,我们现在来对电影出产国家进行特征提取。

df['num_countries'] = df['production_countries'].apply(
lambda x: len(eval(x)) if type(x) == str else 0)
df['all_countries'] = df['production_countries'].apply(lambda x: ' '.join(
sorted([i['name'] for i in eval(x)])) if type(x) == str else '')
top_countries = [m[0] for m in Counter(
[i for j in list_of_countries for i in j]).most_common(25)]
for g in top_countries:
df['production_country_' +
g] = df['all_countries'].apply(lambda x: 1 if g in x else 0) cols = [i for i in df.columns if 'production_country' in str(i)]
df[cols].head()

在所提取到的特征列中,如果一部电影属于某个国家,那么该电影在某个国家所对应的的列中的值为 1 ,否则为 0。

电影语言

我们都知道,不同国家可能使用不同的语言,所以电影的语言也不尽相同。现在来看电影语言列(spoken_languages)。

for i, e in enumerate(df['spoken_languages'][:5]):
print(i, e)

在该列中,name 表示电影语言,iso_639_1 表示语言的简写。同时还可以看到,一部电影可能还有多个语言。现在对语言进行统计,查看一下什么语言的电影最多。

list_of_languages = list(df['spoken_languages'].apply(
lambda x: [i['name'] for i in eval(x)] if type(x) == str else []).values) most_common_languages = Counter(
[i for j in list_of_languages for i in j]).most_common(20) fig = plt.figure(figsize=(10, 6))
data = dict(most_common_languages)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True), values, tick_label=names)
plt.xlabel('Count')
plt.title('Language Count')
plt.show()

可能你也已经猜到,英语肯定是最多的,从上图显示的结果也的确如此。同样的方法来对语言提取特征。

df['num_languages'] = df['spoken_languages'].apply(
lambda x: len(eval(x)) if type(x) == str else 0)
df['all_languages'] = df['spoken_languages'].apply(lambda x: ' '.join(
sorted([i['iso_639_1'] for i in eval(x)])) if type(x) == str else '')
top_languages = [m[0] for m in Counter(
[i for j in list_of_languages for i in j]).most_common(30)]
for g in top_languages:
df['language_' +
g] = df['all_languages'].apply(lambda x: 1 if g in x else 0)
cols = [i for i in df.columns if 'language_' in str(i)]
df[cols].head()

关键字

在数据集中,存在一个关键字列(Keywords)。我们使用同样的方法来处理该列。

for i, e in enumerate(df['Keywords'][:5]):
print(i, e)

关键字表示的一部电影的主题内容。例如在犯罪题材的电影中,关键字就可能有警察、毒枭等关键字。现在对关键字进行统计。

list_of_keywords = list(df['Keywords'].apply(
lambda x: [i['name'] for i in eval(x)] if type(x) == str else []).values) most_common_keywords = Counter(
[i for j in list_of_keywords for i in j]).most_common(20) fig = plt.figure(figsize=(10, 6))
data = dict(most_common_keywords)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True),
values, tick_label=names, color='purple')
plt.xlabel('Count')
plt.title('Top 20 Most Common Keyword Count')
plt.show()

从上面的结果看出,女导演(woman director)出现的次数最多。现在我们可以分析一下,一些电影题材的关键字。

text_drama = " ".join(review for review in drama['Keywords'].apply(
lambda x: ' '.join(sorted([i['name'] for i in eval(x)])) if type(x) == str else ''))
text_comedy = " ".join(review for review in comedy['Keywords'].apply(
lambda x: ' '.join(sorted([i['name'] for i in eval(x)])) if type(x) == str else ''))
text_action = " ".join(review for review in action['Keywords'].apply(
lambda x: ' '.join(sorted([i['name'] for i in eval(x)])) if type(x) == str else ''))
text_thriller = " ".join(review for review in thriller['Keywords'].apply(
lambda x: ' '.join(sorted([i['name'] for i in eval(x)])) if type(x) == str else '')) wordcloud1 = WordCloud(background_color="white",
colormap="Reds").generate(text_drama)
wordcloud2 = WordCloud(background_color="white",
colormap="Blues").generate(text_comedy)
wordcloud3 = WordCloud(background_color="white",
colormap="Greens").generate(text_action)
wordcloud4 = WordCloud(background_color="white",
colormap="Greys").generate(text_thriller) fig = plt.figure(figsize=(25, 20)) plt.subplot(221)
plt.imshow(wordcloud1, interpolation='bilinear')
plt.title('Drama Keywords')
plt.axis("off") plt.subplot(222)
plt.imshow(wordcloud2, interpolation='bilinear')
plt.title('Comedy Keywords')
plt.axis("off")
plt.show() fig = plt.figure(figsize=(25, 20)) plt.subplot(223)
plt.imshow(wordcloud3, interpolation='bilinear')
plt.title('Action Keywords')
plt.axis("off") plt.subplot(224)
plt.imshow(wordcloud4, interpolation='bilinear')
plt.title('Thriller Keywords')
plt.axis("off")
plt.show()

从上面的词云图可以看出,剧情(Drama)类和喜剧类(Comedy)电影的关键字大都都含有家庭(family)、女性(woman)基于小说改编(based novel)等,而动作类(Action)和犯罪类(Thriller)则出现警察(police)、死亡(death)等关键词最多。

同样的方法来对该列进行特征提取。

df['num_Keywords'] = df['Keywords'].apply(
lambda x: len(eval(x)) if type(x) == str else 0)
df['all_Keywords'] = df['Keywords'].apply(lambda x: ' '.join(
sorted([i['name'] for i in eval(x)])) if type(x) == str else '')
top_keywords = [m[0] for m in Counter(
[i for j in list_of_keywords for i in j]).most_common(30)]
for g in top_keywords:
df['keyword_' + g] = df['all_Keywords'].apply(lambda x: 1 if g in x else 0)
cols = [i for i in df.columns if 'keyword_' in str(i)]
df[cols].head()

演员

电影的好坏,演员在很多层面上也取到一定的作用。因此现在来看演员列。

for i, e in enumerate(df['cast'][:1]):
print(i, e)

从上面的结果可以看到,演员的信息包括性别(gender)、姓名(name)等。现在统计一下哪些演员演过的电影最多。

list_of_cast_names = list(df['cast'].apply(
lambda x: [i['name'] for i in eval(x)] if type(x) == str else []).values)
most_common_keywords = Counter(
[i for j in list_of_cast_names for i in j]).most_common(20) fig = plt.figure(figsize=(10, 6))
data = dict(most_common_keywords)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True),
values, tick_label=names, color='purple')
plt.xlabel('Count')
plt.title('Top 20 Most Common Keyword Count')
plt.show()

从上的结果可以看到,塞缪尔·杰克逊(Samuel L. Jackson)演过的电影最多。对于很多中国人来说,可能很多的国人名字不是很容易记住,我们现在来看一下,这些演员的图片。

相信看过美国大片的你对上面的演员会很熟悉。现在来提取特征。

df['num_cast'] = df['cast'].apply(
lambda x: len(eval(x)) if type(x) == str else 0)
df['all_cast'] = df['cast'].apply(lambda x: ' '.join(
sorted([i['name'] for i in eval(x)])) if type(x) == str else '')
top_cast_names = [m[0] for m in Counter(
[i for j in list_of_cast_names for i in j]).most_common(30)]
for g in top_cast_names:
df['cast_name_' + g] = df['all_cast'].apply(lambda x: 1 if g in x else 0)
cols = [i for i in df.columns if 'cast_name' in str(i)]
df[cols].head()

画出参演数量最多的演员所获得的电影票房情况。

cast_name_Samuel_L_Jackson = df.loc[df['cast_name_Samuel L. Jackson'] == 1, ]
cast_name_Robert_De_Niro = df.loc[df['cast_name_Robert De Niro'] == 1, ]
cast_name_Morgan_Freeman = df.loc[df['cast_name_Morgan Freeman'] == 1, ]
cast_name_J_K_Simmons = df.loc[df['cast_name_J.K. Simmons'] == 1, ] cast_name_Samuel_L_Jackson_revenue = cast_name_Samuel_L_Jackson.mean()[
'revenue']
cast_name_Robert_De_Niro_revenue = cast_name_Robert_De_Niro.mean()['revenue']
cast_name_Morgan_Freeman_revenue = cast_name_Morgan_Freeman.mean()['revenue']
cast_name_J_K_Simmons_revenue = cast_name_J_K_Simmons.mean()['revenue'] cast_revenue_concat = pd.Series([cast_name_Samuel_L_Jackson_revenue,
cast_name_Robert_De_Niro_revenue,
cast_name_Morgan_Freeman_revenue,
cast_name_J_K_Simmons_revenue]) cast_revenue_concat.index = ['Samuel L. Jackson',
'Robert De Niro',
'Morgan Freeman',
'J.K. Simmons', ] fig = plt.figure(figsize=(13, 5))
cast_revenue_concat.sort_values(ascending=True).plot(
kind='barh', title='Mean Revenue (100 million dollars) by Top 4 Most Common Cast')
plt.xlabel('Revenue (100 million dollars)')

现在对演员性别等特征进行提取。

list_of_cast_genders = list(df['cast'].apply(
lambda x: [i['gender'] for i in eval(x)] if type(x) == str else []).values)
list_of_cast_characters = list(df['cast'].apply(
lambda x: [i['character'] for i in eval(x)] if type(x) == str else []).values) df['genders_0'] = sum([1 for i in list_of_cast_genders if i == 0])
df['genders_1'] = sum([1 for i in list_of_cast_genders if i == 1])
df['genders_2'] = sum([1 for i in list_of_cast_genders if i == 2])
top_cast_characters = [m[0] for m in Counter(
[i for j in list_of_cast_characters for i in j]).most_common(15)]
for g in top_cast_characters:
df['cast_character_' +
g] = df['cast'].apply(lambda x: 1 if type(x) == str and g in x else 0)
cols = [i for i in df.columns if 'cast_cha' in str(i)]
dfcols].head()

制作团队

一部电影的好坏与制作团队的也是分不开的,现在来看电影的制作团队。

for i, e in enumerate(df['crew'][:1]):
print(i, e)

从上面的结果可以看出,制作团队包括导演,副导演、电影配乐等信息。现在来统计一下团队人物制作的电影数量。

list_of_crew_names = list(df['crew'].apply(
lambda x: [i['name'] for i in eval(x)] if type(x) == str else []).values)
most_common_keywords = Counter(
[i for j in list_of_crew_names for i in j]).most_common(20) fig = plt.figure(figsize=(10, 6))
data = dict(most_common_keywords)
names = list(data.keys())
values = list(data.values()) plt.barh(sorted(range(len(data)), reverse=True),
values, tick_label=names, color='purple')
plt.xlabel('Count')
plt.title('Top 20 Most Common Keyword Count')
plt.show()

从上面可以看到 avy Kaufman,Robert Rodriguez 等导演参与制作的电影最多。现在进行特征提取。

df['num_crew'] = df['crew'].apply(
lambda x: len(eval(x)) if type(x) == str else 0)
df['all_crew'] = df['crew'].apply(lambda x: ' '.join(
sorted([i['name'] for i in eval(x)])) if type(x) == str else '')
top_crew_names = [m[0] for m in Counter(
[i for j in list_of_crew_names for i in j]).most_common(30)]
for g in top_crew_names:
df['crew_name_' +
g] = df['all_crew'].apply(lambda x: 1 if type(x) == str and g in x else 0)
cols = [i for i in df.columns if 'crew_name' in str(i)]
df[cols].head()

同样对排名前 4 位导演进行分析。

crew_name_Avy_Kaufman = df.loc[df['crew_name_Avy Kaufman'] == 1, ]
crew_name_Robert_Rodriguez = df.loc[df['crew_name_Robert Rodriguez'] == 1, ]
crew_name_Deborah_Aquila = df.loc[df['crew_name_Deborah Aquila'] == 1, ]
crew_name_James_Newton_Howard = df.loc[df['crew_name_James Newton Howard'] == 1, ] crew_name_Avy_Kaufman_revenue = crew_name_Avy_Kaufman.mean()['revenue']
crew_name_Robert_Rodriguez_revenue = crew_name_Robert_Rodriguez.mean()[
'revenue']
crew_name_Deborah_Aquila_revenue = crew_name_Deborah_Aquila.mean()['revenue']
crew_name_James_Newton_Howard_revenue = crew_name_James_Newton_Howard.mean()[
'revenue'] crew_revenue_concat = pd.Series([crew_name_Avy_Kaufman_revenue,
crew_name_Robert_Rodriguez_revenue,
crew_name_Deborah_Aquila_revenue,
crew_name_James_Newton_Howard_revenue])
crew_revenue_concat.index = ['Avy Kaufman',
'Robert Rodriguez',
'Deborah Aquila',
'James Newton Howard'] fig = plt.figure(figsize=(13, 5))
crew_revenue_concat.sort_values(ascending=True).plot(
kind='barh', title='Mean Revenue (100 million dollars) by Top 10 Most Common Crew')
plt.xlabel('Revenue (100 million dollars)')

从上面的显示结果可以看到,电影票房最高的制作人员是詹姆斯·纽顿·霍华德(James Newton Howard),其是一名音乐家,主要负责电影的配乐。

特征工程

因为票房数据并不平衡,所以要用对数变换来处理倾斜的数据。

fig = plt.figure(figsize=(15, 10))

plt.subplot(221)
df['revenue'].plot(kind='hist', bins=100)
plt.title('Distribution of Revenue')
plt.xlabel('Revenue') plt.subplot(222)
np.log1p(df['revenue']).plot(kind='hist', bins=100)
plt.title('Train Log Revenue Distribution')
plt.xlabel('Log Revenue')

对预计票房列也做同样的变换。

fig = plt.figure(figsize=(15, 10))

plt.subplot(221)
df['budget'].plot(kind='hist', bins=100)
plt.title('Train Budget Distribution')
plt.xlabel('Budget') plt.subplot(222)
np.log1p(df['budget']).plot(kind='hist', bins=100)
plt.title('Train Log Budget Distribution')
plt.xlabel('Log Budget')
plt.show()

前面我们主要提取了时间、演员、导演等特征,而数据集还存在电影标题、电影编号等特征,这些特征对预测结果可能没有多大影响,因此,现在删除掉这些特征,仅保留前面我们所提取的特征列。

drop_columns = ['homepage', 'imdb_id', 'poster_path', 'status',
'title', 'release_date', 'tagline', 'overview',
'original_title', 'all_genres', 'all_cast',
'original_language', 'collection_name', 'all_crew',
'belongs_to_collection', 'genres', 'production_companies',
'all_production_companies', 'production_countries',
'all_countries', 'spoken_languages', 'all_languages',
'Keywords', 'all_Keywords', 'cast', 'crew'] df_drop = df.drop(drop_columns, axis=1).dropna(axis=1, how='any')

查看最终的数据。

df_drop.head()

划分训练集和测试集。

from sklearn.model_selection import train_test_split

data_X = df_drop.drop(['id', 'revenue'], axis=1)
data_y = np.log1p(df_drop['revenue'])
train_X, test_X, train_y, test_y = train_test_split(
data_X, data_y.values, test_size=0.2)

构建预测模型,并进行训练和预测。这里使用线性回归的改进版模型 Lasso 。

from sklearn.linear_model import Lasso
from sklearn.metrics import mean_squared_error
model = Lasso()
model.fit(train_X, train_y) # 构建模型
y_pred = model.predict(test_X) # 训练模型
mean_squared_error(y_pred, test_y) # 预测模型

Lasso 回归的预测结果与真实值的均方差为 6 到 7 左右。同样的方法,使用岭回归(Ridge)重新构建模型。

from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
model = Ridge()
model.fit(train_X, train_y)
y_pred = model.predict(test_X)
mean_squared_error(y_pred, test_y)

从上面的结果可知,Ridge 回归要相比 Lasso 回归要好一点。

总结

本次是对电影票房进行预测,是一个典型的回归任务。在数据预处理时,主要是通过手动来提取特征,并可视化。在数据预处理完成之后,我们还对原始数据的票房列和预估列进行了平滑。在构建预测模型时,主要使用常见的 Lasso 模型和 Ridge 模型。

kaggle——TMDB 电影票房收入预测的更多相关文章

  1. Neo4j入门之中国电影票房排行浅析

    什么是Neo4j?   Neo4j是一个高性能的NoSQL图形数据库(Graph Database),它将结构化数据存储在网络上而不是表中.它是一个嵌入式的.基于磁盘的.具备完全的事务特性的Java持 ...

  2. python实现的、带GUI界面电影票房数据可视化程序

    代码地址如下:http://www.demodashi.com/demo/14588.html 详细说明: Tushare是一个免费.开源的python财经数据接口包.主要实现对股票等金融数据从数据采 ...

  3. python实现的电影票房数据可视化

    代码地址如下:http://www.demodashi.com/demo/14275.html 详细说明: Tushare是一个免费.开源的python财经数据接口包.主要实现对股票等金融数据从数据采 ...

  4. 【python数据分析实战】电影票房数据分析(一)数据采集

    目录 1.获取url 2.开始采集 3.存入mysql 本文是爬虫及可视化的练习项目,目标是爬取猫眼票房的全部数据并做可视化分析. 1.获取url 我们先打开猫眼票房http://piaofang.m ...

  5. 【python数据分析实战】电影票房数据分析(二)数据可视化

    目录 图1 每年的月票房走势图 图2 年票房总值.上映影片总数及观影人次 图3 单片总票房及日均票房 图4 单片票房及上映月份关系图 在上一部分<[python数据分析实战]电影票房数据分析(一 ...

  6. kaggle之电影评论文本情感分类

    电影文本情感分类 Github地址 Kaggle地址 这个任务主要是对电影评论文本进行情感分类,主要分为正面评论和负面评论,所以是一个二分类问题,二分类模型我们可以选取一些常见的模型比如贝叶斯.逻辑回 ...

  7. python网络爬虫(11)近期电影票房或热度信息爬取

    目标意义 为了理解动态网站中一些数据如何获取,做一个简单的分析. 说明 思路,原始代码来源于:https://book.douban.com/subject/27061630/. 构造-下载器 构造分 ...

  8. VR电影这一新概念在中国电影道路上的探索

    在12月的一个下午,Kevin Geiger正在进行关于VR中的故事讲述的一次再普通不过的演讲.地点是北京电影学院里一个围的水泄不通的场馆,他鼓励大家都来参与电影制作,无论是导演.演员还是电影行业的任 ...

  9. ActiveReports 大数据分析报告:2018中国电影再次迎来黄金时代

    回顾2018,中国电影市场收获颇丰.先是凭借春节档<红海行动>.<唐人街探案>双双实现30亿票房突破,而后暑期档火力全开,<我不是药神>.<西虹市首富> ...

随机推荐

  1. delphi关闭和禁用Windows服务

    function StopServices(const SvrName: string): Boolean; var SCH, SvcSCH: SC_HANDLE; SS: TServiceStatu ...

  2. [题解] LuoguP4091 [HEOI2016/TJOI2016]求和

    传送门 首先我们来看一下怎么求\(S(m,n)\). 注意到第二类斯特林数的组合意义就是将\(m\)个不同的物品放到\(n\)个没有区别的盒子里,不允许有空盒子的方案数. 那么将\(m\)个不同的物品 ...

  3. BZOJ 3332

    题解:给边赋上权值,然后求最大生成树,如果不符合那就无解 证明:留坑 #include<iostream> #include<cstdio> #include<cstri ...

  4. .NET 一次读取几百条数据优化,从原来30分钟优化到30秒

    1.全部数据读取到内存, 不要使用string,而是使用stringbuilder,stringbuilder的效率非常高 2.添加到数据库 不要使用excute,而是使用事务,几百万条数据会请求数据 ...

  5. hive表字段注释显示乱码问题

    创建了一张hive表,对字段增加了注释,比如comment '注释内容' 之类的,但是在hive client查看时候却是乱码 比如: create table test_ultraedit ( id ...

  6. 【BZOJ2400】Optimal Marks

    题意 定义无向图中的一条边的值为:这条边连接的两个点的值的异或值. 定义一个无向图的值为:这个无向图所有边的值的和. 给你一个有 \(n\) 个结点 \(m\) 条边的无向图.其中的一些点的值是给定的 ...

  7. Essay写作没逻辑不要慌,掌握这几点即可

    今天文章的内容,真的是很多很多留学生的最大的问题,没有之一:逻辑.是的,你没有看错,也不用惊讶.大家的essay写作得分不高,很多时候不是因为语言问题.排除很多细节表达的不足,更让教授头疼的居然是:内 ...

  8. hdu4632 Palindrome subsequence 回文子序列个数 区间dp

    Palindrome subsequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65535 K (Java/ ...

  9. EUI库 - EXML

        EXML是可以运行时加载解析的   <e:Skin class="skins.ButtonSkin" states="up,down,disabled&qu ...

  10. 洛谷 P1776 宝物筛选(多重背包)

    题目传送门 解题思路: 可以转化成0-1背包来做,但暴力转化的话,时间不允许.所以就用了一个二进制划分的方法,将m个物品分成2,4,8,16,32......(2的次方)表示,可以证明这些数通过一定组 ...