kaggle項目:IMDB電影數據分析

導讀

  • 本文為練手文,主要練習圖表展示數據,大神請繞道,謝謝;
  • 本文主要介紹分析的思路和圖表展示,代碼將一次性附在後面;
  • 本文分為4個部分,閱讀需要大約15分鐘。

一,概述

電影作為七大藝術之一,已成為重要的藝術形式和娛樂載體,甚至成為了我們生活中必不可少的部分。同時,電影也可看成是一種極度依附於資本和工業水準的投資形式,因此分析電影數據有助於抓住電影工業發展的趨勢。下面是kaggle官網對這個項目的詳情頁:

TMDB 5000 Movie Dataset?

www.kaggle.com

如上是一些官網的介紹,下載好數據後,重點關注變數名:

  • id:標識號
  • imdb_id:IMDB 標識號
  • popularity:在 Movie Database 上的相對頁面查看次數
  • budget:預算(美元)
  • revenue:收入(美元)
  • original_title:電影名稱
  • cast:演員列表,按 | 分隔,最多 5 名演員
  • homepage:電影首頁的 URL
  • director:導演列表,按 | 分隔,最多 5 名導演
  • tagline:電影的標語
  • keywords:與電影相關的關鍵字,按 | 分隔,最多 5 個關鍵字
  • overview:劇情摘要
  • runtime:電影時長genres:風格列表,按 | 分隔,最多 5 種風格
  • genres:風格列表,按 | 分隔,最多 5 種風格
  • production_companies:製作公司列表,按 | 分隔,最多 5 家公司
  • release_date:首次上映日期
  • vote_count:評分次數
  • vote_average:平均評分

這有助於我們快速理解數據。在jupyter notebook中打開數據後就可以開始分析了。


二,提出問題

如果我是電影行業的數據分析師,現在公司要製作電影,想知道電影預算、評分與票房的關係,各種電影類型隨時間變化的趨勢圖,電影產量、票房的趨勢,哪些風格電影最受歡迎等問題,則可提出如下問題:

  • 每年上映電影數量、電影總票房是多少?
  • 電影預算、評分與票房的關係?
  • 那種風格、關鍵詞出現的最多?
  • Universal Pictures 和 Paramount Pictures 之間的對比情況如何?
  • 改編電影和原創電影的對比情況如何?
  • 電影時長為多少最合適?

帶著這些問題,結合數據情況,我們開始分析。


三,每年上映電影數量、電影總票房統計

通過提取數據genres列的分割,並做統計,取前10名用python的matplotlib包可視化後得出如下表:

如上圖所示,電影工業在最近三十幾年呈現爆髮式增長,2009年數量最多。雖然電影數量最近二十年內有3次較大的波動,但電影總票房仍然保持在較高的水準,說明電影市場已經成熟,有固定的消費人群,可見投資電影仍然是很好的投資方式。


四,電影預算、評分與票房的關係

電影的預算直接影響電影的質量,電影的評分也會影響電影的票房。通過數據中的budget,revenue,vote_average三列數據進行分析,如下圖:

如上二圖所示,電影預算、評分基本與電影票房呈正線性相關的關係,這也符合正常經濟規律。


五,電影類型、關鍵詞的走勢

首先根據數據genres列的分割,並做統計圖:

根據上圖,用雲圖展示更加直接:

如上圖,可見投資一部電影的話,上圖中的類型是最好的選擇,尤以comedy和drama最佳。


六,Universal Pictures VS Paramount Pictures

環球影業和派拉蒙這兩家巨頭,他們的電影產量、電影票房之間的對比情況如何呢?

如上圖,電影產量上環球影業要多一些,而票房上兩家公司不相上下,且有一段時間派拉蒙都好過環球影業。因此電影系列開發可參考環球影業的電影項目布局;而電影藝術手法等電影質量方面問題,可參考派拉蒙出品的電影。


七,改編電影 VS 原創電影

電影非常依賴故事性,一個好故事是電影成功的重要條件。我們可以統計下keywords變數中的based on novel欄位來判斷該電影是原創還是改編,結果如下:

可見,絕大部分電影還是原創為主。這也說明了為什麼編劇是電影行業的重要資產,好萊塢也特別重視編劇的培養和原創性、版權等權益的維護,編劇隊伍的規模直接決定了電影行業的規模,而如果要從事電影行業的話,編劇會是一個不錯的選擇。


八,電影時長

一部電影的時長為多少時合適呢?選取數據集的duration列,60分鐘以下定義為短電影,60~150分鐘為中等時長電影,大於150分鐘為長電影。如下:

如上所示,電影時間在60~150分鐘之間最好。這也符合我們平時觀影的體驗,太短會覺得不夠,性價比不高,影響票房;太長則觀眾會覺得拖沓,精力無法集中,影響電影口碑。


九,分析結論

綜上所述,可得出如下幾個結論:

  1. 目前電影市場成熟,產量和票房都處於較高的水平;
  2. 電影的預算、評價會直接影響電影票房,故一定的電影預算是電影票房成功的基礎;
  3. 最受歡迎的電影類型是comedy和drama,因此投資這兩個類型的電影是不錯的選擇;
  4. 環球影業(Universal Pictures)和派拉蒙(Paramount Pictures)兩者,前者的電影數量多餘後者,票房表現不相上下。
  5. 原創電影在電影工業中占絕對主導地位,因此應重視編劇和劇本的選擇;
  6. 電影時長應控制在60~150分鐘,最符合觀眾的觀影習慣,也最易獲得成功。

十,Python代碼

# 準備要用的包import numpy as npimport pandas as pdimport jsonimport matplotlib.pyplot as pltimport seaborn as snsfrom wordcloud import WordCloud

準備數據

# 定義函數將movies導入進來def load_tmdb_movies(path): df = pd.read_csv(path) #轉換日期格式 df[release_date] = pd.to_datetime(df[release_date]).apply(lambda x: x.date()) #將movie中的json列提取出來 json_columns = [genres, keywords, production_countries, production_companies, spoken_languages] #定義for循環將其轉化為字典 for column in json_columns: df[column] = df[column].apply(json.loads) return df# 定義函數將credits導入進來def load_tmdb_credits(path): df=pd.read_csv(path) json_columns=[cast,crew] # 處理json為字典 for column in json_columns: df[column]=df[column].apply(json.loads) return df# 定義丟失的列名的列表LOST_COLUMNS = [ actor_1_facebook_likes, actor_2_facebook_likes, actor_3_facebook_likes, aspect_ratio, cast_total_facebook_likes, color, content_rating, director_facebook_likes, facenumber_in_poster, movie_facebook_likes, movie_imdb_link, num_critic_for_reviews, num_user_for_reviews ]# 更改列名,定義字典TMDB_TO_IMDB_SIMPLE_EQUIVALENCIES = { budget: budget, genres: genres, revenue: gross, title: movie_title, runtime: duration, original_language: language, keywords: plot_keywords, vote_count: num_voted_users, }IMDB_COLUMNS_TO_REMAP = {imdb_score: vote_average}# 獲取語言和國家def safe_access(container, index_values): result = container try: for idx in index_values: result = result[idx] return result except IndexError or KeyError: return pd.np.nan# 獲取導演Directordef get_director(crew_data): directors = [x[name] for x in crew_data if x[job] == Director] return safe_access(directors, [0])# 數據用|隔開def pipe_flatten_names(keywords): return |.join([x[name] for x in keywords])# 定義最後合併的數據表def convert_to_original_format(movies, credits): tmdb_movies = movies.copy() #修改列名 tmdb_movies.rename(columns=TMDB_TO_IMDB_SIMPLE_EQUIVALENCIES, inplace=True) #取出日期中 的年份 tmdb_movies[title_year] = pd.to_datetime(tmdb_movies[release_date]).apply(lambda x: x.year) #國家 tmdb_movies[country] = tmdb_movies[production_countries].apply(lambda x: safe_access(x, [0, name])) #語言 tmdb_movies[language] = tmdb_movies[spoken_languages].apply(lambda x: safe_access(x, [0, name])) #導演 tmdb_movies[director_name] = credits[crew].apply(get_director) #一號演員 tmdb_movies[actor_1_name] = credits[cast].apply(lambda x: safe_access(x, [0, name])) #二號演員 tmdb_movies[actor_2_name] = credits[cast].apply(lambda x: safe_access(x, [1, name])) #三號演員 tmdb_movies[actor_3_name] = credits[cast].apply(lambda x: safe_access(x, [2, name])) #四號演員 tmdb_movies[actor_4_name] = credits[cast].apply(lambda x: safe_access(x, [3, name])) #五號演員 tmdb_movies[actor_5_name] = credits[cast].apply(lambda x: safe_access(x, [4, name])) #用" |" 分割幾個字典 tmdb_movies[genres] = tmdb_movies[genres].apply(pipe_flatten_names) tmdb_movies[plot_keywords] = tmdb_movies[plot_keywords].apply(pipe_flatten_names) tmdb_movies[production_companies]=tmdb_movies[production_companies].apply(pipe_flatten_names) return tmdb_movies# 生產momoviesmovies=load_tmdb_movies(/Users/xiachaozzia/數據分析/第六關筆記/tmdb-5000-movie-dataset/tmdb_5000_movies.csv)# 生成creditscredits=load_tmdb_credits(/Users/xiachaozzia/數據分析/第六關筆記/tmdb-5000-movie-dataset/tmdb_5000_credits.csv)# 合併成一個數據表,並應用上述諸多函數original_format =convert_to_original_format(movies, credits)# original_format的概況original_format.info()original_format.head()# movies,credits的概況movies.info(),print(-*50),credits.info()

  • 如上所示,顯示整理數據成功,下面開始清洗數據

清洗數據

# 查看缺失值original_format.count().sort_values(ascending=False)# 定義函數,調出有缺失的列及其值columnList=original_format.columnsfor i in columnList: if original_format[i].count() < 4803: print (i,:,original_format[i].count())# 刪除homepage列original_format=original_format.drop(homepage,axis=1)# language列用眾數填充# 查看眾數original_format[language].value_counts()# 用language列的眾數English填充original_format[language]=original_format[language].fillna(English)# 片長duration用均值來填充original_format[duration]=original_format[duration].fillna(original_format[duration].mean())# 用U來填充overview缺失值original_format[overview]=original_format[overview].fillna(U)# 獲取title_year列的眾數original_format[title_year].value_counts()# title_year列用眾數2009填充缺失值original_format[title_year]=original_format[title_year].fillna(2009.0)# 順便將其類型改為浮點型,方便運算original_format[title_year]=original_format[title_year].astype(float)# 獲取country列的眾數original_format[country].value_counts()# country列的缺失值用眾數United States of America填充original_format[country]=original_format[country].fillna(United States of America)# tagline(電影主題)等餘下7個列,無規律可循,用U來填充original_format[tagline]=original_format[tagline].fillna(U)original_format[director_name]=original_format[director_name].fillna(U)original_format[actor_1_name]=original_format[actor_1_name].fillna(U)original_format[actor_2_name]=original_format[actor_2_name].fillna(U)original_format[actor_3_name]=original_format[actor_3_name].fillna(U)original_format[actor_4_name]=original_format[actor_4_name].fillna(U)original_format[actor_5_name]=original_format[actor_5_name].fillna(U)# 檢查是否清理完畢original_format.info()

獲取相關舉證

corrDF=original_format.corr()corrDF[gross].sort_values(ascending=False)

電影評分與票房

plt.style.use(ggplot)f,ax=plt.subplots(figsize=(8,5))sns.regplot(x=vote_average,y=gross,data=original_format,ax=ax)plt.title(電影評分和票房的關係,fontsize=20)plt.xlabel(電影評分,fontsize=15)plt.ylabel(票房,fontsize=15)plt.grid(True)# 一定要在plt.show()之前保存照片,不然保存的是一片空白plt.savefig(電影評分和票房的關係.jpg,bbox_inches=tight)plt.show()

出現次數最多的風格類型

# 準備子集:將genres單獨提取出來,作為分析數據dataGdf=pd.DataFrame(original_format[genres])dataGdf=dataGdf.reset_index(drop=True)# 查看具體內容dataGdf[genres].head()# 將上述格式轉為列表dataGdf[genres]=dataGdf[genres].str.split(|)dataGdf[genres].head()# 將genres里的每一個單元的每一個元素,都加入到一個list中list1=[]for i in dataGdf[genres]: list1.extend(i)# 將list1轉化為Seriesax=pd.Series(list1)ax.describe()# ax# 定義畫紙plt.subplots(figsize=(8,5))# 提取前10名,並畫柱狀圖# barh表示橫向,bar表示豎向ax.value_counts()[0:10].plot(kind=barh,width=0.9)# 數據從大到小,比較好看ax2=ax.value_counts()[0:10].sort_values(ascending=True)plt.subplots(figsize=(8,5)) # ax3必不可少,只有plt才能設置文本text,否則下面的for會報錯ax3=ax2.plot(kind=barh,width=0.9)# 設置注釋文本for i, v in enumerate(ax2.values): #這裡比較難,首先應明白enumerate返回的是什麼,見下一個cell, #其次,0.8表示text的x坐標,i表示text的y坐標 ax3.text(1,i,v,fontsize=15,color=white,weight=bold)plt.title(Top Genres)plt.xlabel(出現次數)plt.ylabel(風格類型)plt.savefig(出現次數最多的風格類型.jpg)plt.show()

如上可知10中出現頻率最高的劇種

# 理解enumerate輸出的是什麼for i, v in enumerate(ax2.values): print (i ,v)

Top Genres雲圖

wc=WordCloud(background_color=white,max_words=2000,random_state=1). generate_from_frequencies(ax2.to_dict())plt.figure(figsize=(10,5))plt.imshow(wc,interpolation=bilinear)plt.axis(off)plt.savefig(Top Genres雲圖.jpg)plt.show()

上映電影年份佔比餅圖

year_df=original_format[title_year].value_counts()year_df=year_df/year_df.sum()year_df2=year_df[year_df>=0.01]year_df2[Others]=year_df[year_df<0.01].sum()# explode = (len(year_df2))year_df2.plot(kind=pie,label=,startangle=0,shadow=False, figsize=(10,10),autopct="%1.1f%%")plt.title(上映電影數量最多的年份,fontsize=22)plt.savefig(上映電影數量最多的年份.jpg)plt.show()

每年電影總票房變化趨勢圖

# 每年上映電影數量統計movies_year_count=original_format.groupby([title_year])[movie_title].count()movies_year_count.plot(figsize=(10,5),marker=.)plt.title(每年上映電影數量統計,fontsize=22)plt.xlabel(年份,fontsize=15)plt.ylabel(上映電影數量,fontsize=15)plt.savefig(每年上映電影數量統計.png)plt.show()# 每年總票房統計movies_year_gross=original_format.groupby([title_year])[gross].sum()movies_year_gross.plot(figsize=(10,5),marker=.)plt.title(每年總票房統計,fontsize=22)plt.xlabel(年份,fontsize=15)plt.ylabel(總票房,fontsize=15)plt.savefig(每年票房統計.jpg)plt.show()

電影風格隨時間變化趨勢圖

