1. 程式人生 > >用Python分析豆瓣電影Top250

用Python分析豆瓣電影Top250

開場白:本文中使用的語言為 Python 3 ,其中主要用了 BeautifulSoup、Numpy、Pandas、Matplotlib和WordCloud 等幾個資料分析常用庫,過程儘量寫的詳(luo)細(suo)些,希望能給和我一樣的資料分析初學者一些思路,如果文中有錯誤請告訴我。同時歡迎各種關於程式碼、分析思路、語言組織、排版等等的意見建議。非常感謝!文章很長,如果只看部分內容請戳目錄。

豆瓣電影Top250資料分析

不知道看什麼電影時,就會習慣性的看看豆瓣,但落伍的我直到最近才發現還有個神奇的豆瓣電影Top250榜單!它是根據每部影片看過的人數以及該影片所得的評論等綜合資料排名的,同時還考慮了人群的廣泛適應性和持續關注度。好高大上的演算法!

那麼得出的這個排行榜和電影評分及評論人數有怎樣的關係?
和上映時間關係大不大?
哪種型別的電影上榜最多呢?
哪些國家、導演、主演最受歡迎?
片長多長時間最合適?

帶著這些疑問,不妨進行一下資料分析。

資料收集

先來看一下頁面:
這裡寫圖片描述

我們抓取排名、電影名、導演、主演、上映日期、製片國家/地區、型別,評分、評論數量、一句話評價以及電影連結,其中導演和主演分別取一位。

用開發者工具看一下原始碼:

這裡寫圖片描述

大部分資訊比較好抓取,只有電影資訊在標籤 <p> 中都寫在了一起,可通過 strip() 函式去除兩側空格, split()函式分裂字串來取得具體資訊。

選用Python 3,引入 url.request

BeautifulSoup 庫來抓取頁面資訊。程式碼如下:

import urllib.request as urlrequest
from bs4 import BeautifulSoup

top250_url = "https://movie.douban.com/top250?start={}&filter="

with open('top250_f1.csv','w',encoding='utf8') as outputfile: 
    outputfile.write("num#title#director#role#init_year#area#genre#rating_num#comment_num#comment#url\n"
) for i in range(10): start = i*25 url_visit = top250_url.format(start) crawl_content = urlrequest.urlopen(url_visit).read() http_content = crawl_content.decode('utf8') soup = BeautifulSoup(http_content,'html.parser') all_item_divs = soup.find_all(class_='item') for each_item_div in all_item_divs: pic_div = each_item_div.find(class_='pic') num = pic_div.find('em').get_text() #排名 href = pic_div.find('a')['href'] #電影連結 title = pic_div.find('img')['alt'] #電影名稱 bd_div = each_item_div.find(class_='bd') infos = bd_div.find('p').get_text().strip().split('\n') infos_1 = infos[0].split('\xa0\xa0\xa0') director = infos_1[0][4:].rstrip('...').rstrip('/').split('/')[0] #導演 role = str(infos_1[1:])[6:].split('/')[0] #主演 infos_2 = infos[1].lstrip().split('\xa0/\xa0') year = infos_2[0] #上映時間 area = infos_2[1] #國家/地區 genre = infos_2[2:] #電影型別 star_div = each_item_div.find(class_='star') rating_num = star_div.find(class_='rating_num').get_text() #評分 comment_num = star_div.find_all('span')[3].get_text()[:-3] #評價數量 quote = each_item_div.find(class_='quote') inq = quote.find(class_='inq').get_text() #一句話評價 outputfile.write('{}#{}#{}#{}#{}#{}#{}#{}#{}#{}#{}\n'.format(num,title,director,role,year,area, genre,rating_num,comment_num,inq,href))
本來只打算抓這些資料,分析時發現有些主演名字顯示的不完全,看來還要再抓一下每個電影頁面。在上面抓取的url資訊中截取出id編號,用豆瓣API介面。
import urllib
import urllib.request as urlrequest
import json
import time
import random
import pandas as pd

df = pd.read_csv("top250_f1.csv",sep = "#", encoding = 'utf8')
urlsplit = df.url.str.split('/').apply(pd.Series)
id_list = list(urlsplit[4])
num=0
IP_list = [  ]  #這裡寫幾個可用的IP地址和埠號,只抓250個頁面,有兩三個IP就夠了。
IP = random.chioce(IP_list)

