编辑者提供的图片
当我开始我的数据科学之旅时,我选择了 R 作为我的第一个编程语言。我变得非常熟悉使用 dplyr 包来根据某些条件过滤数据。几年后,当我开始使用 Python 时,最初对 Pandas 感到有些排斥,因为它与 dplyr 的差异很大。
1. 谷歌网络安全证书 - 快速开启网络安全职业生涯。
2. 谷歌数据分析专业证书 - 提升你的数据分析能力
3. 谷歌 IT 支持专业证书 - 支持你的组织 IT 工作
随着时间的推移,我变得越来越习惯使用 Pandas 方法来过滤数据,它变得不再那么令人畏惧。本文是一个关于使用 Pandas 进行条件过滤的五种方法的教程,包括单条件过滤和多条件过滤。
本文使用的过滤技术有:
-
Pandas 的选择括号过滤
-
Pandas 系列方法:isin()、between()、contains()
-
在选择括号过滤之外定义单独的过滤器
-
query()
-
loc[]
-
附加内容:使用 pandas 的 filter()方法
本文的数据集来源于 Nehla Birla 发布的“车辆数据集”,托管在Kaggle.com,包含有关待售二手车的信息。
首先,我导入 Pandas 并读取数据集。
为了感受每种方法的语法和可读性,本文使用两个示例来过滤车辆数据集:一个简单的过滤和一个多条件的过滤。
-
简单过滤:查找所有 2013 年及以后的车辆
-
多条件过滤:查找所有 2013 年、2014 年和 2015 年的本田车型,价格在 300000 到 450000 之间(含)。
查找所有 2013 年或更新年份的车辆是一个相当标准的 Pandas 过滤任务:选择要过滤的数据集列,告诉它要过滤的值,并将该条件插入整个数据框的括号中。
如果我们想进行多条件搜索,我们可以将每个单独的过滤器放在括号 () 内,通过布尔搜索条件分隔(& 表示和,| 表示或,~ 表示非)。
这些多个条件从技术上讲是有效的,但这段代码的可读性不佳。到处都是括号和圆括号。为了简化代码并使用更少的条件,pandas 提供了各种方法,我们可以使用其中之一来获得相同的结果,这里我们在上面的代码块中使用了 str.contains()。
我们可以将许多 pandas.Series 方法应用于我们的列。这些方法列在Pandas 文档中。我们在过滤时查看系列方法的原因是因为 Pandas.DataFrame 的每一列都是一个 Pandas.Series 元素,因此我们可以将 Pandas.Series 方法和功能应用于它。
我们可以在车辆数据集上使用许多方法,但为了使用多个条件的例子来过滤数据,我们将使用:
-
isin() – 检查系列值是否在给定列表中
-
str.contains() – 检查系列中是否包含某个字符串
-
between() – 查找介于两个值之间的系列值
我们将使用 isin() 来检查哪些车辆符合我们感兴趣的年份,使用 str.contains() 查找哪些车辆名称中包含 Honda,以及使用 between() 查找价格范围内的车辆。
这在一定程度上简化了代码,并利用了一些 Pandas.Series 方法,但代码仍然不是特别可读。为了使其看起来更好,我们可以将代码分成多行,每行一个过滤操作。这样做的方法是在初始数据框选择括号内部放入普通圆括号,然后将所有条件插入这些圆括号中。
结合上述两种方法,我们可以将过滤器定义在选择括号外部作为变量,然后在选择括号内部调用每个变量。这是一种干净的方式,可以将每个过滤器写在单独的一行,然后在一行代码中调用所有过滤器。这意味着在整个代码中减少了总体上的括号和换行符。
一两年前,我在播客中第一次听说 pandas.Series.query,一开始我并不喜欢。但随着时间的推移,它确实让我很喜欢。查询表达式是一种很好的数据子集化方式:它们可以很基础也可以很复杂和强大。用于子集化 2013 年及以后的车辆的查询表达式非常简单。你将过滤参数作为字符串传递进去。
当你使用多条件过滤器时,查询字符串会变得更复杂。你可以用 and 或 or 来代替 & 或 | 之间的过滤参数。以下是为我们的多条件过滤器编写查询表达式的代码。注意:要调用环境中但在 DataFrame/Series 外部的变量,你需要在调用变量之前加上 @。请注意 @ 在调用列表“years”之前的使用。
这是一种非常简洁的数据子集化方式!然而,添加的查询参数越多,代码的可读性就会下降。为了解决这个问题,使用 query,我们可以在想要换行的地方添加 \,并在下一行继续查询表达式。如果需要,我们可以保持每个过滤条件单独一行的表示方式。
我真的很喜欢使用 Python lambda 函数所带来的强大功能。我们如何将 lambda 转换为用我们的条件过滤车辆数据集?对于我们一直在应用的简单单条件过滤,我们调用 dataframe 上的 loc,并且通过 lambda,我们可以插入我们的条件。
如果我们想要添加多个条件,只需在前一个结果上链式调用另一个 loc。然而,如果放在一行上,会到处都是括号和句点!这变得非常难以阅读。为了提高可读性,我们可以将表达式的整个右侧括在括号中,然后将每个 loc 过滤器放在自己的行上。
有趣的是,Pandas.DataFrame 的 filter() 方法并不允许你基于数据集中的数据来过滤数据集,尽管它的名字原本给我的印象是这样。实际上,filter() 方法允许你基于行/索引名称和/或列名称来过滤数据,从而对数据进行子集化。
筛选只有少数列和整数作为索引名称的车辆数据集,并不能完全展示 filter() 的强大功能。我们可以将“name”列中的值设置为索引,这些值包括汽车的品牌和型号。然而,很多行的品牌和型号是相同的,会有相同的索引,这并不是最佳实践。因此,为了使用品牌和型号,同时使每个索引唯一,我们将当前的整数索引与行的品牌和型号连接起来,然后将其作为新索引。你在实际操作中可能会选择不这样做,但我这样做是为了演示方法。
使用这个新索引,我们可以使用 Pandas.DataFrame 方法 filter() 基于索引名称进行筛选。通过 filter(),我们可以使用类似于之前使用的 str.contains() 的输入来搜索特定的索引,或者可以使用正则表达式来搜索索引。为了根据本篇文章中使用的多条件筛选来过滤数据,我们可以返回与其他五种技术相同的结果。唯一的区别是我们现在使用索引值来帮助筛选结果。
就这样!我们已经看到五种技术和一种额外的技术,帮助你在给定一个或多个筛选标准的情况下对数据进行切片和切块。我不确定我最喜欢哪一种;我想这取决于使用场景。我能看到自己使用 query() 由于查询字符串表达式的简便性和可读性,但我也能看到自己使用 loc[] 和 lambda 函数!
我希望这篇文章能给你一些如何将强大的 Pandas 功能应用到数据中的想法!
布莱恩·科拉诺 是一名现役的美国陆军军官和数据科学家,居住在华盛顿特区附近。当他不在提升 Python 技能时,他会花时间陪伴他的妻子和四个孩子,或者学习西班牙语和法语。