爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据

爱数课:idatacourse.cn


类型:社会科学




简介:奥斯卡金像奖(Oscars),又名美国电影艺术与科学学院奖(Academy Awards,中文简称学院奖),是由美国电影艺术与科学学院主办的电影类奖项,创办于1929年。该奖项是美国历史最为悠久、最具权威性和专业性的电影类奖项,也是全世界最具影响力的电影类奖项。奥斯卡金像奖是美国电影界的最高奖项,与艾美奖(电视类奖项)、格莱美奖(音乐类奖项)、托尼奖(戏剧类奖项)并称为美国演艺界四大奖(EGOT)。每年的奥斯卡颁布都会引起全世界的瞩目。我们获得了1-88届奥斯卡奖项历史获奖的信息,并在本案例中对其进行清洗工作。




数据:

./dataset/oscar_data.csv

目录

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_02

1 数据介绍和导入

本案例使用到的数据集由第1-88届奥斯卡金像奖的各个奖项获奖者,提名者和相应的信息组成。我们将观察数据集的特征,并对它进行清洗。数据字段的具体信息展示如下:

列名

含义说明

数据类型

Year

年份。

object

Ceremony

届数。

int64

Award

奖项。

object

Winner

是否获奖。

float64

Name

被提名/获奖的对象。

object

Film

所对应的电影。

object

首先我们导入到整个流程中需要用到的库,主要为NumPy,Pandas,以及Matplotlib,Seaborn等可视化库。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")

我们使用read_csv函数导入数据,并储存在df中。

df = pd.read_csv('./dataset/oscar_data.csv')

2 数据探索性分析

2.1 数据基本信息展示

首先我们使用head查看数据的前五行,检查导入的数据。

df.head(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_03

a = df.head(10)
a.fillna('0')

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_04

a.groupby(['Award','Winner']).count()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_05

可以看到,数据已经正常导入。接下来我们使用shape看一下数据的大小。

df.shape

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_06

数据一共有9964行,6列,行数是比较大的。之后,我们使用info观察数据格式等基本信息。

df.info()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_07

首先我们注意到,Winner列和Film列似乎都存在缺失值,有待进一步观察。注意观察数据格式,我们发现Year的数据格式为Object,按照一般的认知,Year作为有序离散特征,类似于Ceremony表示为int64格式比较正常。此外Winner列的格式也有待考证。我们通过isnullsum输出每一列的空值数量。

df.isnull().sum()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_08

接下来我们通过describe来查看数据字段的统计信息。值得注意的是数据格式可能存在一定问题。

df.describe(include = 'all')

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_09

Year的数据格式为Object使得我们无法直接得到它的最大值和最小值等信息,无法判断是否具有异常值,我们可以稍后进行处理。可以看到众数为1942,说明1942年提名了最多的奖项。Ceremony最小值为1,最大值为88,对应Year中共取得88个Unique,代表本数据集或许正确包含了88届奥斯卡的数据。奖项Award共有114个不同的值,有待进一步考证。Winner的最小值最大值均为一,结合该列存在大量的缺失值这一事实,可以判断Winner一列中,未获奖的提名者直接标记为空值。NameFilmUnique为总数量的60%左右,说明重复获得多个奖项提名或者重复出现多次的电影还是比较多的,具体情况有待进一步考证。

2.2 数据缺失值处理

我们目前比较紧急的任务为:对缺失值进行处理和对数据类型进行转化。注意到存在一列同时需要进行这两项操作:Winner。存在缺失值的数据是无法被转化为int类型的,所以我们应首先对数据进行缺失值处理。

以下我们将处理缺失值。首先我们对Winner列进行处理。上文中已经分析过,缺失值均应代表未获奖的提名者,我们使用fillna将缺失值均标记为0即可。填充完成后,我们用value_counts查看结果。

df['Winner'].fillna(0,inplace=True)
df['Winner'].value_counts()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_10

接下来我们对Films列的缺失值进行填补。这一行的填补我们可能需要参考实际意义。我们首先调用这些缺失值所在行,随机展示10行,看看为什么这些缺失值没有信息。

df[df['Film'].isnull()].sample(10)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_11

我们似乎发现这些行的Award列都比较特殊,那么我们对这些Film为空值的行的Award列进行value_counts操作,观察规律。

df.loc[df['Film'].isnull()]['Award'].value_counts()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_12

果然这些奖项的分布比较集中,缺失值很可能并不是随机排列或者由于采集不当造成的。我们不妨将整体数据中这些Award字段的value_counts也展示出来。

PossibleNaNlist = ['Honorary Award','Irving G. Thalberg Memorial Award','Jean Hersholt Humanitarian Award','Special Award','Gordon E. Sawyer Award','John A. Bonner Medal of Commendation'
,'Medal of Commendation','Assistant Director','Award of Commendation','Sound Recording','Honorary Foreign Language Film Award','Engineering Effects','Special Foreign Language Film Award'
,'Writing (Title Writing)','Art Direction (Black and White)']
df.loc[df['Award'].isin(PossibleNaNlist)]['Award'].value_counts()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_13

我们可以将这两个series型数据使用to_frame方法转化为Dataframe型数据,并使用merge将两dataframe连接,做一个对比。

a = df.loc[df['Film'].isnull()]['Award'].value_counts().to_frame()
b = df.loc[df['Award'].isin(PossibleNaNlist)]['Award'].value_counts().to_frame()

转化完的数据没有自带索引,我们使用reset_index方法并将drop参数设置为False为其加上索引,再分别对ab的列进行重命名以区分不同的数据。最后我们用merge连接图表,对比信息。

a = a.reset_index(drop=False)
b = b.reset_index(drop=False)
a.columns = list(['AwardName','Award-NaN'])
b.columns = list(['AwardName','Award-All'])
pd.merge(b,a)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_14

我们逐一对比分析,Irving G. Thalberg Memorial Award等八个奖项全部的Film值标记为空,经过简单的了解,这些奖项都是荣誉奖项或者特殊奖项,一般是颁布给有特殊贡献的人,或者是某几届奥斯卡单独设立的。注意到Honorary Award,Special Award,Assistant Director的缺失值也较多。我们了解到Honorary Award,Special Award的奖项情况和上面类似,但这几个奖项颁发给人时,也可能具有相应的电影信息。对于以上不强调相应电影的荣誉授奖情况,我们标记缺失值为#Honorary

FillNaNlist = ['Honorary Award','Irving G. Thalberg Memorial Award','Jean Hersholt Humanitarian Award','Special Award','Gordon E. Sawyer Award','John A. Bonner Medal of Commendation'
,'Medal of Commendation','Award of Commendation','Honorary Foreign Language Film Award','Special Foreign Language Film Award']
df.loc[df['Award'].isin(FillNaNlist),'Film'] = df.loc[df['Award'].isin(FillNaNlist),'Film'].fillna('#Honorary')

关注Assistant Director奖项,该奖项曾经集中的在某几届奥斯卡被提出,但由于不同届数对于助理导演电影的标注形式不一致,导致了缺失值。对于总数较少但有缺失值的Engineering EffectsWriting (Title Writing)奖项,我们发现这是第一届奥斯卡设立的,未列出未获奖的提名者所对应的电影。对于总数较大但有少量缺失值的Sound RecordingArt Direction (Black and White),我们怀疑存在异常值的可能性。经检查,Sound Recording缺失值也是由于早期奥斯卡标准不统一所致。对于以上由于部分届数奥斯卡评价/记录标准不整齐的情况,我们将缺失值标记为#Missing

FillNaNlist = ['Assistant Director','Engineering Effects','Writing (Title Writing)','Sound Recording']
df.loc[df['Award'].isin(FillNaNlist),'Film'] = df.loc[df['Award'].isin(FillNaNlist),'Film'].fillna('#Missing')

最后我们检查Art Direction (Black and White)奖项仅有的两个缺失值,发现奥斯卡官网显示这两个奖项显示为"nomimation withdrawn",也即荣誉已经被取消。因此我们需要将这两行去除。首先定位这两行的行索引。

df.loc[(df['Award'] == 'Art Direction (Black and White)') & (df['Film'].isnull())]

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_15

使用Drop将这两行数据删除,reset_index重置行索引,shape检查删除后的数据大小。

df.drop([1115,1666],inplace=True)
df.reset_index(drop = True)
df.shape

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_16

可以看到数据减少了两行,至此我们的缺失值填补过程全部完成,最终我们再使用is_nullsum确认数据集中已不存在缺失值。

df.isnull().sum()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_17

2.3 数据类型转化

完成缺失值处理之后,我们希望对数据类型进行转化。Year的数据格式为Object,我们希望将其转化为int64。首先通过values_counts查看其中唯一值的情况。

df['Year'].value_counts()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_18

我们发现,1934年以后的Year格式都正常,但1934年以前的六届,Year的格式为YearPrevious/YearPresent,无法储存为int64格式。因此我们需要对这六种值进行处理。经过查询,对应的前六届奥斯卡的举办年数均为YearPresent,因此去除前一个数据是正确的,并未改变真实数据。故我们首先将这六个数据进处理。我们引入正则化库re,定义一个get_years函数,用正则表达式进行替换。

import re 
def get_years(x):
return int(re.sub(r'(\d+)/(\d+)',r'\2',x))

我们将get_years函数用于Year列,利用map进行遍历,对每一行都应用get_years函数。

df['Year'] = df['Year'].map(get_years)

Year列再使用astype进行数据格式转换,完成对Year列的处理。

df['Year'] = df['Year'].astype(int)
df.info()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_19

我们再将Winner列也转换为int类型。

df['Winner'] = df['Winner'].astype(int)
df.info()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_20

如此一来我们就完成了对数据格式的转化。

2.4 获奖对象判断与分组

在处理完数据格式问题后,我们重点关注数据信息的问题。我们随机选取20行数据,检查Award,name,film列的数据信息。

df.sample(20)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_21

在前面给出的表格中,我们仔细观察namefilm列,不难发现name列并非单指人名,许多颁给电影的奖项中,name列为电影的名称,而这些列对应的Film列则显示的是相应的制作人/导演等电影相关信息。如此展示信息显然是不准确的。对此,我们希望能够区分获奖的是人还是电影。这一点基本可以通过Award的类型完成判断,大部分Award都是专门为人或者电影准备的。

为了完成这一过程,我们首先用value_counts方法取出所有Award的唯一值,使用to_frame将其转化为DataFrame形式的数据保存。

TypeTable = df['Award'].value_counts().to_frame()

我们为其更换索引,添加列名,并手动为其添加获奖对象的标记。

TypeTable = TypeTable.reset_index(drop=False)
TypeTable.columns = list(['Award','WinnerType'])

我们逐一定位Award为每个选项时的情况,观察对象名称name是否统一为人/电影。发现问题后,及时进行处理。例如,我们对AwardDirecting的情况查询。

df.loc[(df['Award'] == 'Directing')]

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_22

我们发现前三届奥斯卡,Directing获奖对象为人,之后则相反。我们不妨将前三届奥斯卡的Name列和Film列数据互换,并检验完成交换后的效果。

fastloc = df[((df['Ceremony']==2) ^ (df['Ceremony']==3))& (df['Award']=='Directing')] #定位要互换的数据位置
df.loc[[56,57,58,59,60,61,95,96,97,98,99],['Name']],df.loc[[56,57,58,59,60,61,95,96,97,98,99],['Film']] = fastloc['Film'].values , fastloc['Name'].values
# 互换数据
df[((df['Ceremony']==2) ^ (df['Ceremony']==3))& (df['Award']=='Directing')] #检验互换后的效果

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_23

完成交换后,再将DirectingTypeTable中的获奖对象列WinnerType标记为电影。在之后的替换中,我们也重点关注前三届奥斯卡的情况。

TypeTable.loc[[0],['WinnerType']] = 'Movie'

下面我们逐一完善TypeTable。对每一个奖项首先如上调取其信息,对前后获奖对象类型不一的异常进行处理。Art Direction​​ ​Cinematography``Sound Recording``Writing``Assistant Director均出现了前后获奖对象类型不一的情况,我们均按照最后一次颁发该奖项时的获奖对象类型为准,将之前不符合的类型的NameFilm列进行互换。

fastloc = df[((df['Ceremony']==1) ^ (df['Ceremony']==2))& (df['Award']=='Art Direction')] #定位要互换的数据位置
df.loc[[5,6,7,46,47,48,49,50],['Name']],df.loc[[5,6,7,46,47,48,49,50],['Film']] = fastloc['Film'].values , fastloc['Name'].values
# 互换数据

fastloc = df[((df['Ceremony']==1) ^ (df['Ceremony']==2))& (df['Award']=='Cinematography')]
df.loc[[8,9,10,51,52,53,54,55],['Name']],df.loc[[8,9,10,51,52,53,54,55],['Film']] = fastloc['Film'].values , fastloc['Name'].values

fastloc = df[((df['Ceremony']==4) ^ (df['Ceremony']==5))& (df['Award']=='Sound Recording')]
df.loc[[145,146,147,148,192,193,194,195],['Name']],df.loc[[145,146,147,148,192,193,194,195],['Film']] = fastloc['Film'].values , fastloc['Name'].values

fastloc = df[(df['Ceremony']==1)& (df['Award']=='Writing (Original Story)')]
df.iloc[28:30,4],df.iloc[28:30,5] = fastloc['Film'].values , fastloc['Name'].values

fastloc = df[(df['Ceremony']==6)& (df['Award']=='Assistant Director')]
df.iloc[213:231,4],df.iloc[213:231,5] = fastloc['Film'].values , fastloc['Name'].values

fastloc = df[(df['Ceremony']==2)& (df['Award']=='Writing')]
df.iloc[67:73,4] , df.iloc[67:73,5] = fastloc['Film'].values , fastloc['Name'].values

我们将按Award得到的分类标签输入到LabelList并替换到TypeTable中,分为三个类别:Film , IndividualMusic 。Film代表获奖对象为电影,Individual代表获奖对象为个体(包含部分组织),Music代表获奖对象为音乐作品。暂时标记Film1Individual2,Music3。输入后使用replace进行统一替换。

完成后,展示TypeTable前五行,验证效果。

df['Award'].value_counts().to_frame().reset_index()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_24

LabelList = np.array([[1],[1],[2],[2],[1],[1],[1],[1],[1],[1],[1],[2],[2],[1],[1],[3],[2],[2],[3],[1],
[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[2],[1],[1],[1],[1],[1],[1],[1],
[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[2],[1],[1],[1],[2],[1],[2],[1],
[1],[1],[1],[1],[3],[1],[2],[1],[1],[1],[1],[1],[1],[2],[2],[1],[1],[1],[1],[2],
[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1],[2],[1],[1],[1],[2],[1],[2],[1],
[1],[1],[1],[1],[2],[2],[2],[2],[1],[2],[2],[1],[1],[1]], dtype=object)
# 每行包括20条数据,由于随机排序,需要关注重复值。
TypeTable.loc[:,['WinnerType']] = LabelList
TypeTable.replace([1,2,3],['Film','Individual','Music'],inplace=True)
TypeTable.head()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_25

使用merge函数连接TypeTabledf,为每一部电影增加WinnerType属性。完成后查看前五行检查效果。

df = pd.merge(df,TypeTable,left_on="Award",right_on="Award",how="left")
df.head(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_26

我们使用groupby对所有获奖对象按照WinnerType进行分组,并用Get_group方法把三张分别对应不同WinnerType的DataFrame格式的表取出,分别命名为FilmTable,IndividualTable,MusicTable

完成后,对MusicTable随机取五行,查看分组是否成功。

groups = df.groupby('WinnerType')
FilmTable,IndividualTable,MusicTable = groups.get_group('Film'),groups.get_group('Individual'),groups.get_group('Music')
MusicTable.sample(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_27

我们得到了三张列数相同,区分获奖对象类别的表格,但这些表格的列标签显然只适用于Individual个人获奖的情况。针对FilmTableMusicTable我们需要修改列标签以切合实际意义。

完成后,对MusicTable随机取五行,查看分组是否成功。

FilmTable.rename(columns = {'Film':'Contributors(Film)'},inplace = True)
MusicTable.rename(columns ={'Film':'Creators(Music)'} ,inplace = True)
MusicTable.sample(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_28

我们也可以将三张表聚合显示,查看分类后显示的效果。

 pd.merge(pd.merge(FilmTable,MusicTable,how = 'outer'),IndividualTable,how = 'outer').sample(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_29

2.5 获奖音乐字段分离

至此,我们对数据本身的清洗工作已经基本完成。但我们注意到,MusicTable中获奖对象Name里,其实包含了音乐本身和其所在的电影两个信息。考虑到如果最终聚合显示三表,音乐对象也可以具有一个空的Film标签,因此我们可以在MusicTable中也增添一个Film列,并尝试将电影信息添加到Film中。

添加Film列后,随机取五行查看效果。

MusicTable ['Film'] = '#Missing'
MusicTable.sample(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_30

我们接下来考虑分离Name中的字段信息。注意到音乐名和电影名都是通过from分割的,我们使用pandas自带的str_split方法,设置分割的patternfrom,设置expand参数为True来默认添加新列,设置n参数为1来限制最大切分次数,防止电影名里本身包含from字段导致多次分割。

我们得到对Name处理后的临时分割表Split,展示前五行检查信息。

Split = MusicTable['Name'].str.split('from',expand = True, n=1)
Split.rename(columns = {0:'Name',1:'Film'},inplace = True)
Split.head(5)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_31

我们将Split中的数据替换到MusicTable中,完成处理并检查。

MusicTable['Name'],MusicTable['Film'] = Split['Name'],Split['Film']
MusicTable.head(10)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_32

再次连接得到总表df_new

df_new = pd.merge(pd.merge(FilmTable,MusicTable,how = 'outer'),IndividualTable,how = 'outer')

2.6 数据清洗结果

至此,我们对数据的清洗工作完成。我们共得到一张清洗分类后的总表df_new和按获奖对象分类得到的三张分表FilmTableIndividualTableMusicTable。我们展示这些表。

df_new.sample(10)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_33

FilmTable.sample(10)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_34

IndividualTable.sample(10)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_35

MusicTable.sample(10)

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_36

3 数据可视化

我们对清洗后的数据做一些有意思的可视化,看看我们能够得到一些什么样的结论。

3.1 奥斯卡每届颁奖数目分布图

我们首先使用seaborn库绘制一个奥斯卡每届的颁奖数目分布图。首先使用seaborn的set方法设定图片大小,字体等参数,用plt中的title,xlabel等设置标题,标签等,再用seaborn的countplot绘制频数柱状图。使用rcParams可以调整图片dpi使其更加清晰。

我们基于Ceremony列绘制频数图,取hue参数为Winner同时绘制是否获奖的两种记录。

sns.set(rc={'figure.figsize':(20,8),"font.size":15,'font.sans-serif':['SimHei'],"axes.titlesize":15,"axes.labelsize":15})
plt.title('奥斯卡每届颁奖数目分布图')
sns.set_color_codes("pastel")
ax = sns.countplot(x=df_new['Ceremony'],hue = 'Winner',data = df_new,palette="Blues_d")
plt.xlabel("届数")
plt.rcParams['figure.dpi'] = 1200
plt.show()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_37

观察上图,我们能发现结论。

奥斯卡奖早期设置的奖项比较少,提名数也相应不多。之后开始上升。

奥斯卡奖每届最终的获奖数在经历前期的上升之后,长期维持比较稳定的水平,但提名数则经历了10-20届的迅速增长,而后又急速下降的过程。40-80届的奥斯卡提名数和获奖数都长期维持较稳定的水平,近期提名数又有所回升。

3.2 奥斯卡不同类别获奖分布

我们还感兴趣,在所有的提名中,获得奖项的比例有多大?我们还知道奥斯卡奖获奖对象有电影,个人,音乐三种类型。我们可以通过绘制圆环图,展示获奖提名总数的比例,以及获奖或提名情况中,不同类型的比例。

我们根据不同情况,把df_new中对应列的频数存在对应的参数中,并通过plt.pie结合对wedgeprops的参数设置,绘制圆环图。

fig = plt.subplots(2,2,figsize=(16,12))
plt.rcParams['figure.dpi'] = 100
sns.set(rc={'figure.figsize':(10,5),"font.size":15,'font.sans-serif':['SimHei'],"axes.titlesize":15,"axes.labelsize":15})

plt.subplot(2,2,1)
Ratio_count = df_new['Winner'].value_counts()
plt.pie(Ratio_count , radius = 1 , autopct='%1.1f%%',wedgeprops = dict(width = 0.5 , edgecolor = 'w'), labels=['提名','获奖'])
plt.ylabel("")
plt.title('获奖/提名分布图')

plt.subplot(2,2,2)
Winner_count = df_new['WinnerType'].value_counts()
plt.pie(Winner_count , radius = 1 , autopct='%1.1f%%',wedgeprops = dict(width = 0.5 , edgecolor = 'w'), labels=['电影','个人','音乐'])
plt.ylabel("")
plt.title('对象类型分布图')

plt.subplot(2,2,3)
Winpart_count = df_new.loc[df['Winner']==1]['WinnerType'].value_counts()
plt.pie(Winpart_count , radius = 1 , autopct='%1.1f%%',wedgeprops = dict(width = 0.5 , edgecolor = 'w'), labels=['电影','个人','音乐'])
plt.title('获奖者对象类型分布图')
plt.ylabel("")

plt.subplot(2,2,4)
Noticepart_count = df_new.loc[df['Winner']==0]['WinnerType'].value_counts()
plt.pie(Noticepart_count , radius = 1 , autopct='%1.1f%%',wedgeprops = dict(width = 0.5 , edgecolor = 'w'), labels=['电影','个人','音乐'])
plt.title('提名者对象类型分布图')
plt.ylabel("")
plt.show()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_38

观察上图,我们发现提名和获奖者的总比例在3:1左右。对于获奖对象,四分之三的获奖对象都为电影,五分之一的获奖对象为个人,剩余的少量奖项的获奖对象为音乐。

3.3 不同获奖对象奖项在每届的分布

我们还想要了解,在每届的颁奖中,不同获奖对象的奖项类型占比有多少,变化趋势又是怎样的?我们分别把3.1中的图片限定获奖对象类型,观察趋势。

sns.set(rc={'figure.figsize':(20,8),"font.size":15,'font.sans-serif':['SimHei'],"axes.titlesize":15,"axes.labelsize":15})
plt.title('获奖对象为电影的奖项每届颁奖数目分布图')
sns.set_color_codes("pastel")
ax = sns.countplot(x=df_new.loc[df_new['WinnerType']=='Film']['Ceremony'],hue = 'Winner',data = df_new,palette="Blues_d")
plt.xlabel("届数")
plt.rcParams['figure.dpi'] = 1200
plt.show()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_39

我们发现电影的变化趋势和总体奖项的变化趋势大致相同,这也符合电影获奖占总体奖项数量中四分之三的事实。电影的数量变化趋势基本上代表总体的数量变化趋势。

sns.set(rc={'figure.figsize':(20,8),"font.size":15,'font.sans-serif':['SimHei'],"axes.titlesize":15,"axes.labelsize":15})
plt.title('获奖对象为个人的奖项每届颁奖数目分布图')
sns.set_color_codes("pastel")
ax = sns.countplot(x=df_new.loc[df_new['WinnerType']=='Individual']['Ceremony'],hue = 'Winner',data = df_new,palette="Blues_d")
plt.xlabel("届数")
plt.rcParams['figure.dpi'] = 1200
plt.show()

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_40

观察获奖对象为音乐的奖项,我们发现每届都只有一个奖单独颁发给音乐,在最初六届中并不单设音乐奖项,但在开始设立之后提名数经历了18届之前的飙升,之后则趋于稳定。

3.4 绘制奥斯卡奖词云图

我们还没有挖掘出NameFilm列本身的信息价值,我们针对这些信息用pyecharts中的WordCloudl来根据这些信息绘制词云图。

from pyecharts import options as opts
from pyecharts.charts import WordCloud
from pyecharts.globals import SymbolType

我们决定为总体的获奖对象和不同类型的获奖对象分别绘制四个词云图,为避免重复代码我们定义一个函数get_word_cloud,传入两个参数,dataset指要使用的DataFrame,columm指要统计的列。

def get_word_cloud(dataset,column):
wordcloud = WordCloud()
wordcloud.add("", data, word_size_range=[10, 100], shape=SymbolType.DIAMOND)
wordcloud.render_notebook()
return wordcloud.render_notebook()

以下为总体的词云图。可以看到Meryl Streep有着最高的出现频数。

wordcloud1 = df_new['Name'].value_counts().to_frame()
wordcloud1.reset_index(drop = False ,inplace = True)
wordcloud1.drop([0],inplace=True)
name = wordcloud1['index'].to_list()
value = wordcloud1['Name'].to_list()
data = []
for i in range(len(name)):
data.append((name[i],value[i]))
get_word_cloud(df_new,'Name')

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_缺失值_41

以下为FilmTable中的词云图,我们能够看到许多熟悉的电影,Titanic泰坦尼克号作为电影13次获奖。

wordcloud1 = FilmTable['Name'].value_counts().to_frame()
wordcloud1.reset_index(drop = False ,inplace = True)
wordcloud1.drop([0],inplace=True)
name = wordcloud1['index'].to_list()
value = wordcloud1['Name'].to_list()
data = []
for i in range(len(name)):
data.append((name[i],value[i]))
get_word_cloud(FilmTable,'Name')

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_42

以下为IndividualTable中的词云图,演员Meryl Streep再次霸榜。

wordcloud1 = IndividualTable['Name'].value_counts().to_frame()
wordcloud1.reset_index(drop = False ,inplace = True)
name = wordcloud1['index'].to_list()
value = wordcloud1['Name'].to_list()
data = []
for i in range(len(name)):
data.append((name[i],value[i]))
get_word_cloud(IndividualTable,'Name')

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据_43

以下为MusicTable中的词云图,可以看到音乐基本上都只获奖了一次,没有重复获奖情况。

wordcloud1 = MusicTable['Name'].value_counts().to_frame()
wordcloud1.reset_index(drop = False ,inplace = True)
wordcloud1.drop([0],inplace=True)
name = wordcloud1['index'].to_list()
value = wordcloud1['Name'].to_list()
data = []
for i in range(len(name)):
data.append((name[i],value[i]))
get_word_cloud(MusicTable,'Name')

爱数课实验 | 利用Python对奥斯卡金像奖得主数据进行数据清洗_数据格式_44

4 总结

我们对奥斯卡1-88届的获奖数据进行了数据清洗和可视化,并归类出了三种表格和总表格以供查询。

数据清洗是较为繁杂,细致的工作,我们为规范化数据做出了努力,用到了许多方法,但仍有不小的改进空间。例如不同获奖对象对应的说明列,都包含着大量的信息,但格式不一,种类繁多,如果需要将这些信息总结出来,仍然需要进行进一步的清洗。但大多数的方法本清洗案例都有所提及,根据类似的,正确的思路去进行清洗,获得需要的数据并非难事。

爱数课(iDataCourse)是一个面向院校的大数据和人工智能课程和资源平台。平台提供权威的课程资源、数据资源、案例实验资源,助力院校大数据和人工智能专业建设,课程建设和师资能力建设。