你知道豆瓣電影是怎麼評分的嗎?(實戰篇—手把手教你分析豆瓣電影)


點贊再看,養成好習慣

Python版本3.8.0,開發工具:Pycharm



寫在前面的話:

如果你是因爲看到標題進來的,那恭喜你,又多了一個漲(入)知(坑)識的機會。

在這篇豆瓣電影Top250的分析文章中,你並不會得到一個像標題那樣確切的答案

但是你可以因此否定很多看似正確的答案,比如下面這些:

“豆瓣電影Top250是根據評分排序的?”
“難道是根據評論數排序?”
“那一定是評分和評論數兩者一起影響的?”

以上的想法或許你曾經也想過,但是都不對。

“爲什麼不對?”
“懷疑我!那我今天就給你分析一下爲什麼!”

在這裏插入圖片描述
數據來源上一節:

不想運行代碼,只想要數據,行!文末有數據獲取方式。

另外,和上篇一樣,重點是分析的流程(敲黑板了)

下面,開始今天的——豆瓣電影分析之路。


假設

“小一哥,怎麼一上來就是假設?假設又是什麼?”
“假設,是針對我們的分析結果而言。你希望最後輸出一個什麼結果,或者你需要證明什麼結果,都可以當做假設!”


數據分析是由結果導向的,什麼是結果導向?

說白了,其實是根據目的去完成任務

你經歷什麼學到什麼,是你自己的經驗教訓,領導不關心,其他人也不關心。你工作是否合格業績、是否優秀,要看結果論成敗。


根據目的去完成任務,總是會事半功倍。你可以用分析去研究你想要的結果

就像週末有朋友要請小一我喫超過兩百塊的大餐,這就是假設。根據假設小一可以選擇去喫什麼,像什麼海底撈、烤全羊統統可以安排,海鮮大餐什麼的也可以啊,那個說喫沙縣的你過分了昂。

在這裏插入圖片描述
但我們的假設可不像兩百塊一樣,是一個定數。我們的假設可能是一個範圍,一個問題,或者一個未知的點

那對應於這次的分析,我們的假設可以是:

  • 哪個星級評分更能體現影片整體評分?
  • 影片整體評分與評論數相關嗎?
  • 影片整體評分與哪些指標相關?

以上三個問題,你們可以先思考一下,然後再繼續下一節



數據分析法則

可能你在入門數據分析的時候周圍的人會告訴你帕累托法則,這個法則最開始是用來形容人類社會的財富分佈:百分之二十的人掌握有百分之八十的財富。

但是現在似乎已經普遍適用,大家都已經認識到:重要的因子通常只佔少數

“小一哥,根據帕累托法則,哪個環節最重要?”
“這個就不能用帕累托法則了,數據的重要性毋庸置疑!”

在整個數據分析的週期中,數據清洗直接決定分析結果是否準確,可視化可以發現事實問題,並尋找出現的原因,在數據探索中你可以進行更深層次的數據挖掘

數據分析大家現在有個概念就好了,後面會補充一節數據分析的理論知識


數據清洗

“小一哥,數據清洗之前,我們需要先了解什麼?”
“做事之前,肯定要先了解目的啊”

數據清洗的目的是爲了清洗髒數據,爲後期的數據可視化、特徵工程,保證數據的合理性、準確性

“嗷,就是我數據必須得乾淨,不能有錯的”
“不止這些,當你的數據存在異常值,你可能還需要藉助可視化圖表對數據進行異常值檢測”


舉個例子,你的數據中存在年齡字段的時候,你不能只認爲不是整數的就是髒數據。年齡小於0的,大於150的,都需要注意

本次數據因爲髒數據不多,大家理解概念即可,具體清洗方法會補充在理論知識那一節。


準備好了嗎

拿到數據之後,需要先檢查數據的整體缺失情況

''' 1. 查看整體數據類型與缺失情況'''
df_data.info()

可以看到,豆瓣電影 Top250 的數據缺失情況如下:

Data columns (total 21 columns):
id                        250 non-null int64
movie_rank                250 non-null object
movie_name                250 non-null object
movie_director            250 non-null object
movie_writer              250 non-null object
movie_starring            250 non-null object
movie_type                250 non-null object
movie_country             250 non-null object
movie_language            250 non-null object
movie_release_date        250 non-null object
movie_run_time            250 non-null object
movie_second_name         2 non-null object
movie_imdb_href           250 non-null object
movie_rating              250 non-null object
movie_comments_user       250 non-null object
movie_five_star_ratio     250 non-null object
movie_four_star_ratio     250 non-null object
movie_three_star_ratio    250 non-null object
movie_two_star_ratio      250 non-null object
movie_one_star_ratio      250 non-null object
movie_note                250 non-null object

“小一哥,這個能看出什麼呢?”
“整體數據字段,以及每個字段的缺失情況!”

可以看到,我們的數據集共有21個字段,其中只有電影又名字段有兩個空數據。

我們爬取的豆瓣電影 Top250 數據本就規整,所有沒有缺失屬於正常情況,後面實戰的其他數據可能就沒有這麼規整了。

對於部分影片缺失又名信息,用影片名稱去填充即可

# 用影片名稱填充影片又名字段
df_data['movie_second_name'].fillna(df_data['movie_name'],inplace=True)

帶着整體數據的統計情況,我們去檢查每一個字段

''' 2. 查看單個指標的數據,並進行相應的清洗操作'''

首先是影片排序數據:

# 1. 影片排名數據
df_data['movie_rank'].head(5)
0    No.1
1    No.2
2    No.3
3    No.4
4    No.5
Name: movie_rank, dtype: object

可以看到數據形式是 No.XX 類型,若是建模的話,這種數據類型是不符合要求。

這裏我們將 No.XX 數據的 No. 刪掉,只保留後面的數字即可。

df_data['movie_rank'] = df_data['movie_rank'].str.replace('No.', '').astype(int)

接下來是影片類型字段:

# 2. 影片類型
df_data['movie_type'].head(5)
0          劇情/犯罪
1       劇情/愛情/同性
2          劇情/愛情
3       劇情/動作/犯罪
4    劇情/喜劇/愛情/戰爭
Name: movie_type, dtype: object

可以看到數據形式是 xx/xx/xx 的形式,數據規整,不需要處理,若是建模的話可以對其進行獨熱編碼


接下來是影片製作國家/地區字段:

# 3. 影片製作國家
print(df_data['movie_country'].head(10))
0             美國
1    中國大陸 / 中國香港
2             美國
3             法國
4            意大利
Name: movie_country, dtype: object

可以看到數據形式是 xx / xx 的形式, 用 / 分割,數據規整,但因爲存在空格,需要對空格進行處理。

“這個簡單,小一哥,我會!”

# 這裏直接對空格進行替換
df_data['movie_country'] = df_data['movie_country'].str.replace(' ', '')

“學以致用,很不錯,小夥子!”


接下來是影片語言字段:

和影片製作國家字段一樣,存在空白字符,同樣的處理方法。

# 同理,直接對空格進行替換
df_data['movie_language'] = df_data['movie_language'].str.replace(' ', '')

接下來是影片上映日期:

# 5. 影片上映日期
df_data['movie_release_date'].head(5)
0    1994-09-10(多倫多電影節)/1994-10-14(美國)
1    1993-01-01(中國香港)/1993-07-26(中國大陸)
2     1994-06-23(洛杉磯首映)/1994-07-06(美國)
3                       1994-09-14(法國)
4                      1997-12-20(意大利)
Name: movie_release_date, dtype: object

可以看到部分影片存在多個上映日期和上映城市。

“小一哥,這個怎麼處理?有多個上映日期和上映城市”
“這裏只保留首映日期,日期保留年份即可,並新增一列上映城市”

df_data['movie_release_date'] = df_data['movie_release_date'].apply(lambda e: re.split(r'/', e)[0])
df_data['movie_release_city'] = df_data['movie_release_date'].apply(lambda e: e[11:-1])
df_data['movie_release_date'] = df_data['movie_release_date'].apply(lambda e: e[:4])

接下來是影片片長:

# 6. 影片片長
df_data['movie_run_time'].head(10))
0         142分鐘
1        171 分鐘
2         142分鐘
3    110分鐘(劇場版)
4         116分鐘
Name: movie_run_time, dtype: object

可以看到影片片長爲 XX分鐘 這種形式,還有部分是 110分鐘(劇場版)這種形式

這裏直接保留影片分鐘數即可

df_data['movie_run_time'] = df_data['movie_run_time'].apply(lambda e: re.findall(r'\d+', e)[0]).astype(int)

