數據分析--百年大電影
電影誕生於1888年,是路易斯普林斯在1888年最早放映的郎德海花園場景。
但在這之前埃德沃得邁布里奇在1878年拍攝的Sallie Gardner at a Gallop有時也被視為早期電影的先驅。
而後,在1895年12月28日,由盧米埃兄弟在法國巴黎卡普幸路14號一家名為「大咖啡館」的地下室首次公開放映了他們的短片,這使電影進入公眾的視野,這被認為標誌著電影的誕生。
盧米埃爾作為一名傑出的攝影師,擅長快速攝影,並發明了既是攝影機又是放映機和洗映機的機器。他拍的第一部片子是一部宣傳片工廠的大門,表現當時法國里昂盧米埃爾工廠放工時的情景,片長僅一分多鐘,但這是世界上第一部影片,因而在電影史上佔有重要地位。為世界各國大學電影藝術研究生的必看片目之一。
電影的內容是:盧米埃爾工廠大門徐徐打開,一群頭戴緞帶紐結羽帽,身穿緊身上衣和曳地長裙,腰系圍裙的女工首先走出,接著是一群手推自行車的男工。與這一百餘名魚貫而出,繁忙勞作的工人的方向相反,廠主們乘坐一輛由兩匹駿馬拉著的馬車進入工廠。年輕的女士們一邊躲避車輛,一邊快步行走,表現出閑暇時光的歡樂情緒。大門口還跑出一隻蹦蹦跳跳的大狗。此後,工廠門衛走出來,很快把工廠大門關閉起來。盧米埃爾用放工的工人隊伍的浩大場面,去展現他的工廠的宏大規模,傳達出了自己內心的喜悅和自豪。
1895年,火車進站是盧米埃兄弟製作的黑白無聲的紀錄片。這部紀錄火車駛抵希歐達(la Ciotat)車站的影片,是架在月台上所拍攝的。全長五十秒的內容,描寫了蒸汽火車牽引著客車,從遠處漸漸地駛進車站。
以及盧米埃爾兄弟拍攝的Repas de bébé(1895)、Exiting the Factory(1895)、Démolition d"un mur(1895)、L"Arrivée d"un train en gare de La Ciotat(1895)、L"Arroseur Arrosé(1895)等等。 電影在情節,方向和表演方面都有起色(主要是由於其早期的時間極短),但從那以後,世界各地的電影業開始成長,出現導演,編劇,演員,音響設計師和電影攝影師等職業。它也從浪漫發展到喜劇,科幻到恐怖等各種各樣的流派。
幾乎每個上個世紀出生的小孩,都對電影很痴迷,沉迷於此,那時候的電影基本都是大幕布,雖然簡陋,但是卻無比期待。
這裡有一個大約45000部電影的數據集,使用這些數據,探討關於電影的一些問題,這裡面基本是外語片,所以華語片很少,以後可以再寫一個專門關於華語的。
導入庫和載入我們的數據
%matplotlib inlinefrom IPython.display import Image, HTMLimport jsonimport datetimeimport astimport pandas as pdimport matplotlib.pyplot as pltimport seaborn as snsimport numpy as npfrom scipy import statsfrom sklearn.ensemble import GradientBoostingClassifier, GradientBoostingRegressorfrom sklearn.dummy import DummyClassifier, DummyRegressorfrom sklearn.model_selection import train_test_splitfrom wordcloud import WordCloud, STOPWORDSimport plotlyimport plotly.offline as pypy.init_notebook_mode(connected=True)import plotly.graph_objs as goimport plotly.tools as tlsimport warningswarnings.filterwarnings("ignore")plotly.tools.set_credentials_file(username="rounakbanik", api_key="xTLaHBy9MVv5szF4Pwan")#sns.set_style("whitegrid")#sns.set(font_scale=1.25)pd.set_option("display.max_colwidth", 50)
df = pd.read_csv("/Users/chandler/Documents/Data/movie_dataset/movies_metadata.csv")df.head()
上面的數據集來自TMDB,數據包含來自27000個用戶的45000個電影的2600萬個評級,看看數據的columns
df.columns
adult: 表示電影是X級(就是我們說的三級片)或者成人級belongs_to_collection: 一個字元串化的字典,給出特定電影所屬電影系列的信息budget: 以美元計算的電影製作預算genres: 列出與電影相關的所有流派的字典化字典列表homepage: 官方主頁id: 電影 ID imdb_id: 電影的IMDB IDoriginal_language: 電影拍攝的語言(母語)original_title: 電影的原始標題(母語語言標題)overview: 這部電影的簡短介紹popularity: 人氣poster_path: 海報圖片的網址production_companies: 參與制作電影的製作公司名單production_countries: 電影拍攝/製作的國家/地區的字元串化列表release_date: 電影的劇院發行日期revenue: 以美元計算的電影總收入runtime: 以分鐘為單位的電影時長spoken_languages: 電影中包含的所有語言的字元串列表status: 電影的狀態(已發布,即將發布,已發布等)tagline: 電影的標語title: 電影的官方標題(英語)video: 是否存在帶有TMDB的電影的視頻vote_average: 電影的平均評分vote_count: 票數
df.shape
df.info()
總共有45466部電影和24個columns,下面將這個數據集清理成適合分析的形式
數據處理/清洗
df = df.drop(["imdb_id"], axis=1)df.columns
# 看一下有哪些非英語片df[df["original_title"] != df["title"]].index
# 有一萬多條,這個量挺大的。電影的原始標題(母語語言標題)就不要了,就按照英語來分析df = df.drop("original_title", axis=1)df.columns
df[df["revenue"] == 0].shape
有很多電影收入為0,說明數據有缺失,但電影收入作為一個重要的特徵,是要留下來的,畢竟還有好幾千部電影是有收入數據的
df["revenue"] = df["revenue"].replace(0, np.nan)df["budget"].head(20)
# 處理電影製作預算數據df["budget"] = pd.to_numeric(df["budget"], errors="coerce")df["budget"] = df["budget"].replace(0, np.nan)df[df["budget"].isnull()].shape
return_ratio有四萬數據為null,剩下五千多是有數據的,即使只有五千多,但也可以很好地利用起來。
df["adult"].value_counts()
df = df.drop("adult", axis=1)# 把海報圖片的網址拼好並放到一個html標籤裡面base_poster_url = "http://image.tmdb.org/t/p/w185/"df["poster_path"] = "<img src="" + base_poster_url + df["poster_path"] + "" stylex="height:100px;">"df["poster_path"][3]
下面來做兩個詞雲,一個是title:電影的官方標題(英語),還有一個是overview: 這部電影的簡短介紹.
詞雲和拍攝地繪圖
# 強制類型轉換為字元串df["title"] = df["title"].astype("str")df["overview"] = df["overview"].astype("str")# 注意join要用空格,因為電影名字裡面肯定有空格(The Shawshank Redemption肖申克的救贖),不用空格是不行的title_corpus = " ".join(df["title"])overview_corpus = " ".join(df["overview"])title_wordcloud = WordCloud(stopwords=STOPWORDS, background_color="gray", height=2000, width=4000).generate(title_corpus)plt.figure(figsize=(16,8))plt.imshow(title_wordcloud)plt.axis("off")plt.show()
這麼多年,這麼多部電影,標題充滿著love、man、girl、life、day、night這樣的字眼,愛字是電影片名中最常用的字。 女孩,男人和日子也是最常見的單詞之一,我認為這代表了電影的浪漫,所以說大家喜歡電影,因為電影給我們帶來了很多精神上的東西和對美好的無限想像。
# 電影簡介詞雲overview_wordcloud = WordCloud(stopwords=STOPWORDS, background_color="gray", height=2000, width=4000).generate(overview_corpus)plt.figure(figsize=(16,8))plt.imshow(overview_wordcloud)plt.axis("off")plt.show()
family、one、life、live、find是電影簡介中最常用的詞。 加上love,man和girl,這些wordclouds讓人有一個相當不錯的感覺,這也表明了這100年電影的整體主題的選擇的趨勢。
這麼多部電影,英語片佔了大部分,但這些電影可能在世界各地拍攝,下面看看哪些國家是電影製作者最喜愛的拍攝地。
# 電影拍攝地數據查看df["production_countries"].head()[0]
# 提取出拍攝地名『name』df["production_countries"] = df["production_countries"].fillna("[]").apply(ast.literal_eval)df["production_countries"] = df["production_countries"].apply( lambda x: [i["name"] for i in x] if isinstance(x, list) else [])# 臨時查看dataframe轉化後的Seriespd.Series(df["production_countries"]).head()
# 說明數據重構stack的用法df_obj = pd.DataFrame(np.random.randint(0,10, (5,2)), columns=["data1", "data2"])df_obj.head()
stacked = df_obj.stack()stacked.head()
stacked.reset_index(level=1, drop=True)
t = stacked.reset_index(level=1, drop=True)t.name="test"t.head()
# 對拍攝地production_countries這一列數據處理,使用stack和reset_index方法以及apply數據預處理s = df.apply(lambda x: pd.Series(x["production_countries"]),axis=1).stack().reset_index(level=1, drop=True)s.name = "countries"s.head()
# 刪除production_countries替換為scon_df = df.drop("production_countries", axis=1).join(s)# 添加了countries這一列進來了(從production_countries提取出來)con_df.head(1)
# 對拍攝地countries這一列進行count計數得出每個地點拍攝電影次數con_df = pd.DataFrame(con_df["countries"].value_counts())con_df.head()
con_df["country"] = con_df.indexcon_df.head()
con_df.columns = ["num_movies", "country"]con_df.head()
con_df = con_df.reset_index().drop("index", axis=1)con_df.head()
data = [ dict( type = "choropleth", locations = con_df["country"], locationmode = "country names", z = con_df["num_movies"], text = con_df["country"], colorscale = [[0,"rgb(255, 255, 255)"],[1,"rgb(255, 0, 0)"]], autocolorscale = False, reversescale = False, marker = dict( line = dict ( color = "rgb(180,180,180)", width = 0.5 ) ), colorbar = dict( autotick = False, tickprefix = "", title = "拍攝地選取次數"), ) ]layout = dict( title = "電影這100年來拍攝地的選取分布", geo = dict( showframe = False, showcoastlines = False, projection = dict( type = "Mercator" ) ))fig = dict( data=data, layout=layout )py.iplot( fig, validate=False, filename="d3-world-map" )
美國佔了太多太多了,別的國家看不出來了,把美國拿掉再看看,除了美國作為電影拍攝地次數最多之外的其他拍攝地分布
con_df_apartFromUS = con_df[con_df["country"] != "United States of America"]data = [ dict( type = "choropleth", locations = con_df_apartFromUS["country"], locationmode = "country names", z = con_df_apartFromUS["num_movies"], text = con_df_apartFromUS["country"], colorscale = [[0,"rgb(255, 255, 255)"],[1,"rgb(255, 0, 0)"]], autocolorscale = False, reversescale = False, marker = dict( line = dict ( color = "rgb(180,180,180)", width = 0.5 ) ), colorbar = dict( autotick = False, tickprefix = "", title = "拍攝地選取次數"), ) ]layout = dict( title = "電影這100年來拍攝地的選取分布 (除去美國)", geo = dict( showframe = False, showcoastlines = False, projection = dict( type = "Mercator" ) ))fig = dict( data=data, layout=layout )py.iplot( fig, validate=False, filename="d3-world-map" )
數據主要由英文電影組成,美國是最受歡迎的電影製作目的地。 歐洲是英國,法國,德國和義大利為最受歡迎的地區。日本和印度在電影製作方面是亞洲最受歡迎的國家。
電影收入以及製作公司
下面看看那些特許拍攝的電影(根據小說改編之類的電影,存在知識產權,也可以通俗的認為那些根據小說改編的續集電影,比如哈利波特)
# 過濾掉belongs_to_collection(一個字元串化的字典,給出特定電影所屬電影系列的信息)為null的數據df_fran = df[df["belongs_to_collection"].notnull()]df_fran.head(1)["belongs_to_collection"][0]
# 從belongs_to_collection中提取出namedf_fran["belongs_to_collection"] = df_fran["belongs_to_collection"].apply( ast.literal_eval).apply(lambda x: x["name"] if isinstance(x, dict) else np.nan)df_fran.head(1)["belongs_to_collection"][0]
df_fran = df_fran[df_fran["belongs_to_collection"].notnull()]df_fran.head(1)["belongs_to_collection"][0]
df_fran.head()
# 製作數據透視表,計算belongs_to_collection下面的電影出現多少次(也就是拍攝了幾部)、revenue(電影收入)的平均值和總和fran_pivot = df_fran.pivot_table(index="belongs_to_collection", values="revenue", aggfunc={"revenue": ["mean", "sum", "count"]}).reset_index()fran_pivot.sort_values("sum", ascending=False).head()
哈利波特是總收入最高的的,掙了77億美金,我去真厲害.....(我也貢獻過,哈哈哈??),要不把中國最高票房戰狼2拍個十幾部看看能不能趕超??,排第二的是星戰,星戰這個是在太火了,排第二也是實至名歸,還有詹姆斯邦德,這個不用介紹了,從小就看了.....排名前三的就是這幾個了
上面看的是總收入排名,還應該看一下平均每部收入
fran_pivot.sort_values("mean", ascending=False).head(10)
「阿凡達」系列雖然目前只有一部電影,卻是有史以來最成功的,一部電影收入接近30億美元。 這樣算來,比哈利波特厲害多了!
然後再看看續集最多的電影
fran_pivot.sort_values("count", ascending=False).head(10)
詹姆斯邦德、十三號星期五、口袋妖怪排前三,整的最多的的是詹姆斯邦德,當然這是以總和來計算。
下面來看看電影后面的製作公司
df["production_companies"].head(1)[0]
# ast.literal_eval把字元串形式的list轉換成真正的listdf["production_companies"] = df["production_companies"].fillna("[]").apply(ast.literal_eval)df["production_companies"] = df["production_companies"].apply( lambda x: [i["name"] for i in x] if isinstance(x, list) else [])s = df.apply(lambda x: pd.Series(x["production_companies"]),axis=1).stack().reset_index(level=1, drop=True)s.name = "companies"s.head()
com_df = df.drop("production_companies", axis=1).join(s)com_df.head(1)
com_sum = pd.DataFrame(com_df.groupby("companies")["revenue"].sum().sort_values(ascending=False))com_sum.columns = ["Total"]com_mean = pd.DataFrame(com_df.groupby("companies")["revenue"].mean().sort_values(ascending=False))com_mean.columns = ["Average"]com_count = pd.DataFrame(com_df.groupby("companies")["revenue"].count().sort_values(ascending=False))com_count.columns = ["Number"]com_pivot = pd.concat((com_sum, com_mean, com_count), axis=1)com_pivot.sort_values("Total", ascending=False).head(10)
整的最多的是華納兄弟公司????,這個大家應該都非常熟悉,華納一度成為電影代名詞,一共拍了491部電影,一共掙得635.3億美金,我勒個去.....四五千億人民幣啊,吊吊吊!排名第二的是環球影業????,和華納幾乎差不多,463部電影,掙了553億美金!排名第三的是派拉蒙影業????,395部電影,掙得488億美金!電影大佬!電影大佬!
以上是看總數,同樣的,還應該去看看平均值!
com_pivot[com_pivot["Number"] >= 15].sort_values("Average", ascending=False).head(10)
平均每部電影掙錢最多的是皮克斯動畫工作室????,這個也是非常的有名啊!,均值6.2億美金。其次是我大漫威!????,均值6.2億美金,第三是革命工作室????,均值5.43億,宿醉3、碟中諜4、007幽靈黨、雷神2暗黑世界都是他的。
語言
這個之前也有說到,大部分電影語言是英語,現在要做的是把英語去掉,看看哪些語言排名較高。
一共有93種語言
lang_df = pd.DataFrame(df["original_language"].value_counts())lang_df.head()
lang_df["language"] = lang_df.indexlang_df.columns = ["number", "language"]lang_df.head(10)
# iloc[1:11]排除英語plt.figure(figsize=(12,5))sns.barplot(x="language", y="number", data=lang_df.iloc[1:11])plt.show()
除了英語,電影語言最多的前三名就是法語、義大利語、日語,咱們的漢語都跑哪去了,中國電影趕緊加油啊,雖然我們近些年拍了很多電影很少能和國際上的大片抗衡,但是曾經我們的動畫是世界一流的,雖然現在被日本和美國趕超了,日本包括宮崎駿、細田守那幾位動漫老者其實當年也在上美呆過很長時間,學了不少東西,然後把我們趕超了,現在的國內做電影的導演和演員一個勁的要高片酬,尼瑪你倒是出好作品啊,哎....不說了,跑題了,一下子說多了。
人氣/投票數/平均評分
def clean_numeric(x): try: return float(x) except: return np.nan# 評分df["popularity"] = df["popularity"].apply(clean_numeric).astype("float")df["popularity"].head()
# 用戶投票數df["vote_count"] = df["vote_count"].apply(clean_numeric).astype("float")df["vote_count"].head()
# 電影平均評分df["vote_average"] = df["vote_average"].apply(clean_numeric).astype("float")df["vote_average"].head(20)
df["popularity"].describe()
人氣平均值只有2.9,好低啊。
# NaN值用中位數填充(median())sns.distplot(df["popularity"].fillna(df["popularity"].median()))plt.show()
df["popularity"].plot(logy=True, kind="hist")
平均值只有2.9,但是最高值達到五百多,但是75%百分位是3.68分,幾乎所有的電影人氣比較低。
看看什麼電影人氣分最高。
df[["title", "popularity", "year"]].sort_values("popularity", ascending=False).head(10)
小黃人(Minions)是TMDB人氣分數中最受歡迎的電影。 神奇女俠,美女與野獸,兩部非常成功的以女性為核心的電影分別排在第二位和第三位。
下面看看投票數。
df["vote_count"].describe()
sns.distplot(df["vote_count"].fillna(df["vote_count"].median()))
同樣的這個數據分布也是很極端的,也是高峰向左,長尾向右的極端正偏態分布。看來TMDB和IMDB相比,人氣評分這一塊不夠有力啊
還是看看投票數排行吧。
df[["title", "vote_count", "year"]].sort_values("vote_count", ascending=False).head(10)
排在最前面的大家都不陌生,兩個備受讚譽並且在商業上很成功的克里斯托弗·諾蘭(Christopher Nolan)的電影排在前兩位,第一是盜夢空間第二是蝙蝠俠黑暗騎士
下面再看看平均評分
df["vote_average"] = df["vote_average"].replace(0, np.nan)df["vote_average"].describe()
sns.distplot(df["vote_average"].fillna(df["vote_average"].median()))
評分還真嚴格!大家看電影評分都挺嚴的,豆瓣也是,基本上高分很少。
# 查看評分最高的(投票數大於2000的電影)df[df["vote_count"] > 2000][["title", "vote_average", "vote_count" ,"year"]].sort_values( "vote_average", ascending=False).head(10)
前10名我都超級喜歡的!
第一名是肖申克的救贖
第二名教父
第三名美麗人生
第四宮崎駿的千與千尋
第五飛越瘋人院
第六驚魂記
第七名搏擊俱樂部
第八名教父2
第九名蝙蝠俠黑暗騎士
第十名低俗小說
現在這三個都看完了,那麼人氣得分和評分之間有沒有什麼相關性呢?做一個散點圖來看看。
sns.jointplot(x="vote_average", y="popularity", data=df)
只有0.097,看來人氣得分和評分之間沒有什麼相關性 下面再看看投票數和評分之間的相關性。
sns.jointplot(x="vote_average", y="vote_count", data=df)
只有0.12,幾乎沒有什麼相關性,看來票數多不一定電影評分高!
電影具體發行日期
下面嘗試以具體的電影發布的年月日來分析,之前已經構建了年度,現在提取具體日期。
month_order = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]day_order = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]# 提取月份函數def get_month(x): try: return month_order[int(str(x).split("-")[1]) - 1] except: return np.nan # 提取天的函數def get_day(x): try: year, month, day = (int(i) for i in x.split("-")) answer = datetime.date(year, month, day).weekday() return day_order[answer] except: return np.nandf["day"] = df["release_date"].apply(get_day)df["month"] = df["release_date"].apply(get_month)df[["title","year","month","day"]].head()
plt.figure(figsize=(12,6))plt.title("Number of Movies released in a particular month from 1899 to 2017")sns.countplot(x="month", data=df, order=month_order)
每年一月份電影發行最多,也就是說新年開始的時間是新片發行最多的時候!大家以後元旦期間多關注電影,會有很多大片。
那麼哪些月份的電影掙錢最多呢,考慮超過超過1億美金的電影。
month_mean = pd.DataFrame(df[df["revenue"] > 100000000].groupby("month")["revenue"].mean())month_mean["mon"] = month_mean.indexmonth_mean.head()
plt.figure(figsize=(12,6))plt.title("Average Gross by the Month for Blockbuster Movies")sns.barplot(x="mon", y="revenue", data=month_mean, order=month_order)
看來4、5、6三個月份的電影最賣座!
下面再看看return_ratio(回報率)和月份的箱線圖。
fig, ax = plt.subplots(nrows=1, ncols=1,figsize=(15, 8))sns.boxplot(x="month", y="return_ratio", data=df[df["return_ratio"].notnull()], palette="bright", ax=ax, order=month_order)ax.set_ylim([0, 12])
六月和七月收益率最好,九月份最差,這個能看出點東西,六月和七月學生都放暑假了,那個時候最放鬆,也是娛樂消費最多的時候,而九月正值開學季,大家都忙著準備新學期,哪有多少功夫看電影啊。
下面再看看受歡迎的天,就是人們星期幾最愛看電影
plt.figure(figsize=(10,5))plt.title("Number of Movies released on a particular day")sns.countplot(x="day", data=df, order=day_order)
明顯是周五,周五還用說嘛,休息當然要和女友/男友/閨蜜看電影,吃吃飯,酒館喝喝酒,放鬆一下咯!
以上從具體發行日期做了統計分析,那麼實際上還應該看一下電影從1899年到2017年整體的發行趨勢,這份數據集,四萬多部電影,可能不全,但是主流的影片或者都是包含了的
year_count = df.groupby("year")["title"].count()plt.figure(figsize=(18,5))year_count.plot()
圖中存在急劇增加部分,但也有可能就是為了這份數據而採樣的最近的電影,所以不會過多關注這張圖,總之肯定是一直增長的。
下面再來看看早期的電影
df[df["year"] != "NaT"][["title", "year"]].sort_values("year").head(10)
1:金星凌日2:飛馳中的薩利·加德納3:奔跑中的水牛4:繞過牆角者5:拉手風琴者6:利茲大橋7:惡作劇28:倫敦特拉法加廣場9:惡作劇110:Mosquinha
最後,構建一個熱圖,看看本世紀發行的所有電影的情況。
months = {"Jan": 1, "Feb": 2, "Mar": 3, "Apr": 4, "May": 5, "Jun": 6, "Jul": 7, "Aug": 8, "Sep": 9, "Oct": 10, "Nov": 11, "Dec": 12}# 這裡就從90後出生開始算起df_1990 = df.copy()df_1990["year"] = df_1990[df_1990["year"] != "NaT"]["year"].astype(int)df_1990 = df_1990[df_1990["year"] >=1990]hmap_1990 = pd.pivot_table(data=df_1990, index="month", columns="year", aggfunc="count", values="title")hmap_1990 = hmap_1990.fillna(0)sns.set(font_scale=1)f, ax = plt.subplots(figsize=(16, 8))sns.heatmap(hmap_1990, annot=True, linewidths=.5, ax=ax, fmt="n", yticklabels=month_order)
這二十來年每年五月份都是大片雲集,好萊塢大片月啊,但是最近幾年,慢慢的發布月份慢慢往11、12、1、2月份遷移,這正好是年底和年初這幾個月,大家都有時間消費娛樂,2月份可能因為中國的新年,畢竟任何東西都繞不開中國市場,這些年一到過年就是各種賀歲大片,元旦前後也是,這幾個月電影都是超級多的,作為看電影,可能這幾個月份電影最多,可選擇的也多,那麼我們看看2000年往後的熱圖以及電影收入和收益率的圖,可能更加能體驗這十年內的情況。
# 這裡就從2000年開始計算df_2000 = df.copy()df_2000["year"] = df_2000[df_2000["year"] != "NaT"]["year"].astype(int)df_2000 = df_2000[df_2000["year"] >=2000]hmap_2000 = pd.pivot_table(data=df_2000, index="month", columns="year", aggfunc="count", values="title")hmap_2000 = hmap_2000.fillna(0)sns.set(font_scale=1)f, ax = plt.subplots(figsize=(16, 8))sns.heatmap(hmap_2000, annot=True, linewidths=.5, ax=ax, fmt="n", yticklabels=month_order)
month_mean = pd.DataFrame(df_2000[df_2000["revenue"] > 100000000].groupby("month")["revenue"].mean())month_mean["mon"] = month_mean.indexplt.figure(figsize=(12,6))plt.title("Average Gross by the Month for Blockbuster Movies")sns.barplot(x="mon", y="revenue", data=month_mean, order=month_order)
從總的電影收入看來,4、5、6、11、12是幾個收入比較高的月份,1月份反而最少,這個怎麼回事呢,下面看看收益率的圖。
fig, ax = plt.subplots(nrows=1, ncols=1,figsize=(15, 8))sns.boxplot(x="month", y="return_ratio", data=df_2000[df_2000["return_ratio"].notnull()], palette="bright", ax=ax, order=month_order)ax.set_ylim([0, 12])
哎!這個圖有意思了,一月在總收入那張圖最少,但是在這裡收益率一月份是最多的,也就是說,單論一部電影掙了利潤有多少,那麼一月份的電影的收益率是很可觀的,除了一月份,還有7月份、12月份收益率也比較高。
電影中的語言和電影時長
電影中語言的數量是否影響電影的成功? 下面來探討一下這個問題。
df["spoken_languages"] = df["spoken_languages"].fillna("[]").apply( ast.literal_eval).apply(lambda x: len(x) if isinstance(x, list) else np.nan)df["spoken_languages"].value_counts()
大部分電影只有一種語言,我想應該是英語,剩下的大部分是2種或者3種語言,0種可能數據收集的問題,最多的語言能有19種,看看這些有超級多鍾語言的都是什麼電影
df[df["spoken_languages"] >= 10][["title", "year", "spoken_languages"]].sort_values( "spoken_languages", ascending=False)
最高的電影叫歐洲二十五面體,是來自25個成員國的導演個字拍5分鐘短片組成的,所以有各種語言。
看看語言數量對收益率的影響,估計是沒有啥影響的。
sns.jointplot(x="spoken_languages", y="return_ratio", data=df, stat_func=stats.spearmanr)
從1分鐘的影片,黑白剪輯到3小時視覺盛筵,電影在放映時間方面一直在進步。 下面嘗試探討電影長度的性質和隨著時間的演變的一些額外的見解。
df["runtime"].describe()
平均時長94分鐘,這個目前大多數電影都會在一個半小時的樣子,最長的電影竟然有1256分鐘....也太長了吧!看看是哪一部電影。
df[["title","year","runtime"]].sort_values("runtime", ascending=False).head()
是世紀紀念這一部電影,講的是印第安人被白人屠殺的故事。20個小時,哇塞!
畫一張圖來看看吧。時長小於300分鐘的
df["runtime"] = df["runtime"].astype("float")plt.figure(figsize=(12,6))sns.distplot(df[(df["runtime"] < 300) & (df["runtime"] > 0)]["runtime"])
那麼電影時長和電影收益率之間有什麼關係嗎?還是來看圖。
df_mat = df[(df["return_ratio"].notnull()) & (df["runtime"] > 0) & (df["return_ratio"] < 10)]sns.jointplot("return_ratio", "runtime", data=df_mat)plt.show()
0.079,似乎時長和收益率沒啥關係,但是時間越長電影製作費用應該也是越高的,看看這兩者的關係。
df_mat = df[(df["budget"].notnull()) & (df["runtime"] > 0)]sns.jointplot("budget", "runtime", data=df_mat)plt.show()
嗯,0.22,有那麼點關係了,但是還是很低,也是,3小時的藝術電影和科幻大作肯定預算差很多啊。所以時長越長不一定預算就越高
接下來,我想看看電影的平均時間長短,從90年代到2017年。 看看電影製作者認為當時電影的最合適長度的變化。
plt.figure(figsize=(18,5))year_runtime = df[df["year"] != "NaT"].groupby("year")["runtime"].mean()plt.plot(year_runtime.index, year_runtime)plt.xticks(np.arange(1877, 2020, 10.0))plt.show()
早在1914年,電影就開始達到60分鐘的紀錄。從1924年開始,電影開始持續90分鐘,自那以後一直保持不變。
電影製作預算
現在把注意力轉向預算。預計電影預算受到通貨膨脹的嚴重影響。儘管如此,從這個數據中也是可以收集儘可能多的有用的信息的,因為預算往往是預測電影收入和成功的關鍵特徵
df["budget"].describe()
一部電影的平均預算是2160萬美元,而預算的中位數則小得多,為800萬美元。
sns.distplot(df[df["budget"].notnull()]["budget"])
df["budget"].plot(logy=True, kind="hist")
電影預算的分配呈指數衰減。 超過75%的電影預算少於2500萬美元。 接下來,讓我們來看看有史以來最昂貴的電影以及它們產生的收入和回報。
df[df["budget"].notnull()][["title", "budget", "revenue", "return_ratio", "year"]].sort_values( "budget", ascending=False).head(10)
加勒比海盜兩部影片在這個榜單中位居榜首,預算超過3億美元,所有排名前十的製作費用貴最的電影都從中獲得了利潤。獨行俠獲得的利潤較低一些。
看看製作預算和總收入有沒有相關性。
sns.jointplot(x="budget",y="revenue",data=df[df["return_ratio"].notnull()])
0.73,說明製作預算和電影收入有很強的相關性。
再來看看高票房電影
gross_top = df[["poster_path", "title", "budget", "revenue", "year"]].sort_values("revenue", ascending=False).head(5)pd.set_option("display.max_colwidth", 100)HTML(gross_top.to_html(escape=False))
plt.figure(figsize=(18,5))year_revenue = df[(df["revenue"].notnull()) & (df["year"] != "NaT")].groupby("year")["revenue"].max()plt.plot(year_revenue.index, year_revenue)plt.xticks(np.arange(1874, 2024, 10.0))plt.show()
從圖中可以看出,多年來最高的收入一直在穩步上升。 1997年泰坦尼克號的發行,電影界打破了10億美元大關。再過12年,阿凡達突破了20億美元大關。這兩部電影都是由詹姆斯·卡梅隆導演的。
下面看看收益率最高的電影
df[(df["return_ratio"].notnull()) & (df["budget"] > 5e6)][["title", "budget", "revenue", "return_ratio", "year"]].sort_values( "return_ratio", ascending=False).head(10)
這幾部從商業還是從電影本身來說都很成功:
1:E.T.外星人
2:星戰
3:大白鯊
4:驅魔人
5:四個婚禮和一個葬禮
6:教父
7:飛越童真
8:安娜貝爾
9:辣身舞
10:音樂之聲
相關性總結
下面做一個"budget","popularity","revenue", "runtime", "spoken_languages","vote_average", "vote_count","return_ratio", "year"之間的相關性熱圖
df["year"] = df["year"].replace("NaT", np.nan)df["year"] = df["year"].apply(clean_numeric)# 說明:coor() 相關係數# 皮爾遜積矩相關係數(英語:Pearson product-moment correlation coefficient,又稱作 PPMCC或PCCs[1], 常用r或Pearson"s r表示)# 用於度量兩個變數X和Y之間的相關(線性相關),其值介於-1與1之間。在自然科學領域中,該係數廣泛用於度量兩個變數之間的相關程度。# 它是由卡爾·皮爾遜從弗朗西斯·高爾頓在19世紀80年代提出的一個相似卻又稍有不同的想法演變而來。這個相關係數也稱作「皮爾森相關係數r」。sns.set(font_scale=1)corr = df.corr()corr.head()
# 創建元素全為 0 的數組, 類似 np.onesmask = np.zeros_like(corr)mask
mask[np.triu_indices_from(mask)] = Truemask
with sns.axes_style("white"): plt.figure(figsize=(9,9)) ax = sns.heatmap(corr, mask=mask, vmax=.3, square=True, annot=True)
圖中係數越高,說明相關性也就越大,比如說預算和收入最高,這張圖也是對之前的相關性分析的一個匯總。
電影風格
df["genres"] = df["genres"].fillna("[]").apply(ast.literal_eval).apply( lambda x: [i["name"] for i in x] if isinstance(x, list) else [])df[["title","genres"]].head()
s = df.apply(lambda x: pd.Series(x["genres"]),axis=1)s.head()
s = s.stack()s.head()
s = s.reset_index(level=1, drop=True)s.name = "genre"s.head()
gen_df = df.drop("genres", axis=1).join(s)gen_df[["title","genre"]].head()
一共有32種電影風格類型,再看看每種風格有多少部電影
pop_gen = pd.DataFrame(gen_df["genre"].value_counts()).reset_index()pop_gen.columns = ["genre", "movies"]pop_gen.head()
drama最多,也就是劇情片,其次是Comedy喜劇,第三是驚悚類電影,第四是Romance也就是愛情、情感類,第五是動作片,下面把圖畫出來。
plt.figure(figsize=(18,8))sns.barplot(x="genre", y="movies", data=pop_gen.head(15))plt.show()
劇情片是最常見的類型,近一半的電影將自己稱為劇情片。喜劇排在第二,大家都喜歡看喜劇片。前10名的其他主要類型是驚悚,愛情,動作,恐怖,犯罪,紀實片,冒險片,科幻片 那麼下一個問題是世界各地電影類型的趨勢。 科幻電影的需求有所增加嗎? 動畫電影有增長嗎? 做圖來看吧。 首先看2000年開始往後趨勢。只考慮出現在前15個最流行的類型的主題。 排除紀錄片,家庭片和外國片這三種主題。
genres = ["Drama", "Comedy", "Thriller", "Romance", "Action", "Horror", "Crime", "Adventure", "Science Fiction", "Mystery", "Fantasy", "Mystery", "Animation"]pop_gen_movies = gen_df[(gen_df["genre"].isin(genres)) & (gen_df["year"] >= 2000) & (gen_df["year"] <= 2017)]ctab = pd.crosstab([pop_gen_movies["year"]], pop_gen_movies["genre"]).apply(lambda x: x/x.sum(), axis=1)ctab[genres].plot(kind="bar", stacked=True, figsize=(12,8)).legend( loc="center left", bbox_to_anchor=(1, 0.5))plt.title("Stacked Bar Chart of Movie Proportions by Genre")
ctab[genres].plot(kind="line", stacked=False, colormap="jet", figsize=(12,8)).legend( loc="center left", bbox_to_anchor=(1, 0.5))
除了劇情片外,本世紀初以來各類電影的比例一直保持不變。 劇情片的比例下降超過5%。 驚悚電影的份額略有增加。 那麼是否不同類型電影,他們的收益會不同?比如科幻類大片是否一定比文藝片更加賺錢?下面要用到箱線圖。
violin_genres = ["Drama", "Comedy", "Thriller", "Romance", "Action", "Horror", "Crime", "Science Fiction", "Fantasy", "Animation"]violin_movies = gen_df[(gen_df["genre"].isin(violin_genres))]plt.figure(figsize=(18,8))fig, ax = plt.subplots(nrows=1, ncols=1,figsize=(15, 8))sns.boxplot(x="genre", y="revenue", data=violin_movies, palette="muted", ax=ax)ax.set_ylim([0, 3e8])plt.show()
收入最多的類型是動畫類電影,其次是幻想類和科幻類,動作片排到了第四。
同樣的收益率也是需要查看的
plt.figure(figsize=(18,8))fig, ax = plt.subplots(nrows=1, ncols=1,figsize=(15, 8))sns.boxplot(x="genre", y="return_ratio", data=violin_movies, palette="muted", ax =ax)ax.set_ylim([0, 10])plt.show()
恐怖片收益率最高,可能也是因為恐怖片成本不太高的原因,其次是動畫片和愛情片。看來動畫電影對於目前來說是個香餑餑啊
電影演員和幕後
credits_df = pd.read_csv("/Users/chandler/Documents/Data/movie_dataset/credits.csv")credits_df.head()
cast:一個字元串化的list,其中包含演員和他所扮演的人物的dictcrew:一個字元串化的list,其中包含幕後人員和他們所參與的作品的dictid:TMDB的ID
# 這裡處理電影數據中id是NaN的數據,否則連接的時候出問題def convert_int(x): try: return int(x) except: return np.nandf["id"] = df["id"].apply(convert_int)df[df["id"].isnull()]
# 丟掉電影數據中id是NaN的數據df = df.drop([19730, 29503, 35587])df["id"] = df["id"].astype("int")df = df.merge(credits_df, on="id")df.shape
# 把cast和crew這2個字元串化的list取消字元串df["cast"] = df["cast"].apply(ast.literal_eval)df["crew"] = df["crew"].apply(ast.literal_eval)# 長度df["cast_size"] = df["cast"].apply(lambda x: len(x))df["crew_size"] = df["crew"].apply(lambda x: len(x))# 取出cast裡面的name,也就是演員df["cast"] = df["cast"].apply(lambda x: [i["name"] for i in x] if isinstance(x, list) else [])# 拿出crew裡面的導演def get_director(x): for i in x: if i["job"] == "Director": return i["name"] return np.nan# 拿出crew裡面的導演df["director"] = df["crew"].apply(get_director)s = df.apply(lambda x: pd.Series(x["cast"]),axis=1).stack().reset_index(level=1, drop=True)s.name = "actor"cast_df = df.drop("cast", axis=1).join(s)cast_df[["title","cast_size","crew_size","director","actor","year"]].head(20)
下面看看哪些演員和導演的作品最掙錢!
plt.title("Actors with the Highest Total Revenue")cast_df.groupby("actor")["revenue"].sum().sort_values(ascending=False).head(10).plot(kind="bar")
第一名斯坦·李(Stan Lee),1922年12月28日出生於美國紐約,漫畫創作者、演員、編劇第二名塞繆爾·傑克遜(Samuel L. Jackson),1948年12月21日出生於美國華盛頓,美國影視演員、製片人第三名沃維克·戴維斯,1970年2月3日出生於英國Epsom,Surrey,英國演員、製片、編劇他著有《身高不是問題》(Size Matters Not),演出角色有《哈利波特》系列中的菲利烏斯·弗利維
plt.title("Directors with the Highest Total Revenue")df.groupby("director")["revenue"].sum().sort_values(ascending=False).head(10).plot(kind="bar")
第一名史蒂文·斯皮爾伯格(Steven Allan Spielberg)1946年12月18日生於美國俄亥俄州的辛辛那提市,猶太人血統,電影導演、編劇和電影製作人第二名彼得·傑克遜,1961年10月31日出生於紐西蘭首都惠靈頓。導演、編劇、製作人,代表作指環王、霍比特人等第三名邁克爾·貝(Michael Bay),1965年2月17日出生於美國加利福尼亞州洛杉磯,導演、製片人、編劇、特效師代表作變形金剛
下面再看看演員和導演飾演和製作的電影平均收入最高的,當然規定他們至少參與了5部作品
actor_list = cast_df.groupby("actor")["revenue"].count().sort_values(ascending=False)actor_list = list(actor_list[actor_list >= 5].index)director_list = df.groupby("director")["revenue"].count().sort_values(ascending=False)director_list = list(director_list[director_list >= 5].index)
演員飾演的電影平均收入最高的排名
plt.title("Actors with Highest Average Revenue")cast_df[cast_df["actor"].isin(actor_list)].groupby("actor")["revenue"].mean().sort_values( ascending=False).head(10).plot(kind="bar", colormap="Greens_r")plt.show()
導演製作的電影平均收入最高的排名
plt.title("Directors with Highest Average Revenue")df[df["director"].isin(director_list)].groupby("director")["revenue"].mean().sort_values( ascending=False).head(10).plot(kind="bar", colormap="Greens_r")plt.show()
哪個演員和導演對於製作一部電影來說是最不會虧錢的?這要考慮導演或演員帶來的回報率,當然只會考慮那些至少有一千萬美元的電影。另外,只會考慮在至少5部電影中工作過的演員和導演。
success_df = cast_df[(cast_df["return_ratio"].notnull()) & ( cast_df["revenue"] > 10000000) & (cast_df["actor"].isin(actor_list))]pd.DataFrame(success_df.groupby("actor")["return_ratio"].mean().sort_values(ascending=False).head(10))
pd.DataFrame(success_df.groupby("actor")["return_ratio"].mean().sort_values(ascending=False).head(10)).plot(kind="bar")
success_df = df[(df["return_ratio"].notnull()) & (df["revenue"] > 10000000) & ( df["director"].isin(director_list))]pd.DataFrame(success_df.groupby("director")["return_ratio"].mean().sort_values(ascending=False).head(10))
這裡存在異常,看看這個導演的作品。
df[(df["director"] == "John G. Avildsen") & ( df["return_ratio"].notnull())][["title", "budget", "revenue", "return_ratio", "year"]]
空手道小子,第二部分只有113美元的預算。 這肯定不正常,因為官方數字表示,這部電影花費1300萬美元。 到這裡結束探索性數據分析,下面看看哪些因素對電影的成功有著重要影響。
對電影收入的影響
rgf = df[df["return_ratio"].notnull()]rgf.shape
df["return_ratio"].head()
rgf.columns
rgf = rgf.drop(["id", "overview", "poster_path", "release_date", "status", "tagline", "video", "return_ratio", "crew"], axis=1)rgf.columns
rgf.head(1)
工程任務:belongs_to_collection變成一個布爾變數。 1表示電影是系列電影,而0表示不是。genres將被轉換成流派的數量。homepage將被轉換為一個布爾變數,將指示電影是否有主頁。original_language將被一個名為is_foreign的功能所取代,以表示某部電影是英語還是其他語言。production_companies將被替換為合作製作電影的製作公司的數量。production_countries將被電影拍攝國家的數量所取代。day將被轉換為二進位功能,以表明電影是否在星期五發行。month將被轉換成一個變數,表明month是否為假期。
# 數據預處理s = rgf.apply(lambda x: pd.Series(x["genres"]),axis=1).stack().reset_index(level=1, drop=True)s.name = "genre"s.head()
gen_rgf = rgf.drop("genres", axis=1).join(s)genres_train = gen_rgf["genre"].drop_duplicates()genres_train.head()
# 特徵工程def feature_engineering(df): # belongs_to_collection變成一個布爾變數。 1表示電影是集合的一部分,而0表示不是 df["belongs_to_collection"] = df["belongs_to_collection"].apply(lambda x: 0 if x == np.nan else 1) # genres轉換成流派的數量 for genre in genres_train: df[str(genre)] = df["genres"].apply(lambda x: 1 if genre in x else 0) df["genres"] = df["genres"].apply(lambda x: len(x)) # homepage將被轉換為一個布爾變數,將指示電影是否有主頁 df["homepage"] = df["homepage"].apply(lambda x: 0 if x == np.nan else 1) # original_language將被一個名為is_foreign的功能所取代,以表示某部電影是英語還是其他語言 df["is_english"] = df["original_language"].apply(lambda x: 1 if x=="en" else 0) df = df.drop("original_language", axis=1) # production_companies將被替換為合作製作電影的製作公司的數量 df["production_companies"] = df["production_companies"].apply(lambda x: len(x)) # production_countries將被電影拍攝國家的數量所取代 df["production_countries"] = df["production_countries"].apply(lambda x: len(x)) # day將被轉換為二進位功能,以表明電影是否在星期五發行 df["is_Friday"] = df["day"].apply(lambda x: 1 if x=="Fri" else 0) df = df.drop("day", axis=1) # month將被轉換成一個變數,表明month是否為假期 df["is_Holiday"] = df["month"].apply(lambda x: 1 if x in ["Apr", "May", "Jun", "Nov"] else 0) df = df.drop("month", axis=1) # 丟掉"title", "cast", "director" df = df.drop(["title", "cast", "director"], axis=1) return dfX, y = rgf.drop("revenue", axis=1), rgf["revenue"]X = feature_engineering(X)X = pd.get_dummies(X, prefix="is")X.head()
# 如果電影時長為NaN,就用電影時長平均值填充X["runtime"] = X["runtime"].fillna(X["runtime"].mean()) # 如果平均vote數量為NaN,就用平均vote數量填充X["vote_average"] = X["vote_average"].fillna(X["vote_average"].mean())train_X, test_X, train_y, test_y = train_test_split(X, y, train_size=0.75, test_size=0.25)X.shape
reg = GradientBoostingRegressor()reg.fit(train_X, train_y)reg.score(test_X, test_y)
下面再和DummyRegressor進行對比
關於DummyRegressor:DummyRegressor is a regressor that makes predictions using simple rules. This regressor is useful as a simple baseline to compare with other (real) regressors. Do not use it for real problems.
dummy = DummyRegressor()dummy.fit(train_X, train_y)dummy.score(test_X, test_y)
我們的數值比這高多了
plt.figure(figsize=(10,12))sns.barplot(x=reg.feature_importances_, y=X.columns)
針對這份數據來說,一部電影的總收入,vote_count,popularity,budget,year,crew_size這幾個對其影響是很大的。
下面再來看看收益率方面
# 取出收益率數值不為空的數據cls = df[df["return_ratio"].notnull()]cls.shape
cls.columns
# 拿掉不需要的columnscls = cls.drop(["id", "overview", "poster_path", "release_date", "status", "tagline", "revenue"], axis=1)# 收益率大於1就為1,小於1就為0cls["return_ratio"] = cls["return_ratio"].apply(lambda x: 1 if x >=1 else 0)cls["return_ratio"].value_counts()
# 填充belongs_to_collection為空的數值cls["belongs_to_collection"] = cls["belongs_to_collection"].fillna("").apply(lambda x: 0 if x == "" else 1)# 填homepage為空的數值cls["homepage"] = cls["homepage"].fillna("").apply(lambda x: 0 if x == "" else 1)cls.columns
def classification_engineering(df): # genres轉換成流派的數量 for genre in genres_train: df["is_" + str(genre)] = df["genres"].apply(lambda x: 1 if genre in x else 0) df["genres"] = df["genres"].apply(lambda x: len(x)) # 丟棄homepage df = df.drop("homepage", axis=1) # 如果是英語片就為1,不是就為0 df["is_english"] = df["original_language"].apply(lambda x: 1 if x=="en" else 0) # 丟棄電影語言列 df = df.drop("original_language", axis=1) # 製作公司改為製作公司數量 df["production_companies"] = df["production_companies"].apply(lambda x: len(x)) # 拍攝地改為拍攝地數量 df["production_countries"] = df["production_countries"].apply(lambda x: len(x)) # day改為是否是周五發行,是就為1,不是就為0 df["is_Friday"] = df["day"].apply(lambda x: 1 if x=="Fri" else 0) # 丟掉day這一列 df = df.drop("day", axis=1) # 如果電影是在4、5、6、11月發行就為1,否則為0 df["is_Holiday"] = df["month"].apply(lambda x: 1 if x in ["Apr", "May", "Jun", "Nov"] else 0) # 丟棄month列 df = df.drop("month", axis=1) # 丟棄"title", "cast", "director"列 df = df.drop(["title", "cast", "director"], axis=1) df["runtime"] = df["runtime"].fillna(df["runtime"].mean()) df["vote_average"] = df["vote_average"].fillna(df["vote_average"].mean()) df = df.drop("crew", axis=1) return df cls = classification_engineering(cls)cls.columns
cls.head()
X, y = cls.drop("return_ratio", axis=1), cls["return_ratio"]train_X, test_X, train_y, test_y = train_test_split(X, y, train_size=0.75, test_size=0.25, stratify=y)clf = GradientBoostingClassifier()clf.fit(train_X, train_y)clf.score(test_X, test_y)
dummy = DummyClassifier(strategy="most_frequent")dummy.fit(train_X, train_y)dummy.score(test_X, test_y)
plt.figure(figsize=(10,12))sns.barplot(x=clf.feature_importances_, y=X.columns)
電影推薦
%matplotlib inlineimport pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsfrom scipy import statsfrom ast import literal_evalfrom sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizerfrom sklearn.metrics.pairwise import linear_kernel, cosine_similarityfrom nltk.stem.snowball import SnowballStemmerfrom nltk.stem.wordnet import WordNetLemmatizerfrom nltk.corpus import wordnetfrom surprise import Reader, Dataset, SVD, evaluateimport warnings; warnings.simplefilter("ignore")
根據電影的受歡迎程度和流派為每個用戶提供推薦列表。更受歡迎,更受好評的電影更有被普通觀眾喜歡的可能性。這個是一個簡單的推薦,沒有針對客戶定製化。
md = pd. read_csv("/Users/chandler/Documents/Data/movie_dataset/movies_metadata.csv")md.head()
md.columns
md["genres"] = md["genres"].fillna("[]").apply( literal_eval).apply(lambda x: [i["name"] for i in x] if isinstance(x, list) else [])
使用TMDB評分來製作熱門電影排行榜。使用IMDB的加權評級公式來構建圖表。在數學上,它表示如下:加權評分(WR)= (v*R/(v+m) + m*C/(v+m))v是電影的票數m是最小投票數R是電影的平均評分C是整個報告中的平均投票數下一步是確定一個合適的m值,需要在圖表中列出的最低投票數。使用95百分位作為截止條件。換句話說,對於在圖表中顯示的電影,他的投票數必須大於等於95%的電影的票數。我將建立我們整體的Top 250 Chart,並定義一個函數為特定類型建立圖表。
vote_counts = md[md["vote_count"].notnull()]["vote_count"].astype("int")vote_averages = md[md["vote_average"].notnull()]["vote_average"].astype("int")C = vote_averages.mean()C
vote_counts.head()
# quantile 可以計算 Series 或 DataFrame 列的樣本分位數m = vote_counts.quantile(0.95)m
md["year"] = pd.to_datetime(md["release_date"], errors="coerce").apply( lambda x: str(x).split("-")[0] if x != np.nan else np.nan)qualified = md[(md["vote_count"] >= m) & (md["vote_count"].notnull()) & ( md["vote_average"].notnull())][["title", "year", "vote_count", "vote_average", "popularity", "genres"]]qualified["vote_count"] = qualified["vote_count"].astype("int")qualified["vote_average"] = qualified["vote_average"].astype("int")qualified.shape
qualified.sort_values("vote_count").head()
也就是說vote_count投票數必須大於等於434票才能放進這張表中
# 按照IMDB的評分公式計算def weighted_rating(x): v = x["vote_count"] R = x["vote_average"] return (v*R/(v+m)) + (m*C/(m+v))# 把計算的wr結果添加至上面的電影表中qualified["wr"] = qualified.apply(weighted_rating, axis=1)qualified = qualified.sort_values("wr", ascending=False).head(250)qualified.head(15)
1:盜夢空間2:黑暗騎士3:星際穿越4:搏擊俱樂部5:指環王:魔戒再現6:低俗小說7:肖申克的救贖8:指環王:王者歸來9:阿甘正傳10:指環王:雙塔奇兵11:星戰12:回到未來13:教父14:星球大戰帝國反擊戰15:七宗罪
圖片來自豆瓣(推薦的電影在豆瓣全是高分電影)
我想以上電影大部分人都看過,我反正是全都看過,這推薦還是非常靠譜的,IMDB的公式很靠譜。 其中前三全是諾蘭的電影,??x啊!我反正是很喜歡諾蘭的電影,尤其星際和盜夢,超級喜歡!
圖表中,電影的類型也能看出用戶的喜好,不如針對類型再做一個推薦
s = md.apply(lambda x: pd.Series(x["genres"]),axis=1).stack().reset_index(level=1, drop=True)s.name = "genre"gen_md = md.drop("genres", axis=1).join(s)
# 建立電影類型圖表,分位數從95放寬到85def build_chart(genre, percentile=0.85): # 調用函數傳入指定類型,得出指定類型電影的推薦表格 df = gen_md[gen_md["genre"] == genre] # 過濾掉票數為空的數據 vote_counts = df[df["vote_count"].notnull()]["vote_count"].astype("int") # 過濾掉平均得票為空的數據 vote_averages = df[df["vote_average"].notnull()]["vote_average"].astype("int") # 求得IMDB公式中的C C = vote_averages.mean() # 放寬分位數至85 m = vote_counts.quantile(percentile) # 過濾出滿足條件的電影 qualified = df[(df["vote_count"] >= m) & (df["vote_count"].notnull()) & ( df["vote_average"].notnull())][["title", "year", "vote_count", "vote_average", "popularity"]] # 過濾出拍攝國家名稱 qualified["country"] = df["production_countries"].fillna("[]").apply( literal_eval).apply(lambda x: [i["name"] for i in x] if isinstance(x, list) else []) # 數據類型轉換 qualified["vote_count"] = qualified["vote_count"].astype("int") qualified["vote_average"] = qualified["vote_average"].astype("int") # 按照IMDB公式計算分值 qualified["wr"] = qualified.apply( lambda x: (x["vote_count"]/(x["vote_count"]+m) * x["vote_average"]) + (m/(m+x["vote_count"]) * C), axis=1) qualified = qualified.sort_values("wr", ascending=False).head(250) return qualified
查看類型
gen_md["genre"].value_counts()
劇情片推薦
build_chart("Drama").head(15)
1:勇奪芳心(沙魯克汗)2:蝙蝠俠:暗夜騎士3:星際穿越4:搏擊俱樂部5:肖申克的救贖6:阿甘正傳7:教父8:模仿遊戲9:獅子王10:觸不可及11:布達佩斯大飯店12:沉默的羔羊13:致命魔術14:辛德勒的名單15:爆裂鼓手
圖片來自豆瓣
喜劇類電影推薦
build_chart("Comedy").head(15)
1:勇奪芳心(沙魯克汗)2:阿甘正傳3:回到未來4:觸不可及5:布達佩斯大飯店6:美麗人生7:奇愛博士8:摩登時代9:熱情如火(瑪麗蓮·夢露)10:大獨裁者11:桃色公寓(傑克·李蒙、莎莉·麥克琳)12:城市之光13:美味盛宴14:尋子遇仙記(卓別林)15:將軍號(巴斯特基頓、馬里昂馬克)
圖片來自豆瓣
驚悚片推薦
build_chart("Thriller").head(15)
1:盜夢空間2:蝙蝠俠:暗夜騎士3:低俗小說4:七宗罪5:模仿遊戲6:沉默的羔羊7:致命魔術8:這個殺手不太冷9:記憶碎片10:閃靈11:落水狗12:非常嫌疑犯13:疤面煞星14:房間15:驚魂記
圖片來自豆瓣
愛情/情感類電影推薦
build_chart("Romance").head(15)
1:勇奪芳心(沙魯克汗)2:阿甘正傳 (湯姆漢克斯)3:迷魂記(詹姆斯·斯圖爾特、金·諾瓦克)4:你的名字(新海誠)5:熱情如火(瑪麗蓮·夢露)6:天堂電影院(薩瓦特利·卡西歐)7:紙人(華特·迪士尼電影工作室)8:初戀這首情歌9:桃色公寓(傑克·李蒙、莎莉·麥克琳)10:下女的誘惑(金敏喜、金泰梨、河正宇、趙震雄)11:城市之光(卓別林)12:愛,簡單13:心跳無限次(短篇動畫電影)14:泰坦尼克號15:烏雲背後的幸福線(布萊德利庫伯、詹妮弗勞倫斯)
圖片來自豆瓣
動作片推薦
build_chart("Action").head(15)
1:盜夢空間2:蝙蝠俠:暗夜騎士3:指環王:魔戒再現4:指環王:王者歸來5:指環王:雙塔奇兵6:星戰7;帝國反擊戰8:疤面煞星9:老男孩10:七武士11:兄弟連12:M就是兇手13:阿凡達14:復仇者聯盟15:死侍
圖片來自豆瓣
@豆瓣Ruby
推薦閱讀: