43

Moviedata-10M电影数据集统计分析之源码分享(Python)

 4 years ago
source link: https://www.csuldw.com/2020/05/05/2020-05-05-data-analysis-demo/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

之前写过一篇电影数据分析的文章” 豆瓣13万电影数据统计与分析 “,引起了一些读者的关注,并且在后台咨询我是否可以分享下源码。为了满足大家的需要,我在五一期间将源码略作整理了下,并从中筛选了几个绘图源码在这里分享给大家,如有疑问,可在评论区留言。特别说明下,文中分析的数据来自电影数据集Moviedata-10M中的movies.csv文件,需要的童鞋可以按照官方的说明进行下载即可。

准备工作

在进行源码分享之前,这里先说说我们的运行环境吧,我是使用jupyter进行实验的(强烈推荐),python 3.6版本,依赖的相关库如下:

  • pandas
  • matplotlib
  • seaborn
  • numpy
  • WordCloud
  • imageio
  • squarify

如果对上面的库不了解或者不会安装的,请自行查阅,这里就不一一细说了。

数据加载

由于文件是csv文件,所以加载数据只需要使用python里面的pandas库即可,采用pandas中的read_csv就可以将csv中的数据加载到内存中,代码如下:

import csv
import pandas as pd
import random
movies = pd.read_csv("../data/movies.csv", encoding="utf-8")

统计分析

豆瓣13万电影数据统计与分析 一文,我从不同的维度对电影数据进行了分析,在这里不会将全部的源码分享出来,但是会将核心内容贴出来。

按上映年份统计电影

首先导入相关依赖库,主要是matplotlib,如下:

import matplotlib.pyplot as plt
import matplotlib
matplotlib.matplotlib_fname()

下面这几行代码是为了解决图表中的中文乱码问题,仅供参考:

#解决matplotlib 乱码
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['font.family']='sans-serif'
#解决负号'-'显示为方块的问题
matplotlib.rcParams['axes.unicode_minus'] = False
from matplotlib.font_manager import _rebuild
_rebuild()

在绘制图表之前,我们需要对数据进行处理,构造我们需要的数据格式:

#如果year字段为空,就从release_date进行截取
def map_year(x):
    year = x["year"]
    if year == 0:
        year = str(x["release_date"]).split("-")[0]
    return str(year)
    
movies["year2"] = movies.apply(lambda x: map_year(x), axis=1)
#获取2020年之前上映的电影
movies = movies[movies["year2"]<"2020"]

得到2020年之前的电影之后,我们再分组统计每年上映的电影数量

year_grp = movies.groupby("year2").size().reset_index(name="num") \
                 .sort_values(by="year2", ascending=True)
year_grp = year_grp.rename(columns={"year2":"year"})

接着,按照年份和上映的电影量进行绘图,首先分享下散点图的绘制方法,代码如下:

import seaborn as sns
#散点图
def draw_stripplot(df, df_x, df_y, title="Title", ylabel="Y", savepath="defalt.png"):
    # draw stripplot start
    fig, ax = plt.subplots(figsize=(20,10), dpi= 80)    
    sns.stripplot(df_x, df_y, jitter=0.25, size=8, ax=ax, linewidth=.5)

    # decoration
    plt.gca().set_xticklabels(df_x, rotation=90, horizontalalignment= 'right')
    plt.title(title, fontsize=16)
    plt.ylabel(ylabel)
    plt.savefig(savepath)
    plt.show()

draw_stripplot(year_grp, year_grp.year, year_grp.num, 
            title="Number Of Movies Released Each Year(1873-2019)", 
            ylabel='# Number', 
            savepath="result/movies_number_of_each_year_stripplot.png")

draw_stripplot方法是可以共用的,如果其他的聚合数据生成了,也可以调用上面的方法。得到的图表如下所示:

UbQnqeR.png!web

Fig 1.每年上映的电影数(趋势图)

按评分统计电影

首先分组统计出每个评分的电影数量

