统计与计数
本课程讨论使用Pandas进行主数据处理和数据分析技术。
在上一章中,我们在学习如何从 CSV 文件读取数据时使用了 Pandas。现在,我们将更深入地探讨 Pandas 以处理数据。
为此,我们将继续使用之前学习读取 CSV 数据时所用的同一数据集。以下是前五行数据,帮助你回忆一下:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5print(train_df.head())
对数据进行统计分析
一个很好的起点是使用一些 Pandas 函数查看数据,以便更好地理解可能存在的问题。describe 可以为连续变量提供计数和一些统计信息。
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5print(train_df.describe())
对于所有数值型数据,我们现在可以得到 均值、标准差、最小值、最大值 以及多个不同 分位数。
注意:需要记住的是,均值受异常值的影响比中位数更大。此外,你始终可以通过对标准差平方来得到方差。
你可能会注意到某些列缺失了。describe() 函数只返回了包含数值数据的列。
查看数据类型
要了解数据的类型,可以使用 info() 函数:
1import pandas as pd 2 3# 如何读取数据,参考读取 CSV 的课程说明 4names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 5 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 6train_df = pd.read_csv("adult.data", header=None, names=names) 7 8print(train_df.info()) # 在数据框上使用 info() 函数
在我们的数据框中,有两种数据类型:object 和 int64。你可以将 object 理解为字符串类型,而 int64 是整数类型。
转换数据类型
如果某列的数据类型不正确,可以轻松使用 Pandas 提供的转换函数将其转换为其他类型:
to_numeric()to_datetime()to_string()
例如:
1df['numeric_column'] = pd.to_numeric(df['string_column'])
此外,info 命令还会提供每列非空值的数量以及数据框的内存使用情况。
查找唯一值
另一个有用的步骤是查看各列的唯一值。以下是以 relationship 列为例:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5print(train_df['relationship'].unique())
现在我们可以看到上述所有唯一值,并检查 relationship 列中各类别的数量:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5print(train_df['relationship'].value_counts())
这显示了 relationship 所有唯一值及其对应的数量。可以看出,husband 类别数量较多,而 Other-relative 数量较少。
数据分组
我们还可以使用 groupby() 函数按特定分组进行此类统计。该函数接受一个列名列表,用于指定按哪些列进行分组。然后对每个组分别执行指定的计算,并按组返回结果。以下是一个示例:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5 6# 按 relationship 分组,然后对 label 进行值计数并归一化 7print(train_df.groupby('relationship')['label'].value_counts(normalize=True))
我们上面的操作是按 relationship 变量分组,然后对 label 变量进行值计数。在这些数据中,label 表示收入是否超过 50k。可以看到,约 55% 的 husband 收入超过 50k。我们得到的是百分比,因为使用了 normalize=True 参数。
你可以在分组上执行多种类型的计算。例如,这里我们可以查看不同 workclass 的平均每周工作小时数:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5print(train_df.groupby(['workclass'])['hoursperweek'].mean())
从以上结果可以看出,联邦政府工作人员的平均工作时间高于地方工作人员。而 Never-worked 的平均工作时间约为 28 小时。
查找相关性
另一个有用的统计指标是相关性。如果你需要复习相关性的概念,请参考 维基百科。你可以使用 corr 函数计算数据框中所有变量之间的两两相关性。
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5 6# 计算相关性 7print(train_df.corr())
我们可以快速看出,“每周工作小时数”与“教育程度”之间的相关性较高,但整体相关性并不强。不过需要注意的是,由于我们的 label 是对象类型,因此未包含在内。了解变量与标签之间的相关性会很有帮助,因此我们来处理一下:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5 6# 将字符串标签转换为数值:>=50k 为 1,否则为 0 7train_df['label_int'] = train_df.label.apply(lambda x: ">" in x) 8print(train_df.corr())
可以看出,label_int 与 educationnum 之间存在一定的相关性。但需要注意的是,我们的 label 是分类变量,因此相关性并不完全适用,使用 groupby 的频率统计可能是更好的方法。
注意:分类变量是指具有类别但无内在顺序的变量。例如性别。
同时也要注意,这些只是单变量相关性(仅涉及一个变量),并未考虑多变量之间的相互影响。你也可以使用 scipy 包来计算相关性,它还提供了 p 值,这一点在“Scipy 外部库”课程中已有讨论。
生成分位数
最后,Pandas 的 describe 函数已经提供了一些分位数,但你可以轻松添加更多:
1import pandas as pd 2names = ['age', 'workclass', 'fnlwgt', 'education', 'educationnum', 'maritalstatus', 'occupation', 'relationship', 'race', 3 'sex', 'capitalgain', 'capitalloss', 'hoursperweek', 'nativecountry', 'label'] 4train_df = pd.read_csv("adult.data", header=None, names=names) 5 6# 使用 describe 函数计算指定的分位数 7print(train_df.describe(percentiles=[.01,.05,.95,.99]))
分位数是指低于该值的数据占总数据的比例。
我们通过 percentiles 参数传入希望计算的分位数值。
在下一课中,我们将探讨重塑数据框的各种方法。