# 準備數據:年份、電影風格movies_genres=original_format.loc[:,[title_year,genres]]# 創建存放風格的空集合list_genres=set()# i是每一個分割出來的元素for i in original_format[genres].str.split(|):# 將i添加到集合set() list_genres=set().union(i,list_genres)# 將集合list_genres轉化成列表listlist_genres=list(list_genres)# 看看效果如何print(list_genres)# 刪除其中的控制,注意列表list的刪除不能用droplist_genres.remove()print(list_genres)# 準備數據表dataframedf_reduced=pd.DataFrame()df_reduced[title_year]=movies_genres[title_year]for genre in list_genres: df_reduced[genre]=movies_genres[genres].str.contains(genre).apply(lambda x:1 if x else 0) df_reduced=df_reduced.sort_values(by=title_year,ascending=True)genre_details=list(map(str,(original_format[genres])))genre=[]for i in genre_details: split_genre=list(map(str,i.split(|))) for j in split_genre: if j not in genre: genre.append(j)print(genre)original_format[title_year]=original_format[title_year].astype(int)min_year=original_format[title_year].min()max_year=original_format[title_year].max()print(min_year,max_year)genre_df=pd.DataFrame(index=range(min_year,max_year+1),columns=genre)genre_df=genre_df.fillna(value=0)genre_df.info()genre_df.head()year=np.array(original_format[title_year])z=0for i in genre_details: split_genre=list(map(str,i.split(|))) for j in split_genre: genre_df.loc[year[z],j]=genre_df.loc[year[z],j]+1 z+=1genre_df.info()ax2genre_df=genre_df.drop([Fantasy,Animation,Western,Romance,Mystery,History,War,Music,Documentary,Foreign,TV Movie],axis=1)genre_df.head()genre_df1=genre_df.copy()genre_df1.rename(columns={Action:Action(動作片), Adventure:Adventure(冒險片), Science Fiction:Science Fiction(科幻片), Crime:Crime(犯罪片), Drama:Drama(戲劇), Thriller:Thriller(驚悚片), Family:Family(家庭片), Comedy:Comedy(喜劇片), Horror:Horror(恐怖片)},inplace=True)genre_df1.head()genre_df1.plot(figsize=(16,8),marker=.)plt.grid(True)plt.title(各種類型電影數量隨時間變化趨勢圖,fontsize=22)plt.xlabel(時間(年),fontsize=18)plt.ylabel(電影數量,fontsize=18)plt.legend(fontsize=15)plt.savefig(各種類型電影數量隨時間變化趨勢圖.jpg)plt.show()

Universal picture 和 Paramount Pictures 兩家公司對比情況

兩家公司電影數量

# 定義存放數據的數據表companies=pd.DataFrame()companies[production_companies]=original_format[production_companies]companies=companies[(True-companies[production_companies].isin([]))]# 拆分每個元素companies[production_companies]=companies[production_companies].str.split(|)list2=[]for i in companies[production_companies]: list2.extend(i)companiesSeries=pd.Series(list2)companiesSeries.value_counts()companiesSeries2=companiesSeries.value_counts()[Universal Pictures:Paramount Pictures].sort_values(ascending=True)companiesSeries2fig,ax=plt.subplots(figsize=(14,5))companiesSeries2.plot(ax=ax,kind=barh,width=0.2)plt.title(電影產量:環球影業(Universal) VS 派拉蒙影業(Paramount),fontsize=22)# 設置刻度的顯示大小plt.xticks(fontsize=15)plt.yticks(fontsize=12)plt.text(150,0,285,fontsize=18,color=white)plt.text(150,1,311,fontsize=18,color=white)fig.savefig(電影產量:環球影業(Universal) VS 派拉蒙影業(Paramount).jpg)plt.show()

兩家公司電影票房對比

# 選取子集:公司名稱、票房companiesGross=original_format[[production_companies,gross]]# 定義存放公司名稱的集合list_companies=set()# 拆分,並遍歷添加到集合for i in companiesGross[production_companies].str.split(|): list_companies=set().union(i,list_companies)# 將集合轉換成列表list_companies=list(list_companies) list_companies.remove()df_reduced2=pd.DataFrame()df_reduced2[title_year]=original_format[title_year]for i in list_companies: df_reduced2[companies]=companiesGross[production_companies].str.contains(i).apply(lambda x: 1 if x else 0)# 按時間序列拍好df_reduced2=df_reduced.sort_values(by=title_year,ascending=True)companies_details=list(map(str,(companiesGross[production_companies])))companies_list=[]for i in companies_details: split_companies=list(map(str,i.split(|))) for j in split_companies: if j not in companies_list: companies_list.append(j)companies_df=pd.DataFrame(index=range(min_year,max_year+1),columns=companies_list)companies_df=companies_df.fillna(value=0)companies_df.head()# 接下來將每個公司的票房加上去companies_1=np.array(original_format[gross])z=0for i in companies_details: split_companies=list(map(str,i.split(|))) for j in split_companies: companies_df.loc[year[z],j]=companies_df.loc[year[z],j]+companies_1[z] z+=1productionDF=companies_df[[Universal Pictures,Paramount Pictures]]productionDF2=productionDF.copy()fig,ax=plt.subplots(figsize=(10,7))productionDF2.plot(ax=ax,marker=.)plt.title(電影票房:環球影業(Universal) VS 派拉蒙影業(Paramount),fontsize=22)plt.xlabel(時間,fontsize=15)plt.xticks(fontsize=12)plt.ylabel(票房,fontsize=15)plt.yticks(fontsize=12)plt.legend(fontsize=15)fig.savefig(電影票房:環球影業(Universal) VS 派拉蒙影業(Paramount),transparent=False,bbox_inches=tight)plt.savefig(電影票房:環球影業(Universal) VS 派拉蒙影業(Paramount)2,transparent=True,bbox_inches=tight)plt.show()productionDF2.describe()

