python 豆瓣电影分析

437 阅读6分钟

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)

在这里插入图片描述