with open('top250_f2.csv', 'w',encoding='utf8') as outputfile:    

    outputfile.write("num#rank#alt_title#title#pubdate#language#writer#director#cast#movie_duration#year#movie_type#tags#image\n")

    proxy = urlrequest.ProxyHandler({'https':  'IP'})
    opener = urlrequest.build_opener(proxy)
    opener.addheaders = [('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) 
                      AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30')]                      
    urlrequest.install_opener(opener)

    for id in id_list:       
        url_visit = 'https://api.douban.com/v2/movie/{}'.format(id)       
        crawl_content = urlrequest.urlopen(url_visit).read()        
        json_content = json.loads(crawl_content.decode('utf-8'))

        rank = json_content['rating']['average']
        alt_title = json_content['alt_title']
        image = json_content['image']
        title = json_content['title']        
        pubdate = json_content['attrs']['pubdate']
        language = json_content['attrs']['language']
        try:
            writer = json_content['attrs']['writer']
        except:
            writer = 'None'
        director = json_content['attrs']['director']
        cast = json_content['attrs']['cast']
        movie_duration = json_content['attrs']['movie_duration']
        year = json_content['attrs']['year']
        movie_type = json_content['attrs']['movie_type']
        tags = json_content['tags']
        num = num +1    
        outputfile.write("{}#{}#{}#{}#{}#{}#{}#{}#{}#{}#{}#{}#{}#{}\n".format(num,rank,alt_title,title,pubdate,language,writer,
                                                                              director,cast,movie_duration,year,movie_type,tags,image))
        time.sleep(1)
OK,資料抓取完畢,看一下抓取結果。先來看爬取Top250頁面的資訊:
import numpy as ny
import pandas as pd
df_1 = pd.read_csv("top250_f1.csv",sep = "#", encoding = 'utf8')
df_1.head()
num title director role init_year area genre rating_num comment_num comment url
0 1 肖申克的救贖 弗蘭克·德拉邦特 Frank Darabont 蒂姆·羅賓斯 Tim Robbins 1994 美國 [‘犯罪 劇情’] 9.6 861343 希望讓人自由。 https://movie.douban.com/subject/1292052/
1 2 霸王別姬 陳凱歌 Kaige Chen 張國榮 Leslie Cheung 1993 中國大陸 香港 [‘劇情 愛情 同性’] 9.5 618349 風華絕代。 https://movie.douban.com/subject/1291546/
2 3 這個殺手不太冷 呂克·貝鬆 Luc Besson 讓·雷諾 Jean Reno 1994 法國 [‘劇情 動作 犯罪’] 9.4 824694 怪蜀黍和小蘿莉不得不說的故事。 https://movie.douban.com/subject/1295644/
3 4 阿甘正傳 Robert Zemeckis Tom Hanks 1994 美國 [‘劇情 愛情’] 9.4 703838 一部美國近現代史。 https://movie.douban.com/subject/1292720/
4 5 美麗人生 羅伯託·貝尼尼 Roberto Benigni 羅伯託·貝尼尼 Roberto Beni…’] 1997 義大利 [‘劇情 喜劇 愛情 戰爭’] 9.5 410615 最美的謊言。 https://movie.douban.com/subject/1292063/

再看一下抓取單個頁面的資訊:

df_2 = pd.read_csv("top250_f2.csv",sep = "#", encoding = 'utf8')
df_2.head()
num rank alt_title title pubdate language writer director cast movie_duration year movie_type tags image
0 1 9.6 肖申克的救贖 / 月黑高飛(港) The Shawshank Redemption [‘1994-09-10(多倫多電影節)’, ‘1994-10-14(美國)’] [‘英語’] [‘弗蘭克·德拉邦特 Frank Darabont’, ‘斯蒂芬·金 Stephen King’] [‘弗蘭克·德拉邦特 Frank Darabont’] [‘蒂姆·羅賓斯 Tim Robbins’, ‘摩根·弗里曼 Morgan Freeman’… [‘142 分鐘’] [‘1994’] [‘犯罪’, ‘劇情’] [{‘count’: 178370, ‘name’: ‘經典’}, {‘count’: 15… https://img3.doubanio.com/view/movie_poster_co…
1 2 9.5 再見,我的妾 霸王別姬 [‘1993-01-01(香港)’] [‘漢語普通話’] [‘蘆葦 Wei Lu’, ‘李碧華 Lillian Lee’] [‘陳凱歌 Kaige Chen’] [‘張國榮 Leslie Cheung’, ‘張豐毅 Fengyi Zhang’, ‘鞏俐 … [‘171 分鐘’] [‘1993’] [‘劇情’, ‘愛情’, ‘同性’] [{‘count’: 109302, ‘name’: ‘經典’}, {‘count’: 54… https://img3.doubanio.com/view/movie_poster_co…
2 3 9.4 這個殺手不太冷 / 殺手萊昂 Léon [‘1994-09-14(法國)’] [‘英語’, ‘義大利語’, ‘法語’] [‘呂克·貝鬆 Luc Besson’] [‘呂克·貝鬆 Luc Besson’] [‘讓·雷諾 Jean Reno’, ‘娜塔莉·波特曼 Natalie Portman’, … [‘110分鐘(劇場版)’, ‘133分鐘(國際版)’] [‘1994’] [‘劇情’, ‘動作’, ‘犯罪’] [{‘count’: 136989, ‘name’: ‘經典’}, {‘count’: 75… https://img3.doubanio.com/view/movie_poster_co…
3 4 9.4 阿甘正傳 / 福雷斯特·岡普 Forrest Gump [‘1994-06-23(洛杉磯首映)’, ‘1994-07-06(美國)’] [‘英語’] [‘Eric Roth’, ‘Winston Groom’] [‘Robert Zemeckis’] [‘Tom Hanks’, ‘Robin Wright Penn’, ‘Gary Sinis… [‘142 分鐘’] [‘1994’] [‘劇情’, ‘愛情’] [{‘count’: 165677, ‘name’: ‘勵志’}, {‘count’: 12… https://img1.doubanio.com/view/movie_poster_co…
4 5 9.5 美麗人生 / 一個快樂的傳說(港) La vita è bella [‘1997-12-20(義大利)’] [‘義大利語’, ‘德語’, ‘英語’] [‘文森佐·克拉米 Vincenzo Cerami’, ‘羅伯託·貝尼尼 Roberto B… [‘羅伯託·貝尼尼 Roberto Benigni’] [‘羅伯託·貝尼尼 Roberto Benigni’, ‘尼可萊塔·布拉斯基 Nicolet… [‘116分鐘’] [‘1997’] [‘劇情’, ‘喜劇’, ‘愛情’, ‘戰爭’] [{‘count’: 66790, ‘name’: ‘義大利’}, {‘count’: 61… https://img3.doubanio.com/view/movie_poster_co…

都是250行資訊。下面進行的資料清洗。

資料清洗

一般我們得到的資料是不可以直接使用的,裡面可能存在重複值、缺失值、空值、
無效值、異常值、錯誤值,以及邏輯、格式不正確等的資料不一致問題。網上抓取來的資料更容易有這些問題,我們需要處理這些髒資料,轉化成可供分析的資料。

資料分佈在兩個檔案中,我們選取 top250_f1.csv 檔案中的 num(排名)、 title(電影名)、 init_year(上映時間)、 area(國家/地區)、 genre(型別)、 rating_num(評分)、 comment_num(評價人數),和 top250_f2.csv 檔案中的 language(語言)、 director(導演)、 cast(主演)、 movie_duration(時長)、 tags(標籤)這些列進行分析,因此只對這些列中的髒資料做清洗工作。

先將這些列放到同一個DataFrame中:

df_1_cut = df_1[['num','title','init_year','area','genre','rating_num','comment_num']]
df_2_cut = df_2[['num','language','director','cast','movie_duration','tags']]
df = pd.merge(df_1_cut,df_2_cut,how = 'outer', on = 'num')
df.head()
num title init_year area genre rating_num comment_num language director cast movie_duration tags
0 1 肖申克的救贖 1994 美國 [‘犯罪 劇情’] 9.6 861343 [‘英語’] [‘弗蘭克·德拉邦特 Frank Darabont’] [‘蒂姆·羅賓斯 Tim Robbins’, ‘摩根·弗里曼 Morgan Freeman’… [‘142 分鐘’] [{‘count’: 178370, ‘name’: ‘經典’}, {‘count’: 15…
1 2 霸王別姬 1993 中國大陸 香港 [‘劇情 愛情 同性’] 9.5 618349 [‘漢語普通話’] [‘陳凱歌 Kaige Chen’] [‘張國榮 Leslie Cheung’, ‘張豐毅 Fengyi Zhang’, ‘鞏俐 … [‘171 分鐘’] [{‘count’: 109302, ‘name’: ‘經典’}, {‘count’: 54…
2 3 這個殺手不太冷 1994 法國 [‘劇情 動作 犯罪’] 9.4 824694 [‘英語’, ‘義大利語’, ‘法語’] [‘呂克·貝鬆 Luc Besson’] [‘讓·雷諾 Jean Reno’, ‘娜塔莉·波特曼 Natalie Portman’, … [‘110分鐘(劇場版)’, ‘133分鐘(國際版)’] [{‘count’: 136989, ‘name’: ‘經典’}, {‘count’: 75…
3 4 阿甘正傳 1994 美國 [‘劇情 愛情’] 9.4 703838 [‘英語’] [‘Robert Zemeckis’] [‘Tom Hanks’, ‘Robin Wright Penn’, ‘Gary Sinis… [‘142 分鐘’] [{‘count’: 165677, ‘name’: ‘勵志’}, {‘count’: 12…
4 5 美麗人生 1997 義大利 [‘劇情 喜劇 愛情 戰爭’] 9.5 410615 [‘義大利語’, ‘德語’, ‘英語’] [‘羅伯託·貝尼尼 Roberto Benigni’] [‘羅伯託·貝尼尼 Roberto Benigni’, ‘尼可萊塔·布拉斯基 Nicolet… [‘116分鐘’] [{‘count’: 66790, ‘name’: ‘義大利’}, {‘count’: 61…

通過 pd.merge()函式選出的 df_1_cutdf_2_cut 兩張表,取並集,連結鍵為num

看一下資料基本資訊:

df.info()

重複值檢查

檢查重複值可以用 duplicated() 函式,若返回值為“True”,則含有重複項,返回值為“False”,則不含重複項。 pd.Series.value_counts() 函式可以用來對series計數。

df.duplicated().value_counts()
False 250 dtype: int64 250個 `False` ,說明不含重複項。 檢查是否有重名電影:
len(df.title.unique())
250 250個唯一值,說明沒有重名電影。 檢查是否有並列排名:
len(df.num.unique())
250 同樣250個唯一值,沒有並列排名。

清洗資料格式、資料分列

粗略看一下,可以發現 genrelanguagedirectorcastmovie_durationtags列方括號、花括號和英文省略號等無效資訊,需要去掉。
對於兩側的 [' ']{[' ']} 形式,可以用str分割字串。

df['genre'] = df['genre'].str[2:-2]
df['language'] = df['language'].str[2:-2]
df['director'] = df['director'].str[2:-2]
df['cast'] = df['cast'].str[2:-2]
df['movie_duration'] = df['movie_duration'].str[2:-2]
df['tags'] = df['tags'].str[3:-3]
df.head()
num title init_year area genre rating_num comment_num language director cast movie_duration tags
0 1 肖申克的救贖 1994 美國 犯罪 劇情 9.6 861343 英語 弗蘭克·德拉邦特 Frank Darabont 蒂姆·羅賓斯 Tim Robbins’, ‘摩根·弗里曼 Morgan Freeman’, … 142 分鐘 count’: 178370, ‘name’: ‘經典’}, {‘count’: 15001…
1 2 霸王別姬 1993 中國大陸 香港 劇情 愛情 同性 9.5 618349 漢語普通話 陳凱歌 Kaige Chen 張國榮 Leslie Cheung’, ‘張豐毅 Fengyi Zhang’, ‘鞏俐 Li… 171 分鐘 count’: 109302, ‘name’: ‘經典’}, {‘count’: 54458…
2 3 這個殺手不太冷 1994 法國 劇情 動作 犯罪 9.4 824694 英語’, ‘義大利語’, ‘法語 呂克·貝鬆 Luc Besson 讓·雷諾 Jean Reno’, ‘娜塔莉·波特曼 Natalie Portman’, ‘加… 110分鐘(劇場版)’, ‘133分鐘(國際版) count’: 136989, ‘name’: ‘經典’}, {‘count’: 75963…
3 4 阿甘正傳 1994 美國 劇情 愛情 9.4 703838 英語 Robert Zemeckis Tom Hanks’, ‘Robin Wright Penn’, ‘Gary Sinise’… 142 分鐘 count’: 165677, ‘name’: ‘勵志’}, {‘count’: 12412…
4 5 美麗人生 1997 義大利 劇情 喜劇 愛情 戰爭 9.5 410615 義大利語’, ‘德語’, ‘英語 羅伯託·貝尼尼 Roberto Benigni 羅伯託·貝尼尼 Roberto Benigni’, ‘尼可萊塔·布拉斯基 Nicoletta… 116分鐘 count’: 66790, ‘name’: ‘義大利’}, {‘count’: 61289…

對於 area 列,有些電影由多個國家或地區聯合制作,例如《霸王別姬》電影:

df['area'][1]
‘中國大陸 香港’ “中國大陸”和“香港”之間用空格隔開,可以用` str.split` 函式進行分列, `apply(pd.Series)` 使用到的函式作用在每一行或列。
area_split = df['area'].str.split(' ').apply(pd.Series)
area_split.head()
0 1 2 3 4
0 美國 NaN NaN NaN NaN
1 中國大陸 香港 NaN NaN NaN
2 法國 NaN NaN NaN NaN
3 美國 NaN NaN NaN NaN
4 義大利 NaN NaN NaN NaN

可以看到最多為5個製作國家/地區,當然,大多數只有一個製片國家/地區。要了解哪個國家/地區的電影最受歡迎,就需要對國家進行統計。
對於這麼多的空值,可以通過先按列計數,將空值 NaN 替換為“0”,再按行彙總。

a = area_split.apply(pd.value_counts).fillna('0') 
a.columns = ['area_1','area_2','area_3','area_4','area_5']
a['area_1'] = a['area_1'].astype(int)
a['area_2'] = a['area_2'].astype(int)
a['area_3'] = a['area_3'].astype(int)
a['area_4'] = a['area_4'].astype(int)
a['area_5'] = a['area_5'].astype(int)
a = a.apply(lambda x: x.sum(),axis = 1)
area_c = pd.DataFrame(a, columns = ['counts'])
area_c.head()
counts
中國大陸 15
丹麥 1
伊朗 2
加拿大 7
南非 2

以上過程也可以通過 unstack() 函式和 groupby() 函式來完成。 對 genre 列,我們使用這一方法。

genre_split = df['genre'].str.split(' ').apply(pd.Series)
genre_split.head()
0 1 2 3 4 5
0 犯罪 劇情 NaN NaN NaN NaN
1 劇情 愛情 同性 NaN NaN NaN
2 劇情 動作 犯罪 NaN NaN NaN
3 劇情 愛情 NaN NaN NaN NaN
4 劇情 喜劇 愛情 戰爭 NaN NaN
g = genre_split.apply(pd.value_counts)
g.head()
0 1 2 3 4 5
傳記 2.0 9.0 NaN NaN 1.0 NaN
兒童 1.0 3.0 2.0 NaN NaN NaN
冒險 7.0 6.0 13.0 11.0 4.0 1.0
劇情 163.0 26.0 5.0 NaN NaN NaN
動作 16.0 16.0 2.0 NaN NaN NaN

通過 unstack 函式將行“旋轉”為列,重排資料:

g.unstack().head()
0 傳記 2.0 兒童 1.0 冒險 7.0 劇情 163.0 動作 16.0 dtype: float64 此時資料為 `Series` ,去掉空值,並通過 `reset_index()` 轉化為 `Dataframe` :
g = g.unstack().dropna().reset_index()
g.head()
level_0 level_1 0
0 0 傳記 2.0
1 0 兒童 1.0
2 0 冒險 7.0
3 0 劇情 163.0
4 0 動作 16.0
g.columns = ['level_0','level_1', 'counts']
genre_c = g.drop(['level_0'],axis = 1).groupby('level_1').sum()
genre_c.head()
counts
level_1
傳記 12.0
兒童 6.0
冒險 42.0
劇情 194.0
動作 34.0

此時 counts 列即為電影型別的統計計數。

類似的方法來處理以下幾項。
language 列:

language_split = df['language'].str.replace('\', \'',' ').str.split(' ').apply(pd.Series)
l = language_split.apply(pd.value_counts).stack().dropna().reset_index()
l.columns = ['level_0','level_1', 'counts']
language_c = l.groupby('level_0').sum()
language_c = language_c.drop(['level_1'],axis = 1)
language_c.head()
counts
level_0
Ungwatsi 1.0
上海話 4.0
世界語 1.0
丹麥語 3.0
烏克蘭語 1.0

director 列:

director_split = df['director'].str.replace('\', \'','#').str.split('#').apply(pd.Series)
director_split.head()
0 1 2
0 弗蘭克·德拉邦特 Frank Darabont NaN NaN
1 陳凱歌 Kaige Chen NaN NaN
2 呂克·貝鬆 Luc Besson NaN NaN
3 Robert Zemeckis NaN NaN
4 羅伯託·貝尼尼 Roberto Benigni NaN NaN

選取第一位導演作為分析物件:

director = director_split[0].str.strip()
df['director'] = director
`cast` 列:
cast_split = df['cast'].str.replace('\', \'','#').str.split('#').apply(pd.Series)  #[[0,1,2,3]].columns=['performer_1','performer_2','performer_3','performer_4']
cast_split.head()
0 1 2 3 4 5 6 7 8 9 34 35 36 37 38 39 40 41 42 43
0 蒂姆·羅賓斯 Tim Robbins 摩根·弗里曼 Morgan Freeman 鮑勃·岡頓 Bob Gunton 威廉姆·賽德勒 William Sadler 克蘭西·布朗 Clancy Brown 吉爾·貝羅斯 Gil Bellows 馬克·羅斯頓 Mark Rolston 詹姆斯·惠特摩 James Whitmore 傑弗裡·德曼 Jeffrey DeMunn 拉里·布蘭登伯格 Larry Brandenburg NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 張國榮 Leslie Cheung 張豐毅 Fengyi Zhang 鞏俐 Li Gong 葛優 You Ge 英達 Da Ying 蔣雯麗 Wenli Jiang 吳大維 David Wu 呂齊 Qi Lü 雷漢 Han Lei 尹治 Zhi Yin NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 讓·雷諾 Jean Reno 娜塔莉·波特曼 Natalie Portman 加里·奧德曼 Gary Oldman 丹尼·愛羅 Danny Aiello 彼得·阿佩爾 Peter Appel 邁克爾·巴達魯科 Michael Badalucco 艾倫·格里尼 Ellen Greene 伊麗莎白·瑞根 Elizabeth Regen 卡爾·馬圖斯維奇 Carl J. Matusovich 弗蘭克·賽格 Frank Senger NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 Tom Hanks Robin Wright Penn Gary Sinise Mykelti Williamson Sally Field Michael Conner Humphreys Haley Joel Osment NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 羅伯託·貝尼尼 Roberto Benigni 尼可萊塔·布拉斯基 Nicoletta Braschi 喬治·坎塔里尼 Giorgio Cantarini 朱斯蒂諾·杜拉諾 Giustino Durano 塞爾吉奧·比尼·布斯特里克 Sergio Bini Bustric 瑪麗莎·佩雷德斯 Marisa Paredes 豪斯特·巴奇霍茲 Horst Buchholz 利迪婭·阿方西 Lidia Alfonsi 朱利亞娜·洛約迪切 Giuliana Lojodice 亞美利哥·豐塔尼 Amerigo Fontani NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 44 columns

選取前六位演員作為分析物件。

c = cast_split[[0,1,2,3,4,5]] #.columns=['performer_1','performer_2','performer_3','performer_4','performer_5','performer_6']
c.columns=['performer_1','performer_2','performer_3','performer_4','performer_5','performer_6']
c = cast_split.unstack().dropna().reset_index()
c.head()
level_0 level_1 0
0 0 0 蒂姆·羅賓斯 Tim Robbins
1 0 1 張國榮 Leslie Cheung
2 0 2 讓·雷諾 Jean Reno
3 0 3 Tom Hanks
4 0 4 羅伯託·貝尼尼 Roberto Benigni
c.columns=['level_0','level_1','performers']
c['performers'] = c['performers'].str.strip()
c.head()
level_0 level_1 performers
0 0 0 蒂姆·羅賓斯 Tim Robbins
1 0 1 張國榮 Leslie Cheung
2 0 2 讓·雷諾 Jean Reno
3 0 3 Tom Hanks
4 0 4 羅伯託·貝尼尼 Roberto Benigni

演員表中有些人名中英文都標註了,有些只寫了中文或英文名,例如“Tom Hanks”和“湯姆·漢克斯 Tom Hanks”是指一個人。下面的步驟是找出單獨的中文或英文名,補全為中英兩種語言的名字。

for i in c['performers']:
    for j in c[c['performers'].str.contains(i)]['performers']:
        if (len(j) > len(i)):
            c[c['performers']==i] = j
        else:
            continue
d:\ProgramData\Anaconda3\lib\site-packages\ipykernel\__main__.py:2: UserWarning: This pattern has match groups. To actually get the groups, use str.extract. from ipykernel import kernelapp as app
c['performers'].head()
0 蒂姆·羅賓斯 Tim Robbins 1 張國榮 Leslie Cheung 2 讓·雷諾 Jean Reno 3 湯姆·漢克斯 Tom Hanks 4 羅伯託·貝尼尼 Roberto Benigni Name: performers, dtype: object
c = c.groupby('performers').count()
此時 `level_0` 和 `level_1` 的資料是完全一樣的,都是表示演員出現的次數,刪除 `Level_0` 列。
c = c.drop(['level_0'], axis = 1)
c.columns = ['counts']
cast_c = c
cast_c.head()
counts
performers
1326270 1
1976 (樂團) 1
Agnese Nano 1
Aldo Giuffrè 1
Alexandre Rodrigues 1

movie_duration 列:

movie_duration_split = df['movie_duration'].str.strip().str.replace('\', \'','#').str.split('#').apply(pd.Series)
movie_duration_split.head()
0 1 2 3 4 5
0 142 分鐘 NaN NaN NaN NaN NaN
1 171 分鐘 NaN NaN NaN NaN NaN
2 110分鐘(劇場版) 133分鐘(國際版) NaN NaN NaN NaN
3 142 分鐘 NaN NaN NaN NaN NaN
4 116分鐘 NaN NaN NaN NaN NaN

有些電影時長存在多種版本,一般情況下第一個時長為國內最普通、觀看數量較多的版本,因此僅取第一個時長。

duration = movie_duration_split[0].str.split('分').apply(pd.Series)[0].str.strip()
duration.head()
0 142 1 171 2 110 3 142 4 116 Name: 0, dtype: object 型別為object,需改為int型別。此時若是直接用 `duration.astype(int)` ,會報錯: `ValueError: invalid literal for int() with base 10: ‘Australia: 80’` ,錯誤資訊顯示有一行為“Australia: 80”,混有其他字串,所以無法轉換成int型別。我們需要找出非數值型資料。 電影時長應該為兩位數或三位數,可以看一下數字長度:
duration.str.len().value_counts()
3 187 2 61 13 1 9 1 Name: 0, dtype: int64 字串長度有兩個不合理的值,找出來:
duration[duration.str.len() > 3]
74 Australia: 80 226 Japan: 75 Name: 0, dtype: object
duration[74] = duration[74].split(' ')[1]
duration[226] = duration[226].split(' ')[1]
現在更改資料型別:
duration = duration.astype(int)
duration.dtypes
dtype(‘int32’)
df['movie_duration'] = duration
df['movie_duration'].head()
0 142 1 171 2 110 3 142 4 116 Name: movie_duration, dtype: int32 對於 `tags` 列,先看一下第一項基本情況:
df.tags[0]
“count’: 178370, ‘name’: ‘經典’}, {‘count’: 150016, ‘name’: ‘勵志’}, {‘count’: 131943, ‘name’: ‘信念’}, {‘count’: 117510, ‘name’: ‘自由’}, {‘count’: 90200, ‘name’: ‘美國’}, {‘count’: 82546, ‘name’: ‘人性’}, {‘count’: 61162, ‘name’: ‘人生’}, {‘count’: 53244, ‘name’: ‘劇情” 其中的數值和漢字部分是需要提取的,其餘無關資訊可用 `str.replace()` 函式替換掉,用 `str.split()` 函式分列:
tags_split = df['tags'].str.replace('count\': ',' ').str.replace(', \'name\': \'',' ').str.replace('\'}, {\'','').str.split(' ').apply(pd.Series)
tags_split.head()
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 178370 經典 150016 勵志 131943 信念 117510 自由 90200 美國 82546 人性 61162 人生 53244 劇情
1 109302 經典 54458 中國電影 53522 愛情 49358 文藝 46339 人性 45374 同志 37368 人生 28356 劇情
2 136989 經典 75963 愛情 73361 溫情 51532 人性 47454 劇情 36808 動作 31271 犯罪 19390 1994
3 165677 勵志 124126 經典 94060 美國 82929 人生 61445 信念 59325 成長 34048 劇情 24545 人性
4 66790 義大利 61289 經典 60683 二戰 58827 親情 36662 戰爭 21463 溫情 18881 愛情 18695 人性

刪除“0”列:

del tags_split[0]
tags_split.head(2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 178370 經典 150016 勵志 131943 信念 117510 自由 90200 美國 82546 人性 61162 人生 53244 劇情
1 109302 經典 54458 中國電影 53522 愛情 49358 文藝 46339 人性 45374 同志 37368 人生 28356 劇情

一般閱讀習慣是先看標籤類別,再看標籤數量,調整一下位置比較便於閱讀:

tags_split = tags_split.reindex(columns = [2,1,4,3,6,5,8,7,10,9,12,11,14,13,16,15])
tags_split.head(2)
2 1 4 3 6 5 8 7 10 9 12 11 14 13 16 15
0 經典 178370 勵志 150016 信念 131943 自由 117510 美國 90200 人性 82546 人生 61162 劇情 53244
1 經典 109302 中國電影 54458 愛情 53522 文藝 49358 人性 46339 同志 45374 人生 37368 劇情 28356

更改列名:

tags_split.columns = ['tags_1','tags_counts_1','tags_2','tags_counts_2','tags_3','tags_counts_3','tags_4','tags_counts_4','tags_5','tags_counts_5','tags_6','tags_counts_6','tags_7','tags_counts_7','tags_8','tags_counts_8']
tags_split.head()
tags_1 tags_counts_1 tags_2 tags_counts_2 tags_3 tags_counts_3 tags_4 tags_counts_4 tags_5 tags_counts_5 tags_6 tags_counts_6 tags_7 tags_counts_7 tags_8 tags_counts_8
0 經典 178370 勵志 150016 信念 131943 自由 117510 美國 90200 人性 82546 人生 61162 劇情 53244
1 經典 109302 中國電影 54458 愛情 53522 文藝 49358 人性 46339 同志 45374 人生 37368 劇情 28356
2 經典 136989 愛情 75963 溫情 73361 人性 51532 劇情 47454 動作 36808 犯罪 31271 1994 19390
3 勵志 165677 經典 124126 美國 94060 人生 82929 信念 61445 成長 59325 劇情 34048 人性 24545
4 義大利 66790 經典 61289 二戰 60683 親情 58827 戰爭 36662 溫情 21463 愛情 18881 人性 18695

有的電影給出了不同多家上映的時間,其中第一個最早,因此對於好幾個年份的情況取第一個值。

year_split = df['init_year'].str.split('/').apply(pd.Series)[0].str.strip() 
year_split = pd.to_datetime(year_split).dt.year
df['init_year'] = year_split
df['init_year'].head()
0 1994 1 1993 2 1994 3 1994 4 1997 Name: init_year, dtype: int64

缺失值檢查

df[df.isnull().values == True]
num title init_year area genre rating_num comment_num language director cast movie_duration tags