原創VS改編電影

original_format.loc[7,plot_keywords]source_df=pd.DataFrame()source_df[plot_keywords]=original_format[plot_keywords].str.split(|)source_df[plot_keywords][7]# 千萬注意:是based,我寫成了base,導致耽誤了一個多小時source_df[conut]=original_format[plot_keywords].str.contains(based on novel).apply(lambda x: not original if x else original)original_count2=source_df[conut].value_counts()original_count3=pd.Series(original_count2)fig,ax=plt.subplots(figsize=(6,6))original_count3.plot(ax=ax,kind="pie",label=,shadow=False,autopct="%1.1f%%",startangle=10,fontsize=15)plt.title(電影數量: 原創(original) VS 改編(not original),fontsize=22)fig.savefig(電影數量: 原創(original) VS 改編(not original),transparent=False,bbox_inches=tight)plt.show()

預算和收入的關係

moneyDF=pd.DataFrame()moneyDF=pd.concat([original_format[budget],original_format[gross]],axis=1)moneyDF.head()fig,ax=plt.subplots(figsize=(9,6))plt.scatter(x=moneyDF[budget],y=moneyDF[gross])plt.title(電影預算與票房的關係圖,fontsize=22)plt.xlabel(電影預算,fontsize=15)plt.ylabel(電影票房,fontsize=15)plt.xticks(fontsize=12)plt.yticks(fontsize=12)fig.savefig(電影預算與票房的關係圖,bbox_inches=tight)plt.show()

電影時長

moviestimeDF=pd.DataFrame()moviestimeDF[short]=original_format[duration].map(lambda x: 1 if x<=60 else 0)moviestimeDF[middle]=original_format[duration].map(lambda x: 1 if 60<x<=150 else 0)moviestimeDF[long]=original_format[duration].map(lambda x: 1 if x>150 else 0)shortmovie=moviestimeDF[short].sum()middlemovie=moviestimeDF[middle].sum()longmovie=moviestimeDF[long].sum()moviestimeSeries={short:shortmovie, middle:middlemovie, long:longmovie}moviestimeSeries=pd.Series(moviestimeSeries)fig,ax=plt.subplots(figsize=(6,6))moviestimeSeries.plot(kind=pie,label=,fontsize=15,autopct="%1.1f%%")plt.title(電影時長分布,fontsize=22)fig.savefig(電影時長分布,bbox_inches=tight)plt.show()


十一,小結

  • 學習一定要快。這個項目做了接近2個星期,這中間我換了一個城市,各種雜事。想起來自己之前也沒有一鼓作氣,導致回鍋了好多次,各位朋友一定要以我為戒哈。
  • 學會搜索非常的重要。這個項目我是參照的一位朋友,並上kaggle上看了其他大神的思路,這中間出了很多問題,還是要會自己描述問題並去搜索引擎找答案。
  • 思路比敲代碼更重要。數據分析首先要會提問題,就是說要知道自己要什麼,包括用哪些指標來描述問題,用那種圖表來展示,然後再去想怎麼實現這些目標。那麼這個過程中,「提出問題」就顯得尤為重要,好問題的提出就相當於解決了一半的問題,先理順思路再下手將事半功倍。

以上就是關於該項目的全部,感謝觀看,人氣稀薄????,急需關愛????。如果您竟然看到了這裡還沒走開,請幫忙多多點贊、收藏哈,謝謝啦朋友們~~


推薦閱讀:

什麼是Python Descriptors
Python從零開始系列連載(32)——Python文件操作(下)
用Python開始的數據分析——分組groupby(1)
NumPy基礎:多維數組
Udacity工程師送你一份新年書單,快接住!

TAG:Python入門 | Python | 數據分析 |