df = movies.groupby('douban_score').size().reset_index(name='counts')
df = df[df["douban_score"]>0]
df["douban_score"] = df.douban_score.astype("str")

采用 movies[movies["douban_score"] > 0]["douban_score"].mean() 可以统计出电影的平均得分为6.63。

接着编写柱状图绘制函数,代码如下:

#柱状图
def draw_barplot(df, df_x, df_y, title="Title", ylabel="# Y", 
                savepath="default.png", fontsize=5, x_fontsize=10):
    all_colors = list(plt.cm.colors.cnames.keys())
    random.seed(100)
    c = random.choices(all_colors, k=df_x.shape[0])

    # Plot Bars柱状
    plt.figure(figsize=(20,10), dpi= 200)
    plt.bar(df_x, df_y, color=c, width=.5)
    for i, val in enumerate(df_y.values):
        plt.text(i, val, int(val), horizontalalignment='center', 
                verticalalignment='bottom', 
                fontdict={'fontweight':200, 'size':fontsize})

    # Decoration
    plt.gca().set_xticklabels(df_x, rotation=90, horizontalalignment= 'right', 
                            fontdict={"size":x_fontsize})
    plt.title(title, fontsize=16)
    plt.ylabel(ylabel)
    plt.savefig(savepath)
    plt.show()

将数据采用上面别写函数进行渲染:

draw_barplot(df, df.douban_score, df.counts, 
            title="Movie Statistics For Each Score", 
            ylabel='# Score', savepath="result/movie_stat_by_score.png", fontsize=10)

得到的柱状图如下所示:

Yzu6Rri.png!web

Fig 2.各个评分下的电影数统计

按照国家进行统计

首先根据国家进行聚合,

movies_regions = movies
movies_regions["regions"] = movies_regions.regions  \
                .apply(lambda x: x.split("/")[0].split(" ")[0].strip())
df = movies_regions.groupby('regions').size().reset_index(name='counts')
df = df[df["regions"]!=""].sort_values(by=["counts"], ascending=False)[:50]

然后调用 draw_barplot 函数即可:

draw_barplot(df, df.regions, df.counts, 
            title="Movie Statistics For Each Region", ylabel='# Number', 
            savepath="result/movies_stat_by_regions.png", fontsize=8,x_fontsize=12)

结果图如下:

2ENfiim.png!web

Fig 3.按发行地域统计电影数(Top 50的发行地域)

按语言进行统计

数据构建

df = movies.groupby('languages').size().reset_index(name='counts')
df = df[df["languages"]!=""]
df = movies.languages.apply(lambda x: x.split("/")[-1].split(" ")[0])   \
         .reset_index(name="languages").drop(columns="id")
df = df.groupby("languages").size().reset_index(name='counts')
df = df[df["languages"]!=""]
df = df.sort_values(by=["counts"], ascending=False)[:20]

绘制饼状图,并进行渲染:

import matplotlib.pyplot as plt
import numpy as np

def draw_pieplot(df, df_x, df_y, 
                title="Title", 
                subtitle="subtitle", 
                savepath="default.png"):
    # Draw Plot
    fig, ax = plt.subplots(figsize=(12, 7), subplot_kw=dict(aspect="equal"), dpi= 80)

    data = df_y
    categories = df_x
    explode = np.zeros(df_x.shape[0])
    explode[3] = 0.1

    def func(pct, allvals):
        absolute = int(pct/100.*np.sum(allvals))
        return "{:.1f}% ({:d} )".format(pct, absolute)

    wedges, texts, autotexts = ax.pie(data,
                                      autopct=lambda pct: func(pct, data),
                                      textprops=dict(color="w"),
                                      colors=plt.cm.Dark2.colors,
                                     startangle=140)

    # Decoration
    ax.legend(wedges, categories, 
            title=subtitle, loc="center left", 
            bbox_to_anchor=(1, 0, 0.5, 1))
    plt.setp(autotexts, size=10, weight=700)
    ax.set_title(title)
    plt.savefig(savepath)
    plt.show()
    
