1. 项目背景
1.1 项目说明
本文利用豆瓣电影平台的真实数据,进行实操分析。现市面上国产烂片横行,我们这里针对电影的各属性进行分析。
1.2 提出问题
我们这里提出一个问题,烂片的形成与什么有关,怎样才能更大可能性的回避观看、制作烂片。
1.3 数据理解
1)数据来源于豆瓣电影平台的电影信息()
2)数据字段说明
电影数据表共有 12 个字段, 共计 2457 条记录。
电影数据表所有字段如下:
| 电影名称 | 豆瓣评论数 | 豆瓣评分 | 上映日期 | 主演 | 制片国家/地区 | 又名 | 导演 | 片长 | 类型 | 编剧 | 演员 |
2. 数据处理
2.1 数据导入
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style('whitegrid')
plt.rcParams["font.sans-serif"] = ["SimHei"] # 设置图片上的中文显示正常
plt.rcParams["axes.unicode_minus"] = False # 设置图片上正负号显示正常
import warnings # 不显示可运行的警告
warnings.filterwarnings('ignore')
movie = pd.read_excel('moviedata.xlsx')
movie = movie[movie['豆瓣评分'].notnull()] # 取出评分不为空的记录
movie.shape # 查看规模 (2306, 12)
2.2 转换特征属性
movie.info() # 查看特征属性
# 查看空缺值分布情况
plt.figure(figsize=(8,8))
sns.heatmap(movie.isnull(), cmap='viridis')
plt.xticks( rotation=30, ha='right') # 使x轴的坐标名倾斜30度
# 由图,因数据残缺分布较为分散,所以不在这一步对整体进行大规模的转换,用到的时候再对数据进行修改
movie['电影名称'] = movie['电影名称'].astype('str')
# movie['上映日期'] =
movie['豆瓣评论数'] = movie['豆瓣评论数'].astype('int')
2.3 重复值处理
movie['电影名称'].nunique() #2306
先对整体进行一次清洗。在之后的分析中,由于数据残缺较多且难以合理填补但统一删除后会造成大幅缺失,所以会多次进行清洗。
3. 问题分析
这是一个对电影特征进行构建和分析的需求,需要我们利用电影的特征进行描述及分析,为观影及制作提供建议。
我们将问题具体落实为以下五个:
- 烂片标准的制定
- 烂片类型的偏好
- 主演、主演数量是否和烂片有关
- 导演是否和烂片有关
- 和什么国家合作更可能生产烂片
我们可以从这些角度分析出豆瓣电影烂片的影响因素,从而为观影及电影制作给出建议及意见
因此根据以上问题,将其拆解为以下三个具体维度来分析:
- 烂片与剧本类型
- 烂片与制作方
- 烂片与主演/导演
3.1 烂片与剧本类型
3.1.1 烂片标准的制定
首先我们通过绘制‘豆瓣评分’的直方图、箱型图来查看数据分布。
fig, axs = plt.subplots(2,1,figsize=(25,12))
axs[0].hist(movie['豆瓣评分'], bins=50, density=True, facecolor='g', edgecolor='k', linewidth=1.2, alpha=0.5)
axs[1].boxplot(movie['豆瓣评分'], vert=False, whiskerprops={'color':'orange', 'linestyle':'-.'}) # 横的箱型图
axs[1].yaxis.grid(True)
axs[0].xaxis.grid(True)
axs[0].yaxis.grid(True)
axs[0].set_title('豆瓣评分数据分布-直方图', fontsize=22)
axs[1].set_title('豆瓣评分数据分布-箱型图', fontsize=22)
axs[0].set_ylabel('Frequency', fontsize=18)
axs[1].set_xlabel('豆瓣评分', fontsize=18)
可以看出,评分整体呈略微的左偏趋势。从图中也可发现用户评分时多数喜欢往以0.0、0.5为结尾的数字上靠。
本文取上四分位数为烂片标准。
movie['豆瓣评分'].quantile(0.25) # 4.3
筛选出烂片数据,并进行排名,找到 烂片榜的TOP20
movie_lan = movie[movie['豆瓣评分'] < 4.3]
movie_lan.sort_values(['豆瓣评分'])[0:20]
按上映日期对电影数据制图
# 通过‘上映日期’筛选出每个电影的上映年份
movie_year = movie[movie['上映日期'].notnull()]
movie_year['上映日期'] = movie_year['上映日期'].astype(str).str.strip()
movie_year = movie_year[movie_year['上映日期'].map(lambda x: len(x)>=4)][['电影名称','导演','豆瓣评分','上映日期']] # 把不详、暂无、NaN的数据都过滤掉
movie_year['上映年份'] = movie_year['上映日期'].map(lambda x: x[:4]) # 取年份
movie_year.reset_index(drop=True)
movie_year_group1 = movie_year.groupby(by='上映年份')['电影名称'].count()
movie_year_group2 = movie_year[movie_year['豆瓣评分']>4.3].groupby(by='上映年份')['电影名称'].count()
plt.figure(figsize=(12,6))
movie_year_group1.plot(label='电影数量')
movie_year_group2.plot(label='烂片数量')
plt.title('不同年份的电影上映数', fontsize=15)
plt.ylabel('电影数量')
plt.legend(loc=0)
3.1.2 什么类型的电影烂片最多
movie_all_type = movie[movie['类型'].notnull()]
movie_lan_type = movie_lan[movie_lan['类型'].notnull()]
movie_all_type.shape, movie_lan_type.shape # ((2100,12),(515,12))
movie_all_type = movie_all_type.reset_index()
movie_lan_type = movie_lan_type.reset_index()
movie_all_type['类型'] = movie_all_type['类型'].str.strip().str.replace(' ','')
movie_lan_type['类型'] = movie_lan_type['类型'].str.strip().str.replace(' ','')
df_type1 = pd.DataFrame(pd.Series(np.concatenate(movie_all_type['类型'].map(lambda x: x.split("/"))))\
.value_counts().sort_index()).reset_index()
df_type2 = pd.DataFrame(pd.Series(np.concatenate(movie_lan_type['类型'].map(lambda x: x.split("/"))))\
.value_counts().sort_index()).reset_index()
df_type = pd.merge(df_type1, df_type2, on='index', suffixes=('_typeCount', '_(bad)typeCount'))
df_type['probability'] = df_type['0_(bad)typeCount']/df_type['0_typeCount']
df_type = df_type.rename(columns={"index": "类型", "0_typeCount": "电影数", "0_(bad)typeCount":'烂片数','probability':'烂片率'})
df_type.sort_values('烂片率', ascending=False).head(20).reset_index(drop=True)
情色、恐怖、惊悚类型的电影相对其他类型更盛产烂片,犯罪、战争、音乐类型的电影则较少为烂片。
3.2 和哪个国家合拍更容易产生烂片
movie_area = movie[movie['制片国家/地区'].notnull()]
movie_area['制片国家/地区'] = movie_area['制片国家/地区'].str.strip().str.replace(' ','')
lst_area = list()
for i in range(len(movie_area)):
for j in movie_area['制片国家/地区'].iloc[i].split('/'):
if j not in ['中国大陆','中国','台湾','香港','中国香港']:
lst_area.append(i)
else:
pass
df_area = movie_area.iloc[lst_area][movie_area['制片国家/地区'].str.contains('/')]
df_area = df_area[df_area['制片国家/地区'].str.contains('中国大陆|中国|台湾|香港|中国台湾|中国香港')].reset_index(drop=True)
df_area.drop_duplicates(subset=['电影名称'],inplace=True)
df_area.reset_index(drop=True, inplace=True)
df_area_lan1 = df_area[df_area['豆瓣评分']<4.3].reset_index()
df_area_lan = pd.DataFrame(pd.Series(np.concatenate(df_area_lan1['制片国家/地区'].map(lambda area: area.split("/"))))\
.value_counts()).reset_index()
df_area_all = pd.DataFrame(pd.Series(np.concatenate(df_area['制片国家/地区'].map(lambda area: area.split("/"))))\
.value_counts()).reset_index()
df_area_all = df_area_all[df_area_all.iloc[:,1] >= 3]
df_area_lan = df_area_lan[~df_area_lan['index'].str.contains('中国大陆|中国|台湾|香港|中国香港')]
df_area_all = df_area_all[~df_area_all['index'].str.contains('中国大陆|中国|台湾|香港|中国香港')]
df0_area = pd.merge(df_area_all, df_area_lan, how='left', left_on='index', right_on='index', suffixes=('_all','_lan'))
df0_area['probability'] = df0_area['0_lan']/df0_area['0_all']
df0_area = df0_area.sort_values(by=['probability'], ascending=False)
df0_area = df0_area.rename(columns={"index": "制片国家/地区", "0_all": "电影数", "0_lan":'烂片数','probability':'可能性'})
df0_area = df0_area.fillna(0)
df0_area['烂片数'] = df0_area['烂片数'].astype(int)
df0_area.reset_index(drop=True)
当分析合作电影数不受限时,与英国合作拍电影出烂片的可能性最高,且可能性竟高达 0.75 ;而和加拿大、澳大利亚合作的烂片率为 0 ,这显然是有些不合理的。
只针对合拍电影数大于10的国家而言的话,烂片率则在美国、韩国、法国、日本四国中依次走低。其中美国烂片率为 0.280 最高,日本烂片率低达0.067。
3.3 主演是否和烂片有关
3.3.1 主演数
movie['主演'].isnull().sum() # 95
movie_star = movie[movie['主演'].notnull()]
movie_star['主演'] = movie_star['主演'].str.strip().str.replace(' ','')
movie_star['主演数'] = movie_star['主演'].map(lambda x: len(x.split('/')))
def starNum(x):
"""
根据‘主演数’给定分组标签
"""
if x in [1,2]:
return '1-2人'
elif x in [3,4]:
return '3-4人'
elif x in [5,6]:
return '5-6人'
elif x in [7,8,9]:
return '7-9人'
else:
return '10人及以上'
movie_star['主演数分组'] = movie_star['主演数'].apply(lambda x: starNum(x))
new_index=['1-2人','3-4人','5-6人','7-9人','10人及以上']
movie_star_all = pd.DataFrame(movie_star.groupby(['主演数分组'])['电影名称'].count()).reindex(new_index)
movie_star_lan = pd.DataFrame(movie_star[movie_star['豆瓣评分']<4.3].groupby(['主演数分组'])['电影名称'].count()).reindex(new_index)
movie0_star = pd.merge(movie_star_all, movie_star_lan, left_on='主演数分组', right_on='主演数分组')
movie0_star['烂片率'] = movie0_star['电影名称_y']/movie0_star['电影名称_x']
movie0_star.rename(columns={'电影名称_x':'电影数','电影名称_y':'烂片数'})
3.3.2 主演人
针对具体演员分析他们的烂片产出率。
movie_actor = movie[movie['主演'].notnull()]
movie_actor['主演'] = movie_actor['主演'].str.strip().str.replace(' ','')
movie_actor = movie_actor.reset_index(drop=True)
movie_actor_all = pd.DataFrame(pd.Series(np.concatenate(movie_actor['主演'].map(lambda x: x.split('/')))).value_counts()).reset_index()
movie_actor_lan = movie_actor[movie_actor['豆瓣评分']<4.3] .reset_index()
movie_actor_lan = pd.DataFrame(pd.Series(np.concatenate(movie_actor_lan['主演'].map(lambda x: x.split('/')))).value_counts()).reset_index()
movie0_actor = pd.merge(movie_actor_all, movie_actor_lan, left_on='index', right_on='index')
movie0_actor['烂片率'] = movie0_actor['0_y']/movie0_actor['0_x']
movie0_actor = movie0_actor.sort_values('烂片率', ascending=False)
movie_goodActor = movie0_actor[(movie0_actor['烂片率']<0.1) & (movie0_actor['0_x']>10)].reset_index(drop=True).iloc[-5:]\
.sort_values('烂片率', ascending=True).reset_index(drop=True)
movie_goodActor.rename(columns={'index':'演员姓名','0_x':'电影数','0_y':'烂片数'})
令我们感到欣慰的是古天乐高居榜首,并在烂片率少的榜单里的前五出现了两位女性演员的名字。
下面列举几位热度较高的演员进行截取展示。
movie0_actor = movie0_actor[movie0_actor['index'].isin (['吴亦凡','范冰冰','杨幂','黄晓明','甄子丹','刘亦菲'])].reset_index(drop=True)
movie0_actor.rename(columns={'index':'演员姓名','0_x':'电影数','0_y':'烂片数'})
3.4 不同导演电影产量情况
3.4.1 查看不同导演的烂片比例
movie_director_all = movie[movie['导演'].notnull()].reset_index()
movie_director_lan = movie_lan[movie_lan['导演'].notnull()].reset_index()
movie_director_all['导演'] = movie_director_all['导演'].str.strip().str.replace(' ','')
movie_director_lan['导演'] = movie_director_lan['导演'].str.strip().str.replace(' ','')
movie_director_all = pd.DataFrame(pd.Series(np.concatenate(movie_director_all['导演'].map(lambda x: x.split("/"))))\
.value_counts()).reset_index()
movie_director_lan = pd.DataFrame(pd.Series(np.concatenate(movie_director_lan['导演'].map(lambda x: x.split("/"))))\
.value_counts()).reset_index()
# 筛选出电影作品数 >=10 的导演
movie_director_all.iloc[:,1] = movie_director_all.iloc[:,1].astype(int)
movie_director_all = movie_director_all[movie_director_all.iloc[:,1]>=10]
movie0_director = pd.merge(movie_director_all, movie_director_lan, left_on='index', right_on='index')
movie0_director['烂片率'] = movie0_director['0_y']/movie0_director['0_x']
movie0_director = movie0_director.rename(columns={'index':'导演','0_x':'电影数','0_y':'烂片数'})
movie0_director.sort_values(by=['烂片率'],ascending=False)