Kiggle电影数据分析与可视化(pandas)
前言
本次项目展示了基于 5000+ 条电影数据,分别围绕电影市场、电影观众、电影发行方等多方面进行的各项数据分析。
数据清洗
1. 导入包
python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sn
import warnings
# 忽略警告
warnings.filterwarnings("ignore")
#导入词云包
from wordcloud import WordCloud,STOPWORDS,ImageColorGenerator
%matplotlib inline
2. 导入数据
ini
movie = pd.read_csv('tmdb_5000_movies.csv')
credits = pd.read_csv("tmdb_5000_credits.csv")
scss
# 观察数据类型
movie.info()
credits.info()
根据info()信息,发现movies中 homepage runtime tagline 有缺失值,但是这些字段对于我们后期的分析无关,所以可以不做处理。
3. 去重
scss
len(movie.id.unique())
-- 4803 与数据表长度对应 无重复值
// 或者
movie.duplicated().sum()
-- 0 无重复值
ini
# 若有重复值,如下代码删除即可
movie.drop_duplicates(inplace=True)
4. 去空
由info()得知,没有空数据,所以无需处理
5. 规范数据
5.1 两表连接
将movie和credits通过共同列id进行连接
ini
# 使用merge函数,movie左连接credits
movie_credits = movie.merge(credits,left_on = "id",right_on = "movie_id",how = "left")
5.2 读取json数据
观察数据,发现geners、keywords、cast列等都是json格式,对json数据进行处理
ini
json_columns = ["genres","keywords","production_companies","production_countries","cast","crew"]
# 使用json.loads()函数对以上列进行读取
for column in json_columns:
movie_credits[column] = movie_credits[column].apply(json.loads)
5.2 提取数据
根据上一步,我们已经将json数据类型转化为python对象,但是我们观察genres
name后面的值才是我们真正想要的类型,所以需要将数据提取出来
python
# 定义一个提取函数
# 传入一个列,遍历列中的每一行,再遍历每一行中的每个字典,将字典中为name的key的value提取出来
def extract_name(column):
# 此处用列表推导式,col将是列表的格式
col = [[dic["name"] for dic in row] for row in column]
return col
ini
# 待提取的列
to_extract_col = ["genres","production_companies","production_countries"]
# 将原来列中的数据覆盖
for column in to_extract_col:
movie_credits[column] = extract_name(movie_credits[column])
# 根据分析需求 cast 和 keywords列中含有多个value,我们只提取前几个
movie_credits['actors'] = [[dic["name"] for dic in row[0:4]] for row in movie_credits["cast"]] # 演员取前四个
movie_credits['keywords'] = [[dic["name"] for dic in row[0:5]] for row in movie_credits["keywords"]] # 关键字取前五
ini
# 为了方便后续的分析,只保留genres中第一个值
movie_credits.genres = movie_credits.genres.str.get(0)
movie_credits.genres
css
# 提取release_date中的年份
movie_credits['year'] = pd.to_datetime(movie_credits["release_date"]).apply(lambda x:x.year)
ini
# 将我们想要的列拼接成一个新的表
data = movie_credits[["id","original_title","genres","keywords","budget","revenue","popularity","production_companies","production_countries","release_date","vote_average","vote_count","actors","director","year"]].dropna()
data.year = data.year.astype(int)
5.3 规范数据
ini
# 重命名列
data.rename(columns={"original_title":"movieName"},inplace = True)
kotlin
data.describe()
bash
# 发现投票人数波动过大,并且评价人数太少的电影评分也不具备参考价值,筛选投票人数大于 40 的
# 上映时间跨度大,过早的电影对我们的分析没有参考价值,选取2000年后的数据
# 发现电影预算与票房的最小值为0,这些数据没有参考意义,应筛选掉
scss
data.groupby("year")["id"].count()
# 2016年和2017年的数据量少,所以这两年的数据也不要
kotlin
# 结合上述条件写出过滤语句
data = data[(data.year > 2000) & (data.year <2016) & (data.vote_count > 50)
& (data.budget * data.revenue * data.popularity * data.vote_average != 0)].reset_index(drop ="True")
diff
data.shape
-- (1971,34)
剩余1971条数据
可视化与数据分析
1. 从市场角度
1.1 对比是市面上各类型电影数量
ini
# 更改字体
plt.rcParams['font.family']='Kaiti'
# 创建画布
plt.figure(figsize = (12,9))
# 分组聚合
genres = data['genres'].value_counts() # 按类型进行分组并计数
# 绘制横向柱状图
plt.barh(y = genres.index[::-1],
width = genres.values[::-1],
color = '#3c7f99')
plt.box(False) #不显示边框
plt.title(label = '各类型电影数量对比',
fontsize = 32,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20) # 图与标题间的距离
plt.tick_params(labelsize = 16) # 刻度字体设置
plt.grid(axis = 'x', # 分割线
linewidth = 0.5,# 线宽
color = '#3c7f99')
由图中看出,目前市面上电影类型最多的Drama、Comedy、Action、Adventure等
1.2 对比每年利润最高的电影类型
ini
plt.rcParams['font.family']='Kaiti'
plt.figure(figsize = (22,12))
# 对类型分组,且对revenue求和
genres_t = data.groupby('genres')["revenue"].sum()
# 绘制柱状图
plt.bar(genres_t.index, # y轴
genres_t, # x轴
color = '#3c7f99'
)
plt.xlabel('类型', fontsize=30) # x轴标签
plt.ylabel('收入(亿美元)', fontsize=30) # y轴标签
# 标题设置
plt.title(label = '不同类型电影收入对比',
fontsize = 50,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 30)
# 刻度标签设置
plt.xticks(fontsize=15)
plt.yticks(fontsize=15)
由图可知,在2000-2015年间,收入最高的电影类型的从大到小的顺序为:Action、Adventure、Comedy、Drama等
1.3 电影收入随年份变化
ini
plt.rcParams['font.family']='Kaiti'
plt.figure(figsize = (12,9))
revenue_year_t = data.groupby("year")["revenue"].sum()
# 绘制折线图
plt.plot(revenue_year_t)
plt.ylabel("收入(百亿美元)",fontsize = 20)
plt.xlabel("年份",fontsize = 20)
plt.title(label = '电影收入逐年趋势',
fontsize = 32,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20) # 图与标题间的距离
由图可知,电影收入呈逐年上涨趋势。但光看票房不看预算不准确,应该进一步去看ROI
1.4 ROI趋势
ini
profit = data.groupby("year")[["budget","revenue"]].sum()
profit['roi'] = (profit.revenue - profit.budget) / profit.budget
profit
plt.figure(figsize = (12,9))
plt.plot(profit.roi)
plt.ylabel("ROI",fontsize = 20)
plt.xlabel("年份",fontsize = 20)
plt.title("ROI随年份变化曲线",
fontsize = 32,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20
)
由图可知,每年的投资回报率呈上涨趋势,电影市场向好。
1.5 影响票房的多种因素
1.5.1 电影票房与预算的关系
ini
plt.scatter(data["budget"],data["revenue"],color='#3c7f99')
plt.xlabel('预算')
plt.ylabel('票房')
plt.title("预算与票房的相关性",
fontsize = 25,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20)
ini
# 计算相关系数
reference=pd.DataFrame({'budget':data["budget"],'revenue':data["revenue"]})
reference.corr()
相关系数为0.7,说明预算与票房之间相关性较强,预算越高,电影的票房越高
1.5.2 电影票房与评分的关系
ini
plt.scatter(data["vote_average"],data["revenue"],color='#3c7f99')
plt.xlabel('评分')
plt.ylabel('票房')
plt.title("预算与票房的相关性",
fontsize = 25,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20)
ini
# 计算相关系数
reference=pd.DataFrame({'vote':data["vote_average"],'revenue':data["revenue"]})
reference.corr()
相关系数为0.2,说明评分与票房之间关联不大
1.6 档期对电影票房的影响
scss
data["release_month"] = pd.to_datetime(data.release_date)
# data["release_month"].dtype
data["release_month"] = data["release_month"].dt.month
# data["release_month"]
# 平均每个月上映的电影数
release_month_num = data.groupby('release_month').count()['id']/15
release_month_num.plot(kind='bar',title='平均每月上映的电影数量(2000-2015)\n', figsize=(12,9));
plt.xlabel('月份')
plt.ylabel('数量')
plt.xticks(rotation=360)
ini
revenue_month_revenue = data.groupby('release_month').sum()['revenue']/15
revenue_month_revenue.plot(kind = "bar",title='平均每月票房(2000-2015)', figsize=(12,9))
plt.xticks(rotation=360)
plt.title('平均每月票房(2000-2015)',
fontsize = 32,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20)
由此可以看出,六月份上映的电影利润最高。
2. 从观众角度
2.1 电影风格随时间变化趋势
ini
# 取出不重复的电影类型
listgenres=set()
for s in data['genres'].str.split(','):
listgenres=set().union(s,listgenres)
listgenres=list(listgenres)
for genre in listgenres:
data[genre]=data['genres'].str.contains(genre).apply(lambda x:1 if x else 0)
genres_y=data.loc[:,listgenres]
genres_y.index=data['year']
genresx = genres_y.groupby('year').sum() #groupby函数对数据集进行聚类运算
genresx = genresx[['Drama','Comedy','Action','Adventure','Horror','Thriller','Crime','Animation']]
plt.rcParams['font.family']='Kaiti'
genresx.plot(figsize = (12,8))
plt.ylabel("数量",fontsize = 20)
plt.xlabel("年份",fontsize = 20)
plt.title('电影类型随时间变化',
fontsize = 32,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20)
plt.show()
电影类型的数量变化能够反映观众的口味变化,从图中可以看出,Drama、Comedy、Action类型依旧占领电影市场相当大的比重。
2.2 词云分析
ini
# 关键词分析
# 利用关键字制作词云图
# 建立keywordlist列表
import matplotlib.colors as colors
keywordlist = []
for i in data['keywords']:
keywordlist.append(i)
keywordlist = list(keywordlist)
keywordlist
# 把字符串列表连接成一个长字符串
lis = ''.join(str(keywordlist))
# # 使用空格替换中间多余的字符串''s'
# lis.replace(''s','')
# 自定义颜色函数
def randomcolor():
colorArr = ['1','2','3','4','5','6','7','8','9','A','B','C','D','E','F']
color ="#"+''.join([np.random.choice(colorArr) for i in range(6)])
return color
color_list=[randomcolor() for i in range(20)]
# 生成词云
wc = WordCloud( background_color="white", # 背景颜色
max_words=2000, # 词云显示的最大词数
max_font_size=100, # 字体最大值
colormap = colors.ListedColormap(color_list))
# 根据字符串生成词云
wc.generate(lis)
plt.figure(figsize=(8,8))
# 以下代码显示图片
plt.imshow(wc)
plt.axis("off")
plt.show()
占比最重的关键字为loss、novel、relationship等
3. 从发行方角度
3.1 导演对电影票房的影响
ini
director_revenue_avg.hist(bins=100,figsize=(5,4))
plt.title('导演平均票房',
fontsize = 10,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 10)
极少数的导演贡献了极高的票房
ini
data.groupby("director").revenue.mean().sort_values().tail(10).plot(kind = "bar",figsize = (10,6))
plt.xticks(rotation=360)
# 标签旋转 标签距离坐标轴的距离
plt.ylabel("票房",fontsize = 15,rotation=360,labelpad = 20)
plt.ylabel("导演",fontsize = 15,labelpad = 20)
plt.title("导演票房分布", fontsize = 25,
weight = 'bold',
color = 'white',
backgroundcolor = '#c5b783',
pad = 20)
平均票房排名前十的导演,詹姆斯-卡梅隆位列第一
结论
综上分析看来,得出以下结论:
- 近年来电影市场前景向好,投资者们往往都能获得不错的收益
- 动作、冒险、戏剧、Drama四种类型的电影收益是最高的,且这四类影片依旧受观众追捧。
- 预算越高,往往能拍出更高质量的额电影,从而获得更高的票房;而观众评分则对票房的影响不大,例如有些文艺片的评分普遍较高,但电影受众却很小,容易出现叫好不叫座的现象。
- 每年的5、6、11、12月份的票房普遍高,避开寒暑期及十一黄金档,电影上映量不高,往往能收获不错的票房。
- 近年来上映的电影中 "loss"、"relationship"等关键词高频出现,说明电影主题的重点放在 "人" 上,更加关注人文精神。
- 导演对票房的贡献属于电影的长尾效应,极少数的导演贡献出超高的票房,名导效应有所加成。