接下來是影片總評分,影片評論數:

# 7. 影片總評分,影片評論人數
df_data[['movie_rating', 'movie_comments_user']].head(5)

在這裏插入圖片描述

設置爲相應的數據格式即可,影片總評分是浮點類型,影片評論數是整數型

# 這裏將影片總評分轉換爲 float、影評人數轉換爲 int(默認都是 object類型)
df_data['movie_rating'] = df_data['movie_rating'].astype(float)
df_data['movie_comments_user'] = df_data['movie_comments_user'].astype(int)

接下來是影片星級評分佔比:

# 8. 影片星級評分佔比
df_data[['movie_five_star_ratio', 'movie_four_star_ratio', 'movie_three_star_ratio',
         'movie_two_star_ratio', 'movie_one_star_ratio']].head(5)

在這裏插入圖片描述

可以看出星級評分佔比爲 xx% 的形式。

這裏對所有星級的影片進行處理,將百分比轉換成小數即可。


“小一哥,數據清洗算是完成了嗎?”
“前面的步驟只是爲了我們可以更好的進行數據可視化。在接下來的可視化過程中,我們會針對性的進行數據清洗”

所以,接下來的,重點(第二次敲黑板


數據可視化

通過對數據可視化,發現數據的分佈情況,甚至是數據之間的關聯信息。

”可視化需要用到什麼模塊?

可視化可以使用 matplotlib, 但是我使用了 seaborn。

“爲什麼使用 seaborn 作圖?“

seabornmatplotlib一樣,也是 Python 進行數據可視化分析的重要第三方包。

seaborn是在 matplotlib的基礎上進行了更高級的 API 封裝,使得作圖更容易,圖形更漂亮。

針對一些特殊情況,還是需要用到matplotlib的,應該把seaborn視爲matplotlib的補充,而不是替代物。

seaborn 的相關操作大家能看懂即可,後期會抽空出簡單使用教程。


準備好了嗎

上一步中我們已經針對每個字段進行了初步檢測。看一下整體數據的描述性統計:

對數值型特徵進行簡單的描述性統計,包括均值,中位數,衆數,方差,標準差,最大值,最小值等

# 描述性數據統計
df_data.describe()

文章首發:公衆號『知秋小夢』


接下來需要判斷數據類型,定類?定序?定距?還是定比?

弄清楚這一步主要是爲了後續正確找對方法進行可視化

'''數據類型劃分
影片類型、影片製片國家、影片語言: 定類數據<br>
影片片長、影片總評分、影片評論數、影片時間:定距數據
影片5/4/3/2/1星佔比:定比數據
'''

根據上面對各個特徵數據類型的判斷,選擇合適的可視化方法完成可視化。

定類/定序特徵分析

影片類型數據通過 / 分割後統計每個類型出現的次數

'''統計影片類型數據'''
df_data['movie_type'] = df_data['movie_type'].map(lambda e: e.split('/'))
# 將數據轉換成一維數組
movie_type_list = np.concatenate(df_data['movie_type'].values.tolist())
# 將一維數組重新生成 Dataframe 並統計每個類型的個數
movie_type_counter = pd.DataFrame(movie_type_list, columns=['movie_type'])['movie_type'].value_counts()
# 生成柱狀圖的數據 x 和 y
movie_type_x = movie_type_counter.index.tolist()
movie_type_y = movie_type_counter.values.tolist()

畫出影片類型的柱狀圖

# 畫出影片類型的柱狀圖
ax1 = sns.barplot(x=movie_type_x, y=movie_type_y, palette="Blues_r", )
# Seaborn 需要通過 ax.set_title() 來添加 title
ax1.set_title('豆瓣影片Top250類型統計    by:『知秋小一』')
# 設置 x/y 軸標籤的字體大小和字體顏色
ax1.set_xlabel('影片類型', fontsize=10)
ax1.set_ylabel('類型出現次數', fontsize=10)
# 設置座標軸刻度的字體大小
ax1.tick_params(axis='x', labelsize=8)
# 顯示數據的具體數值
for x, y in zip(range(0, len(movie_type_x)), movie_type_y):
    ax1.text(x - 0.3, y + 0.3, '%d' % y, color='black')
    plt.show()

後面的畫圖代碼就不一一顯示,整體代碼太長你們看着也不舒服。需要源碼的在文末有獲取方式。

影片類型統計如下:

可以看到,劇情類佔比特別高,類型前五分別是:劇情、愛情、喜劇、犯罪和冒險

其中,還有兩個情色類的,emmm,我就不告訴你們是什麼了。

同理,將影片語言數據通過 / 分割後統計每個語言出現的次數


影片語言統計如下:
在這裏插入圖片描述

可以看到,英語類佔比特別高,語言前五分別是:英語、日語、漢語普通話、法語和德語

發現一個更有意思的現象,可以看到粵語、上海話、閩南語、重慶話、山西話、湖南話、唐山話、客家話、四川話也都有出現,等會可以看一下具體是哪些影片。

同理將影片製片國家/地區數據通過 / 分割後統計每個國家/地區出現的次數


影片製片國家統計如下:
在這裏插入圖片描述

好萊塢大國穩居榜首,製片國家/地區前五分別是美國、日本、英國、中國香港和中國大陸。

港片還是有很多經典之作的,比起大陸來說相對多一些吧。


定距/定比特徵分析

影片片長、影片總評分、影片評論人數都屬於定距定比特徵,我們來依次分析一下。

影片片長統計如下:
在這裏插入圖片描述

影片片長大多在75~175之間,這個也是目前大多數影片的片長。

可以看出還有一個影片在50分鐘以下,難道是個短紀錄片?我們等會把它揪出來瞅瞅


影片總評分統計如下:
在這裏插入圖片描述

影片總評分最高分9.7,最低分8.3,8.8分的最多。

總評分9.4及以上的有十部,不知道是不是對應的 Top10


影片評論數統計如下
在這裏插入圖片描述

大部分影片的評論數比較集中,評論數在75w人以下。

評論數最多的接近175w人,可以看出差別還是挺明顯的。

“思考一下,我們前面提起的 帕累托法則(二八原則)是否適用?”


影片上映日期統計如下:
在這裏插入圖片描述

Top250的影片集中在 2000年~2017年,其中2004年上映影片最多,達到14部。

“請問一個月一部大片是什麼感覺?小一我也想體驗一下!”


影片星級評論佔比統計如下:

影片星級分爲五級,我們來看一下每個星級的評論數分佈:
在這裏插入圖片描述

星級分佈差別不是很大,但是五星和一星的分佈似乎和總評論數的分佈更符合。

看來二八原則的適用性還是挺強的!



數據探索

上一節我們留下了一些問題,同時還有我們今天的目的:總評分到底與什麼相關?都會在這一節去探索

準備好知道答案了嗎?

先解決上節問題:

影片語言是中國大陸語言的影片:

# 中國大陸參與制作的影片
df_data[df_data['movie_country'].str.contains('中國大陸')][
              ['movie_rank', 'movie_name', 'movie_release_date', 
               'movie_type', 'movie_country', 'movie_language']]

在這裏插入圖片描述

影片時長在五十分鐘以下的影片:

df_data.sort_values(by='movie_run_time')[
    ['movie_rank', 'movie_name', 'movie_release_date', 'movie_run_time',
     'movie_rating', 'movie_comments_user']].head(1)

在這裏插入圖片描述
“emmm,是小一我孤陋寡聞了,寫完文章我就去看!”

評論數最多的前五部影片:

# 評論數最多的前五條影片
df_data.sort_values(by='movie_comments_user', ascending=False)[
    ['movie_rank', 'movie_name', 'movie_release_date', 'movie_rating',
     'movie_comments_user']].head(5)

在這裏插入圖片描述

總評論數最多的影片【肖申克的救贖】實至名歸。

但是,豆瓣電影Top250排序真的不是按照評論數排序的(①)


評分最高的前五部影片

# 評分最高的前五部影片
df_data.sort_values(by='movie_rating', ascending=False)[
    ['movie_rank', 'movie_name', 'movie_release_date', 'movie_rating',
     'movie_comments_user']].head(5)

在這裏插入圖片描述

沒有懸念,總評分最高還是【肖申克的救贖】。

但是,豆瓣電影Top250排序真的不是按照總評分數排序的(②)


星級評分的前五部電影

我們前面分析出,五星級和一星級分佈與總評分吻合,來看一下

# 五星評分人數最多的前五條影片
df_data['five_star_movie_comments_user'] = \
        df_data['movie_comments_user'] * df_data['movie_five_star_ratio']
df_data.sort_values(by='five_star_movie_comments_user', ascending=False)[
    ['movie_rank', 'movie_name', 'movie_release_date', 'movie_rating',
     'movie_comments_user']].head(5)

在這裏插入圖片描述

雖然也不對,但是似乎比前面兩種的排序靠譜點!(③)

”小一哥,會不會是根據總評分和評論數共同決定排序的?“
”我們來試試“

評分+評論數最高的前五部影片

# 評分+評論數最高的前五部影片
df_data.sort_values(by=['movie_rating', 'movie_comments_user'], 
                    ascending=False)[['movie_rank', 'movie_name', 'movie_release_date', 
                                      'movie_rating', 'movie_comments_user']].head(5)

在這裏插入圖片描述

評論數+評分最高的前五部影片

# 評論數+評分最高的前五部影片
df_data.sort_values(by=['movie_comments_user', 'movie_rating'], 
                    ascending=False)[['movie_rank', 'movie_name', 'movie_release_date', 
                                      'movie_rating', 'movie_comments_user']].head(5)

在這裏插入圖片描述
豆瓣電影Top250排序也不是按照評論數+總評分排序的(④)


“還是不對,影片排序不可能是線性這麼簡單的吧,小一哥?”
“是的,影片排序需要用到一種基於用戶投票的排名算法,類似 IMDB 的加權平均,其中一些影評人,電影人的權重都會考慮進去。“”


關於影片排名算法要說清楚的話可能不是一篇文章能搞定的,而且也脫離了我們這一片的重點。

豆瓣影片評分算法並未公開,小一我從網上找到的一篇豆瓣影片評分機制的內容,大家瞭解瞭解長個見識就行了:

豆瓣的註冊用戶看完一部電影,心情好的話會來打個一到五星的分(有時候心情不好也會來)。

比方說一部電影有42萬用戶打分。我們的程序把這42萬個一到五星換算成零到十分,加起來除以42萬,就得到了豆瓣評分。

這個評分會自動出現在豆瓣各處,中間沒有審覈,平時也沒有編輯盯着看。每過若干分鐘,程序會自動重跑一遍,把最新打分的人的意見包括進來。

——豆瓣創始人阿北




總結一下:

提出假設

針對豆瓣電影數據,我們提出了一些小問題作爲我們分析的目的

數據清洗

檢查數據整體情況,對缺失數據進行增補,對每個字段的數據檢查是否合理,並轉換成我們後期需要的數據。

數據可視化

可視化讓我們對數據有一個直觀的認知,針對不合理數據可以進行二次檢查。

數據探索

解決提出的小問題,針對目標進行深層次的分析。

當然,我們這裏欠缺最後一步:特徵工程和評分模型。(本次分析用不到)

思考

以上就是我們今天分析實戰的主要內容,很基礎,但是內容也很多,第一個分析項目,旨在讓大家瞭解分析流程

覺得今天內容量不夠的同學,也可以思考一下以下幾個問題:

  • 還有哪些維度可以互相組合並對總排序造成影響?
  • 它們的可視化顯示你能畫出來嗎?
  • 評分模型應該怎麼設計(可以參考阮一峯的排名算法)?


源碼獲取

目前爲止,和我們豆瓣電影相關的源碼如下:

公衆號後臺回覆 豆瓣電影 獲取 爬取豆瓣電影Top250源碼

公衆號後臺回覆 電影數據 獲取 豆瓣電影Top250詳細數據

公衆號後臺回覆 電影分析 獲取 分析豆瓣電影Top250源碼



寫在後面的話

第一個實戰項目結束了,有部分內容其實並沒有說清楚,只是直接拿來了用,不知道你們能不能理解。

不過,這兩篇內容都只是我們的一個基礎文章,重點是流程不必去細究其中某個細節。

我已經想好了下一個項目應該玩什麼了,你們準備好了嗎?

下期見


碎碎念一下

寫技術文難了不止一個檔次是因爲要把內容輸出成文章,還是挺難的。

我代碼實現兩個晚上就寫完了,但是寫這篇卻用了我整個週末的時間,點個贊支持一下


原創不易,歡迎點贊噢

文章首發:公衆號【知秋小一】

文章同步:掘金,簡書


原文鏈接你知道豆瓣電影是怎麼評分的嗎?



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章