draw_pieplot(df, df.languages, df.counts, 
            title="Statistics By Languages: Pie Chart", 
            subtitle="Languages", 
            savepath="result/movie_language_stat_pieplot.png")

结果图如下:

aUviqaa.png!web

Fig 4.按语言统计电影数

对中国的电影进行分析

同理,首先构造数据格式:

movies_china = movies[movies.regions.str.startswith("中国") |  \
                      movies.regions.str.startswith("香港") |  \
                      movies.regions.str.startswith("台湾") |  \
                      movies.regions.str.startswith("澳门")]

df = movies_china.reset_index().groupby('year').size().reset_index(name="counts")
df = df[df["year"]!=""][df["year"]!=0]
df = df.sort_values(by="year", ascending=True)
#df["counts"] = df.counts.astype("str")

接着绘制线性趋势图:

def draw_plot_liner2(df, df_x, df_y, 
                    x_name, 
                    y_name, 
                    title="Title", 
                    ylabel="Y", 
                    savepath="defalt.png"):
    # Draw Plot - liner
    plt.figure(figsize=(16,10), dpi= 80)
    plt.plot(x_name, y_name, data=df, color='tab:red')

    plt.yticks(fontsize=12, alpha=.7)
    plt.title(title, fontsize=22)
    plt.ylabel(ylabel)
    plt.grid(axis='both', alpha=.3)

    # Remove borders
    plt.gca().spines["top"].set_alpha(0.0)    
    plt.gca().spines["bottom"].set_alpha(0.3)
    plt.gca().spines["right"].set_alpha(0.0)    
    #plt.gca().spines["left"].set_alpha(0.3)   
    plt.savefig(savepath)
    plt.show()

draw_plot_liner2(df, df.year, df.counts,'year','counts', 
                title="Statistics of Movie_China For Each Year", 
                ylabel='# Number', savepath="result//movies_china_each_year.png")

最后得到的趋势图如下:

Jb6bYrb.png!web

Fig 5.中国每年的电影数量统计

如果需要渲染多个国家进行对比,只需要将多个国家的数据进行聚合然后一个个绘制到图上即可。

词云

电影类型词云

如果想要绘制类型词云,需要上面提到的WordCloud库。

from wordcloud import WordCloud
import collections
import imageio

当具备这些之后,我们首先要准备数据,取出电影标签,然后进行词频统计,

object_list = movies.genres.tolist()
word_list = []
for words in object_list:
    word_list.extend(words.split("/"))
word_counts = collections.Counter(word_list) # 对分词做词频统计

接着调用WordCloud库进行分析

b_mask = imageio.imread("./data/bg_my.jpeg") #如果运行到这里找不到图片,请自行替换图片即可

wc = WordCloud(font_path="Hiragino Sans GB.ttc", # 字体
               background_color = 'white', # 背景色
               max_words = 2000, # 最大显示单词数
               #width=1000,
               #height=500,
               max_font_size = 160, # 频率最大单词字体大小
               mask=b_mask
               #stopwords = stopwords # 过滤噪声词
              ).generate_from_frequencies(word_counts)

wc.to_file("genres_cloud.png")
plt.imshow(wc, interpolation="bilinear")
plt.axis("off")
plt.show()

如果词库比较大的话,时间需要久一点,最后得到的图片如下:

Y7VZvuQ.png!web

Fig 6.电影类型词云

标签词云也是类似的,只修要重新渲染下数据即可。

结束语

文章共介绍了散点图、线性图、柱状图、饼状图、词云这几个核心图表的绘制,只要下载了相关库,那么构造出相应的数据格式之后,代码可以直接运行,后续我会考虑以jupyter文件分享出来,大家可以关注下我的公众号:【斗码小院】,相关内容会第一时间发布到公众号中,如果相关问题,也可以在公众号的“关于小院”一栏进行留言。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK