之前寫過一篇電影數據分析的文章” 豆瓣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方法是可以共用的,如果其他的聚合數據生成了,也可以調用上面的方法。得到的圖表如下所示:

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)

得到的柱狀圖如下所示:

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)

結果圖如下:

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")

結果圖如下:

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")

最後得到的趨勢圖如下:

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()

如果詞庫比較大的話,時間需要久一點,最後得到的圖片如下:

Fig 6.電影類型詞雲

標籤詞雲也是類似的,只修要重新渲染下數據即可。

結束語

文章共介紹了散點圖、線性圖、柱狀圖、餅狀圖、詞雲這幾個核心圖表的繪製,只要下載了相關庫,那麼構造出相應的數據格式之後,代碼可以直接運行,後續我會考慮以jupyter文件分享出來,大家可以關注下我的公衆號:【鬥碼小院】,相關內容會第一時間發佈到公衆號中,如果相關問題,也可以在公衆號的“關於小院”一欄進行留言。

相關文章