Skip to content

Files

Latest commit

9ac7d86 · Jul 13, 2020

History

History
989 lines (705 loc) · 41 KB

File metadata and controls

989 lines (705 loc) · 41 KB

二、数据帧基本操作

在本章中,我们将介绍以下主题:

  • 选择数据帧的多个列
  • 用方法选择列
  • 明智地排序列名称
  • 处理整个数据帧
  • 将数据帧方法链接在一起
  • 将运算符与数据帧一起使用
  • 比较缺失值
  • 转换数据帧操作的方向
  • 确定大学校园的多样性

介绍

本章介绍了数据帧的许多基本操作。 许多秘籍将与第 1 章,“Pandas 基础”中的内容类似,这些内容主要涵盖序列操作。

选择数据帧的多个列

选择单个列是通过将所需的列名作为字符串传递给数据帧的索引运算符来完成的。 在第 1 章,“Pandas 基础”的“选择序列”秘籍中对此进行了介绍。 通常需要关注当前工作数据集的一个子集,这是通过选择多个列来完成的。

准备

在此秘籍中,将从movie数据集中选择所有actordirector列。

操作步骤

  1. 读取电影数据集,并将所需列的列表传递给索引运算符:
>>> movie_actor_director = movie[['actor_1_name', 'actor_2_name',
                                  'actor_3_name', 'director_name']]
>>> movie_actor_director.head()

  1. 在某些情况下,需要选择数据帧的一列。 这是通过将单个元素列表传递给索引运算符来完成的:
>>> movie[['director_name']].head()

工作原理

数据帧的索引运算符非常灵活,可以接受许多不同的对象。 如果传递了字符串,它将返回一维序列。 如果将列表传递给索引运算符,它将以指定顺序返回列表中所有列的数据帧。

步骤 2 显示了如何选择单个列作为数据帧而不是序列。 最常见的是,使用字符串选择单个列,从而得到一个序列。 当数据帧是所需的输出时,只需将列名放在一个单元素列表中。

更多

在索引运算符内部传递长列表可能会导致可读性问题。 为了解决这个问题,您可以先将所有列名保存到列表变量中。 下面的代码获得与步骤 1 相同的结果:

>>> cols = ['actor_1_name', 'actor_2_name',
            'actor_3_name', 'director_name']
>>> movie_actor_director = movie[cols]

KeyError是处理 Pandas 的最常见例外之一。 此错误主要是由于列名或索引名的错误输入。 每当尝试不使用列表进行多列选择时,都会引发相同的错误:

>>> movie['actor_1_name', 'actor_2_name',
          'actor_3_name', 'director_name']
KeyError: ('actor_1_name', 'actor_2_name',
           'actor_3_name', 'director_name')

这是一个常见的错误,因为很容易忘记将所需的列放在列表中。 您可能想知道这里到底发生了什么。 技术上,用逗号分隔的四个字符串名称是一个元组对象。 通常,元组用开括号和闭括号括起来,但这不是必需的:

>>> tuple1 = 1, 2, 3, 'a', 'b'
>>> tuple2 = (1, 2, 3, 'a', 'b')
>>> tuple1 == tuple2
True

Pandas 正试图找到与元组('actor_1_name', 'actor_2_name', 'actor_3_name', 'director_name')完全相同的列名。 它失败并引发KeyError

用方法选择列

尽管列选择通常直接由索引运算符完成,但是有一些数据帧方法可以以替代方式方便其选择。select_dtypesfilter是执行此操作的两种有用方法。

准备

您需要熟悉所有 Pandas 数据类型以及如何访问它们。 第 1 章,“Pandas 基础”中的“了解数据类型”秘籍具有包含所有 Pandas 数据类型的表。

工作原理

  1. 读入电影数据集,并使用电影的标题标记每一行。 使用get_dtype_counts方法输出每种特定数据类型的列数:
>>> movie = pd.read_csv('data/movie.csv',
                        index_col='movie_title')
>>> movie.get_dtype_counts()
float64    13
int64       3
object     11
dtype: int64
  1. 使用select_dtypes方法仅选择整数列:
>>> movie.select_dtypes(include=['int']).head()

  1. 如果要选择所有数字列,则只需将字符串数字传递给include参数:
>>> movie.select_dtypes(include=['number']).head()

  1. 选择列的另一种方法是使用filter方法。 此方法很灵活,可以根据使用的参数搜索列名(或索引标签)。 在这里,我们使用like参数搜索包含确切字符串facebook的所有列名称:
>>> movie.filter(like='facebook').head()

  1. filter方法允许使用regex参数通过正则表达式搜索列。 在这里,我们搜索名称中某处有数字的所有列:
>>> movie.filter(regex='\d').head()

工作原理

步骤 1 列出了所有不同数据类型的频率。 或者,您可以使用dtypes属性来获取每一列的确切数据类型。select_dtypes方法在其include参数中获取数据类型的列表,并返回仅包含那些给定数据类型的列的数据帧。 列表值可以是数据类型的字符串名称,也可以是实际的 Python 对象。

filter方法仅通过检查列名而不是实际数据值来选择列。 它具有三个互斥的参数itemslikeregex,一次只能使用其中一个。like参数采用一个字符串,并尝试查找名称中某处包含该确切字符串的所有列名称。 为了获得更大的灵活性,您可以使用regex参数代替通过正则表达式选择列名称。 这个特定的正则表达式\d表示从零到九的所有数字,并且匹配其中至少包含一个数字的任何字符串。

正则表达式是代表搜索模式的字符序列,这些搜索模式用于选择文本的不同部分。 它们允许非常复杂和高度特定的模式匹配。

更多

filter方法带有另一个参数items,该参数采用一列确切的列名。 这几乎与索引运算符完全相同,只是如果其中一个字符串与列名不匹配,则不会引发KeyError。 例如,movie.filter(items=['actor_1_name', 'asdf'])运行无错误,并返回单列数据帧。

select_dtypes的一个令人困惑的方面是它同时接受字符串和 Python 对象的灵活性。 下表应阐明选择许多不同列数据类型的所有可能方法。 在 Pandas 中没有引用数据类型的标准或首选方法,因此最好同时了解两种方式:

Python 对象 字符串 注释
np.number number 选择整数和浮点数,而不考虑大小
np.float64, np.float_, float float64float_float 仅选择 64 位浮点数
np.float16, np.float32, np.float128 float16float32float128 分别选择精确的 16 位,32 位和 128 位浮点数
np.floating floating 选择所有浮点,而不管大小
np.int0, np.int64, np.int_, int int0int64int_int 仅选择 64 位整数
np.int8, np.int16, np.int32 int8int16int32 分别选择 8、16 和 32 位整数
np.integer integer 选择所有整数,而不考虑大小
np.object objectO 选择所有对象数据类型
np.datetime64 datetime64datetime 所有 64 位的日期时间
np.timedelta64 timedelta64timedelta 所有 64 位的时间增量
pd.Categorical category Pandas 特有的; NumPy 没有等效的东西

因为所有整数和浮点数默认为 64 位,所以可以通过使用字符串int,或float来选择它们,如上表所示。 如果要选择所有整数和浮点数,而不管它们的大小如何,请使用字符串number

另见

明智地排序列名称

最初将数据集导入为数据帧之后要考虑的首要任务之一是分析列的顺序。 这个基本任务经常被忽略,但是可以在分析进行中产生很大的不同。 计算机没有优先选择列顺序,计算也不受影响。 作为人类,我们自然地从左到右查看和阅读列,这直接影响我们对数据的解释。 杂物柱布置类似于壁橱中的杂物衣服布置。 在短裤顶部的衬衫和裤子旁边放西装是没有好处的。 考虑列顺序时,查找和解释信息要容易得多。

没有标准的规则集来规定应如何在数据集中组织列。 但是,优良作法是制定一组您始终遵循的准则以简化分析。 如果您与一组共享大量数据集的分析师合作,则尤其如此。

准备

以下是排序列的简单指南:

  • 将每列分为离散列或连续列
  • 在离散列和连续列中将公共列分组
  • 将最重要的列组首先放置在分类列之前,然后再放置连续列

本秘籍向您展示如何使用此指南排序各列。 有许多明智的可能排序。

操作步骤

  1. 读取电影数据集,然后扫描数据:
>>> movie = pd.read_csv('data/movie.csv')
>>> movie.head()

  1. 输出所有列名称并扫描相似的离散列和连续列:
>>> movie.columns
Index(['color', 'director_name', 'num_critic_for_reviews',
       'duration', 'director_facebook_likes',
       'actor_3_facebook_likes', 'actor_2_name',
       'actor_1_facebook_likes', 'gross', 'genres',
       'actor_1_name', 'movie_title', 'num_voted_users',
       'cast_total_facebook_likes', 'actor_3_name',
       'facenumber_in_poster', 'plot_keywords',
       'movie_imdb_link', 'num_user_for_reviews', 'language',
       'country', 'content_rating', 'budget', 'title_year',
       'actor_2_facebook_likes', 'imdb_score', 'aspect_ratio',
       'movie_facebook_likes'], dtype='object')
  1. 这些列似乎没有任何逻辑顺序。 将名称合理地组织到列表中,以便遵循上一部分的指南:
>>> disc_core = ['movie_title', 'title_year',
                 'content_rating', 'genres']
>>> disc_people = ['director_name', 'actor_1_name', 
                   'actor_2_name', 'actor_3_name']
>>> disc_other = ['color', 'country', 'language', 
                  'plot_keywords', 'movie_imdb_link']

>>> cont_fb = ['director_facebook_likes', 'actor_1_facebook_likes', 
               'actor_2_facebook_likes', 'actor_3_facebook_likes',
               'cast_total_facebook_likes', 'movie_facebook_likes']

>>> cont_finance = ['budget', 'gross']
>>> cont_num_reviews = ['num_voted_users', 'num_user_for_reviews',
                        'num_critic_for_reviews']
>>> cont_other = ['imdb_score', 'duration',
                  'aspect_ratio', 'facenumber_in_poster']
  1. 将所有列表连接在一起以获得最终的列顺序。 另外,请确保此列表包含原始文档中的所有列:
>>> new_col_order = disc_core + disc_people + \
                    disc_other + cont_fb + \
                    cont_finance + cont_num_reviews + \
                    cont_other
>>> set(movie.columns) == set(new_col_order)
True
  1. 将具有新列顺序的列表传递给数据帧的索引运算符以对列进行重新排序:
>>> movie2 = movie[new_col_order]
>>> movie2.head()

工作原理

要从数据帧中选择列的子集,请使用特定列名称的列表。 例如,movie[['movie_title', 'director_name']]仅使用movie_titledirector_name列创建一个新的数据帧。 通过名称选择列是 Pandas 数据帧的索引运算符的默认行为。

步骤 3 根据类型(离散或连续)以及它们的数据相似程度,将所有列名称整齐地组织到单独的列表中。 最重要的列(例如电影的标题)位于第一位。

步骤 4 连接所有列名称列表,并验证此新列表是否包含与原始列名称相同的值。 Python 集是无序的,并且相等语句检查一个集的每个成员是否是另一个集的成员。 手动排序此秘籍中的列容易受到人为错误的影响,因为很容易错误地忘记新列列表中的列。

步骤 5 通过将新的列顺序作为列表传递给索引运算符来完成重新排序。 现在,这个新顺序比原来的要明智得多。

更多

除了前面提到的简单建议外,还有其他排序列的准则。 Hadley Wickham 在有关整洁数据的开创性论文中建议将固定变量放在第一位,然后再放置测量变量。 由于此数据并非来自受控实验,因此可以灵活地确定哪些变量是固定的,哪些是测量的。 测量变量的良好候选者是我们希望预测的变量,例如gross,总收入或imdb_score。 例如,以这种顺序,我们可以混合离散变量和连续变量。 在该演员的名字之后直接放置 Facebook 点赞人数的列可能更有意义。 当然,由于计算部分不受列顺序的影响,因此您可以提出自己的列顺序准则。

通常,您将直接从关系数据库中提取数据。 关系数据库的一种非常常见的做法是将主键(如果存在)作为第一列,并在其后直接放置任何外键。

主键唯一地标识当前表中的行。 外键唯一地标识其他表中的行。

另见

处理整个数据帧

在第 1 章,“Pandas 基础”的“调用序列方法”秘籍中,对单列或序列数据进行操作的各种方法。 当从数据帧调用这些相同的方法时,它们会立即对每一列执行该操作。

准备

在本秘籍中,我们将对电影数据集探索各种最常见的数据帧属性和方法。

操作步骤

  1. 阅读电影数据集,并获取基本描述性属性shapesizendim,以及运行len函数:
>>> movie = pd.read_csv('data/movie.csv')
>>> movie.shape
(4916, 28)

>>> movie.size
137648

>>> movie.ndim
2

>>> len(movie)
4916
  1. 使用count方法查找每列的不丢失值的数量。 输出是一个序列,现在其旧列名称为,其索引为:
>>> movie.count()
color                     4897
director_name             4814
num_critic_for_reviews    4867
duration                  4901
                          ... 
actor_2_facebook_likes    4903
imdb_score                4916
aspect_ratio              4590
movie_facebook_likes      4916
Length: 28, dtype: int64
  1. 其他计算摘要统计信息的方法,例如minmaxmeanmedianstd都返回相似的序列,其索引中的列名称及其计算结果为值:
>>> movie.min()
num_critic_for_reviews     1.00
duration                   7.00
director_facebook_likes    0.00
actor_3_facebook_likes     0.00
                           ... 
actor_2_facebook_likes     0.00
imdb_score                 1.60
aspect_ratio               1.18
movie_facebook_likes       0.00
Length: 16, dtype: float64
  1. describe方法非常强大,可以一次计算前面步骤中的所有描述性统计数据和四分位数。 最终结果是一个数据帧,其描述性统计信息为,其索引为:
>>> movie.describe()

  1. 可以使用percentiles参数在describe方法中指定精确的分位数:
>>> movie.describe(percentiles=[.01, .3, .99])

工作原理

步骤 1 提供有关数据集大小的基本信息。shape属性返回行和列数的两个元素的元组。size属性返回数据帧中元素的总数,它只是行和列数的乘积。ndim属性返回维数,对于所有数据帧,维数均为 2。 Pandas 定义了内置的len函数以返回行数。

步骤 2 和步骤 3 中的方法将每一列汇总为一个数字。 现在,每个列名称都是序列中的索引标签,其汇总结果为相应的值。

如果仔细观察,您会发现步骤 3 的输出缺少步骤 2 的所有对象列。其原因是对象列中缺少值,而 pandas 不知道如何处理字符串值与缺失值。 它会静默删除无法为其计算最小值的所有列。

在这种情况下,静默意味着没有引发任何错误并且没有发出警告。 这有点危险,需要用户熟悉 Pandas。

数字列也缺少值,但返回了结果。 默认情况下,pandas 通过跳过数值列来处理缺失值。 通过将skipna参数设置为False可以更改此行为。 如果存在至少一个缺失值,这将导致所有这些聚合方法的 Pandas 返回NaN

describe方法可一次显示所有主要摘要,并且可以通过将 0 到 1 之间的数字列表传递给percentiles参数来扩展其摘要以包含更多分位数。 默认情况下,仅在数字列上显示信息。 有关describe方法的更多信息,请参见“开发数据分析例程”秘籍。

更多

要查看skipna参数如何影响结果,我们可以将其值设置为False,然后从前面的秘籍重新运行步骤 3。 只有没有缺失值的数字列将计算结果:

>>> movie.min(skipna=False)
num_critic_for_reviews     NaN
duration                   NaN
director_facebook_likes    NaN
actor_3_facebook_likes     NaN
                          ... 
actor_2_facebook_likes     NaN
imdb_score                 1.6
aspect_ratio               NaN
movie_facebook_likes       0.0
Length: 16, dtype: float64

将数据帧方法链接在一起

无论您相信方法链接是否是一种好的做法,在使用 Pandas 进行数据分析时都会遇到它是很普遍的。 第 1 章,“Pandas 基础”中的“将序列方法链接在一起”秘籍展示了链接序列方法一起的几个示例。 本章中的所有方法链都将从数据帧开始。 方法链接的关键之一是知道在链接的每个步骤中返回的确切对象。 在 Pandas 中,这几乎总是一个数据帧,序列或标量值。

准备

在此秘籍中,我们计算移动数据集每一列中的所有缺失值。

操作步骤

  1. 要获得缺失值的计数,必须首先调用isnull方法以将每个数据帧值更改为布尔值。 让我们在电影数据集上调用此方法:
>>> movie = pd.read_csv('data/movie.csv')
>>> movie.isnull().head()

  1. 我们将链接将True/False布尔值解释为 1/0 的sum方法。 请注意,返回了一个序列:
>>> movie.isnull().sum().head()
color                       19
director_name              102
num_critic_for_reviews      49
duration                    15
director_facebook_likes    102
dtype: int64
  1. 我们可以再走一步,取该序列的总和,然后将整个数据帧中缺失值总数的计数作为标量值返回:
>>> movie.isnull().sum().sum()
2654
  1. 略有偏差是为了确定数据帧中是否缺少任何值。 我们在此连续两次使用any方法来执行此操作:
>>> movie.isnull().any().any()
True

工作原理

isnull方法返回一个与调用数据帧相同大小的数据帧,但所有值都转换为布尔值。 请参阅以下数据类型的计数以验证这一点:

>>> movie.isnull().get_dtype_counts()
bool    28
dtype: int64

由于布尔值的数值求值为 0/1,因此可以按列对它们进行求和,如步骤 2 所示。所得的序列本身也具有sum方法,该方法可以使我们在数据帧中获得总计的缺失值。

在步骤 4 中,数据帧的any方法返回布尔值序列,指示每个列是否存在至少一个Trueany方法再次链接到该布尔结果序列上,以确定是否有任何列缺少值。 如果步骤 4 求值为True,则整个数据帧中至少存在一个缺失值。

更多

电影数据集中具有对象数据类型的大多数列都包含缺少的值。 默认情况下,聚合方法minmaxsum不返回任何内容,如以下代码片段所示,该代码片段选择三个对象列并尝试查找每个对象的最大值:

>>> movie[['color', 'movie_title', 'color']].max()
Series([], dtype: float64)

为了迫使 Pandas 为每一列返回值,我们必须填写缺失值。 在这里,我们选择一个空字符串:

>>> movie.select_dtypes(['object']).fillna('').min()
color                                                          Color
director_name                                          Etienne Faure
actor_2_name                                           Zubaida Sahar
genres                                                       Western
actor_1_name                                           Oscar Jaenada
movie_title                                                 Æon Flux
actor_3_name                                           Oscar Jaenada
plot_keywords                                    zombie|zombie spoof
movie_imdb_link    http://www.imdb.com/title/tt5574490/?ref_=fn_t...
language                                                        Zulu
country                                                 West Germany
content_rating                                                     X
dtype: object

出于可读性考虑,方法链通常被编写为每行一个方法调用,并在末尾使用反斜杠字符以避开新行。 这样可以更轻松地阅读和插入有关链的每个步骤返回的内容的注释:

>>> # rewrite the above chain on multiple lines
>>> movie.select_dtypes(['object']) \
         .fillna('') \
         .min()

由于未统一定义最小值和最大值,因此汇总所有字符串的列是无类型。 尝试调用明显没有字符串解释的方法,例如查找均值或方差,将无法正常工作。

另见

  • 参考第 1 章,“Pandas 基础”中的“将序列方法链接到一起”秘籍

将运算符与数据帧一起使用

它与第 1 章,“Pandas 基础”的秘籍有关,其中提供了关于运算符的入门知识。 这里。 Python 算术和比较运算符直接在数据帧上工作,就像在序列上一样。

准备

当数据帧直接使用算术运算符或比较运算符之一进行运算时,每列的每个值都会对其应用运算。 通常,当运算符与数据帧一起使用时,列要么全为数字,要么为所有对象(通常是字符串)。 如果数据帧不包含同类数据,则该操作很可能会失败。 让我们来看一个关于大学数据集失败的示例,该数据集同时包含数字和对象数据类型。 尝试将5添加到数据帧的每个值都会引发TypeError,因为不能将整数添加到字符串中:

>>> college = pd.read_csv('data/college.csv')
>>> college + 5
TypeError: Could not operate 5 with block values must be str, not int

若要成功将运算符与数据帧配合使用,请首先选择同类数据。 对于此秘籍,我们将选择以UGDS_开头的所有列。 这些栏代表按种族划分的大学生比例。 首先,我们导入数据并使用机构名称作为索引的标签,然后使用filter方法选择所需的列:

>>> college = pd.read_csv('data/college.csv', index_col='INSTNM')
>>> college_ugds_ = college.filter(like='UGDS_')
>>> college_ugds_.head()

此秘籍使用多个运算符和一个数据帧将本科生的列四舍五入到最接近的百分之一。 然后,我们将看到此结果如何等效于round方法。

操作步骤

  1. 为了与运算符开始四舍五入的冒险,我们首先将.00501添加到college_ugds_的每个值:
>>> college_ugds_ + .00501

  1. 使用楼层除法运算符//舍入到最接近的整数百分比:
>>> (college_ugds_ + .00501) // .01

  1. 要完成舍入练习,请除以 100:
>>> college_ugds_op_round = (college_ugds_ + .00501) // .01 / 100
>>> college_ugds_op_round.head()

  1. 现在,使用数据帧的round方法为我们自动进行舍入。 NumPy 四舍五入正好在两边到偶数边中间的数字。 因此,我们在舍入前添加一小部分:
>>> college_ugds_round = (college_ugds_ + .00001).round(2)
  1. 使用数据帧的equals方法测试两个数据帧的相等性:
>>> college_ugds_op_round.equals(college_ugds_round)
True

工作原理

步骤 1 使用加法运算符,该运算符尝试将标量值添加到数据帧的每一列的每个值。 由于列都是数字,因此此操作按预期进行。 每列中都有一些缺失值,但在操作后它们仍然缺失。

从数学上讲,添加.005应该足够,以便下一步的底数分割正确舍入到最接近的整数百分比。 由于浮点数的不精确性而出现问题:

>>> .045 + .005
0.049999999999999996

每个数字都有一个额外的.00001,以确保浮点表示的前四位数字与实际值相同。 之所以可行,是因为数据集中所有点的最大精度是四个小数位。

步骤 2 将楼层除法运算符//应用于数据帧中的所有值。 实际上,当我们除以小数时,它是将每个值乘以100并截断任何小数。 在表达式的第一部分周围需要括号,因为底数划分的优先级高于加法。 步骤 3 使用除法运算符将小数返回正确的位置。

在步骤 4 中,我们使用round方法重现了先前的步骤。 在执行此操作之前,由于与步骤 1 有所不同的原因,我们必须再次向每个数据帧值添加一个额外的.00001。NumPy 和 Python 3 的舍入数字恰好位于两边到偶数之间。 这种与偶数技术的联系通常不是学校正式教的。 它不会始终将数字偏向更高端

这里有必要四舍五入,以使两个数据帧值相等。equals方法确定两个数据帧之间的所有元素和索引是否完全相同,并返回一个布尔值。

更多

与序列一样,数据帧具有与运算符等效的方法。 您可以将运算符替换为其等效的方法:

>>> college_ugds_op_round_methods = college_ugds_.add(.00501) \
                                                 .floordiv(.01) \
                                                 .div(100)
>>> college_ugds_op_round_methods.equals(college_ugds_op_round)
True

另见

比较缺失值

Pandas 使用 NumPy NaN(np.nan)对象表示缺失值。 这是不寻常的对象,因为它不等于其自身。 与自身相比,甚至 Python 的None对象也将其求值为True

>>> np.nan == np.nan
False
>>> None == None
True

np.nan的所有其他比较也返回False,除了不等于:

>>> np.nan > 5
False
>>> 5 > np.nan
False
>>> np.nan != 5
True

准备

序列和数据帧使用等号运算符==进行逐元素比较,以返回相同大小的对象。 此秘籍向您展示如何使用相等运算符,该运算符与equals方法非常不同。

与前面的秘籍一样,将使用代表大学数据集中各种族学生的分数的列:

>>> college = pd.read_csv('data/college.csv', index_col='INSTNM')
>>> college_ugds_ = college.filter(like='UGDS_')

操作步骤

  1. 为了了解相等运算符的工作原理,我们将每个元素与一个标量值进行比较:
>>> college_ugds_ == .0019

  1. 这可以按预期工作,但是每当您尝试比较缺少值的数据帧时,就会出现问题。 该相同的等于运算符可用于在逐个元素的基础上将两个数据帧相互比较。 例如,将college_ugds_与自身进行比较,如下所示:
>>> college_self_compare = college_ugds_ == college_ugds_
>>> college_self_compare.head()

  1. 乍一看,所有值似乎都相等,就像您期望的那样。 但是,使用all方法确定每列是否仅包含True值会产生意外结果:
>>> college_self_compare.all()
UGDS_WHITE    False
UGDS_BLACK    False
UGDS_HISP     False
UGDS_ASIAN    False
UGDS_AIAN     False
UGDS_NHPI     False
UGDS_2MOR     False
UGDS_NRA      False
UGDS_UNKN     False
dtype: bool
  1. 发生这种情况是因为缺失值彼此之间没有相等的比较。 如果您尝试使用相等运算符对缺失值进行计数并对布尔列求和,则每个数字将得到零:
>>> (college_ugds_ == np.nan).sum()
UGDS_WHITE    0
UGDS_BLACK    0
UGDS_HISP     0
UGDS_ASIAN    0
UGDS_AIAN     0
UGDS_NHPI     0
UGDS_2MOR     0
UGDS_NRA      0
UGDS_UNKN     0
dtype: int64
  1. 计算缺失值的主要方法是使用isnull方法:
>>> college_ugds_.isnull().sum()
UGDS_WHITE    661
UGDS_BLACK    661
UGDS_HISP     661
UGDS_ASIAN    661
UGDS_AIAN     661
UGDS_NHPI     661
UGDS_2MOR     661
UGDS_NRA      661
UGDS_UNKN     661
dtype: int64
  1. 比较两个整个数据帧的正确方法不是使用相等运算符,而是使用equals方法:
>>> college_ugds_.equals(college_ugds_)
True

工作原理

步骤 1 将一个数据帧与一个标量值进行比较,而步骤 2 将一个数据帧与另一个数据帧进行比较。 乍看之下,这两种操作都非常简单直观。 第二个操作实际上是检查数据帧是否具有相同标签的索引,以及是否具有相同数量的元素。 如果不是这种情况,操作将失败。 有关更多信息,请参见第 6 章,“索引对齐”中的“生成笛卡尔积”秘籍。

步骤 3 验证数据帧中的列均不相等。 步骤 4 进一步显示了np.nan与它本身的不等价性。 步骤 5 验证数据帧中确实存在缺失值。 最后,第 6 步显示了将数据帧与equals方法进行比较的正确方法,该方法始终返回布尔型标量值。

更多

所有比较运算符都有对应的方法,可以使用更多功能。 有点令人困惑的是,数据帧的eq方法像相等运算符一样进行逐元素比较。eq方法与equals方法完全不同。 它仅执行与相等运算符相似的任务。 以下代码重复了步骤 1:

>>> college_ugds_.eq(.0019)    # same as college_ugds_ == .0019 

pandas.testing子包中,存在开发人员在创建单元测试时必须使用的函数。 如果两个数据帧不相等,则assert_frame_equal函数将引发AssertionError。 如果传递的两个帧相等,则返回None

>>> from pandas.testing import assert_frame_equal
>>> assert_frame_equal(college_ugds_, college_ugds_) 

单元测试是软件开发中非常重要的部分,并确保代码正确运行。 Pandas 包含成千上万的单元测试,可帮助确保其正常运行。 要了解有关 Pandas 如何运行其单元测试的更多信息,请参阅文档中的“对 Pandas 做贡献”部分。

转换数据帧操作的方向

许多数据帧方法都有一个axis参数。 这个重要的参数控制操作的方向。 轴参数只能是两个值之一(0 或 1),并且分别作为字符串indexcolumn的别名。

准备

几乎所有的数据帧方法都将axis参数默认为0/index。 此秘籍向您展示了如何调用相同的方法,但其操作方向已被调换。 为了简化练习,将仅使用引用大学数据集中每个学校的百分比种族的列。

操作步骤

  1. 读取大学数据集; 以UGDS_开头的列代表特定种族的本科生所占的百分比。 使用filter方法选择以下列:
>>> college = pd.read_csv('data/college.csv', index_col='INSTNM')
>>> college_ugds_ = college.filter(like='UGDS_')
>>> college_ugds_.head()

  1. 现在,数据帧包含均匀的列数据,可以在垂直和水平方向上合理地进行操作。count方法返回非缺失值的数量。 默认情况下,其axis参数设置为 0:
>>> college_ugds_.count()
UGDS_WHITE    6874
UGDS_BLACK    6874
UGDS_HISP     6874
UGDS_ASIAN    6874
UGDS_AIAN     6874
UGDS_NHPI     6874
UGDS_2MOR     6874
UGDS_NRA      6874
UGDS_UNKN     6874

由于axis参数几乎总是设置为 0,因此无需执行以下操作,但出于理解的目的,第 2 步等效于college_ugds_.count(axis=0)college_ugds_.count(axis='index')

  1. axis参数更改为 1 /列,将对操作进行转置,以使每行数据都有其非缺失值的计数:
>>> college_ugds_.count(axis='columns').head()
INSTNM
Alabama A & M University               9
University of Alabama at Birmingham    9
Amridge University                     9
University of Alabama in Huntsville    9
Alabama State University               9
  1. 代替计算非缺失值,我们可以对每一行中的所有值求和。 每行百分比应总计为 1。sum方法可用于验证这一点:
>>> college_ugds_.sum(axis='columns').head()
INSTNM
Alabama A & M University               1.0000
University of Alabama at Birmingham    0.9999
Amridge University                     1.0000
University of Alabama in Huntsville    1.0000
Alabama State University               1.0000
  1. 为了了解每列的分布,可以使用median方法:
>>> college_ugds_.median(axis='index')
UGDS_WHITE    0.55570
UGDS_BLACK    0.10005
UGDS_HISP     0.07140
UGDS_ASIAN    0.01290
UGDS_AIAN     0.00260
UGDS_NHPI     0.00000
UGDS_2MOR     0.01750
UGDS_NRA      0.00000
UGDS_UNKN     0.01430

工作原理

操作的方向是 Pandas 中比较混乱的方面之一,互联网上到处都有讨论它的解释的线程。 许多新手 Pandas 用户很难记住axis参数的含义。 幸运的是,在 Pandas 中,一项操作可以完成两个潜在的方向。 一种可能的方法是尝试双向尝试直到获得所需结果的简单蛮力解决方案。 我记得axis参数的含义,认为 1 看起来像一列,对axis=1的任何操作都会返回一个新的数据列(与该列具有相同数量的项)。

这在第 3 步中得到确认,在第 3 步中,结果(没有head方法)将返回新的数据列,并且可以根据需要轻松地将其作为列附加到数据帧中。axis等于1/index的其他步骤将返回新的数据行。

更多

使用axis=1cumsum方法累积了每一行的种族百分比。 它给出的数据视图略有不同。 例如,很容易看到每所学校的白人,黑人和西班牙裔美国人的确切百分比:

>> college_ugds_cumsum = college_ugds_.cumsum(axis=1)
>>> college_ugds_cumsum.head()

另见

确定大学校园的多样性

每年都会写很多文章,讨论多样性对大学校园的不同方面和影响。 各种组织已经开发出度量标准,以尝试测量多样性。 《美国新闻》是为许多不同类别的大学提供排名的领导者,其中之一就是多样性。 他们的多样性指数排名前十的学院如下:

>> pd.read_csv('data/college_diversity.csv', index_col='School')

准备

我们的大学数据集将种族分为九个不同类别。 当尝试量化没有明显定义的事物时,例如多样性,它有助于从非常简单的事物开始。 在此秘籍中,我们的多样性指标将等于学生人数超过 15% 的种族数。

操作步骤

  1. 读入大学数据集,并仅针对大学生种族栏进行过滤:
>>> college = pd.read_csv('data/college.csv', index_col='INSTNM')
>>> college_ugds_ = college.filter(like='UGDS_')
  1. 这些大学中许多都缺少其种族列的值。 我们可以计算每一行的所有缺失值,并对所得的序列从最高到最低进行排序。 这将向大学揭示缺少值:
>>> college_ugds_.isnull()\
                 .sum(axis=1)\
                 .sort_values(ascending=False)\
                 .head()
INSTNM
Excel Learning Center-San Antonio South         9
Philadelphia College of Osteopathic Medicine    9
Assemblies of God Theological Seminary          9
Episcopal Divinity School                       9
Phillips Graduate Institute                     9
dtype: int64
  1. 既然我们已经看到了缺少所有种族列的大学,我们可以使用dropna方法删除所有缺少 9 个种族百分比的所有行。 然后,我们可以计算剩余的缺失值:
>>> college_ugds_ = college_ugds_.dropna(how='all')
>>> college_ugds_.isnull().sum()
UGDS_WHITE    0
UGDS_BLACK    0
UGDS_HISP     0
UGDS_ASIAN    0
UGDS_AIAN     0
UGDS_NHPI     0
UGDS_2MOR     0
UGDS_NRA      0
UGDS_UNKN     0
dtype: int64
  1. 数据集中没有遗漏任何值。 现在,我们可以计算多样性指标。 首先,我们将使用大于或等于数据帧的方法ge将每个值转换为布尔值:
>>> college_ugds_.ge(.15)

  1. 从这里开始,我们可以使用sum方法对每个学院的True值进行计数。 请注意,返回了一个序列:
>>> diversity_metric = college_ugds_.ge(.15).sum(axis='columns')
>>> diversity_metric.head()
INSTNM
Alabama A & M University               1
University of Alabama at Birmingham    2
Amridge University                     3
University of Alabama in Huntsville    1
Alabama State University               1
dtype: int64
  1. 为了了解分布情况,让我们在本序列中使用value_counts方法:
>>> diversity_metric.value_counts()
1    3042
2    2884
3     876
4      63
0       7
5       2
dtype: int64
  1. 令人惊讶的是,两所学校在五个不同种族类别中的比例超过 15% 。 让我们对diversity_metric序列进行排序,以找出它们是哪些:
>>> diversity_metric.sort_values(ascending=False).head()
INSTNM
Regency Beauty Institute-Austin          5
Central Texas Beauty College-Temple      5
Sullivan and Cogliano Training Center    4
Ambria College of Nursing                4
Berkeley College-New York                4
dtype: int64
  1. 学校可以这么多样化似乎有点可疑。 让我们看一下这两家顶级学校的原始百分比。.loc索引器用于根据索引标签专门选择:
>>> college_ugds_.loc[['Regency Beauty Institute-Austin', 
                       'Central Texas Beauty College-Temple']]

  1. 似乎有几个类别被汇总到“未知”和“两个或多个种族”列中。 无论如何,它们似乎都非常不同。 我们可以看到美国新闻学院排名前 10 的学校在这一基本多样性指标方面的表现如何:
>>> us_news_top = ['Rutgers University-Newark',
                   'Andrews University', 
                   'Stanford University', 
                   'University of Houston',
                   'University of Nevada-Las Vegas']

>>> diversity_metric.loc[us_news_top]
INSTNM
Rutgers University-Newark         4
Andrews University                3
Stanford University               3
University of Houston             3
University of Nevada-Las Vegas    3
dtype: int64

工作原理

第 2 步进行计数,然后显示缺失值最多的学校。 由于数据帧中有九列,因此每所学校的缺失值最大数目为九。 许多学校缺少每一列的值。 步骤 3 删除所有值均缺失的行。 步骤 3 中的dropna方法具有how参数,该参数默认为字符串any,但也可以更改为all。 设置为any时,它将删除包含一个或多个缺失值的行。 设置为all时,它仅删除缺少所有值的行。

在这种情况下,我们保守地删除丢失所有值的行。 这是因为某些缺失值可能仅代表 0% 。 这不是碰巧的情况,因为执行dropna之后没有丢失值。 如果仍然缺少值,我们可以运行fillna(0)方法用 0 填充所有剩余值。

步骤 4 使用大于或等于方法ge开始我们的多样性指标计算。 这将导致所有布尔值的数据帧,通过设置axis='columns'将其水平求和。

在第 5 步中使用value_counts方法来生成我们的多样性指标的分布。 对于学校而言,很少有三个种族的大学生人数占总人数的 15% 或更多。 第 7 步和第 8 步根据我们的指标找到最多样化的两所学校。 尽管它们是多种多样的,但似乎很多种族并没有得到充分考虑,并且被默认为未知类别和两个或多个类别。

步骤 9 从“美国新闻”文章中选择排名前五的学校。 然后,从我们新创建的序列中选择其多样性指标。 事实证明,这些学校在我们的简单排名系统中也得分很高。

更多

另外,我们可以通过按最大种族百分比对它们进行排序来找到差异最小的学校:

>>> college_ugds_.max(axis=1).sort_values(ascending=False).head(10)
INSTNM
Dewey University-Manati                               1.0
Yeshiva and Kollel Harbotzas Torah                    1.0
Mr Leon's School of Hair Design-Lewiston              1.0
Dewey University-Bayamon                              1.0
Shepherds Theological Seminary                        1.0
Yeshiva Gedolah Kesser Torah                          1.0
Monteclaro Escuela de Hoteleria y Artes Culinarias    1.0
Yeshiva Shaar Hatorah                                 1.0
Bais Medrash Elyon                                    1.0
Yeshiva of Nitra Rabbinical College                   1.0
dtype: float64

我们还可以确定是否有任何一所学校的所有 9 个种族类别都超过 1% :

>>> (college_ugds_ > .01).all(axis=1).any()
True

另见