pandas库的DataFrame,作为一种非常强大的数据处理手段,一直以来无论是从整个库的API设计和性能,都给我非常大的惊喜,但是,在由生疏到慢慢熟练的过程中,发现在利用DataFrame时,一个最大的问题就是,如何高效优雅地选取到自己需要的数据,毕竟大部分时候我们是不需要整个DataFrame中的所有数据的。而为了遵循python语言本身的设计哲学,这些操作几乎都是利用原有的运算符,pandas只是进行了覆写,因而这些技能都像是隐藏的大招,难以发现和掌握,故特意花时间研究了一下官方文档,现总结如下,见笑。

1.属性选取

属性选取,顾名思义,也就是直接使用面向对象思想中对象的属性这一特性,以进行数据的选取,这里我们先创建一个示例的数据

In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: df = pd.DataFrame(np.random.randn(6, 6),...:                   index=[1, 2, 'C', 'D', '5', '6'],...:                   columns=['A', 'B', '3', '4', 5, 6])

注意,这里我们构建了一个非常特殊的DataFrame,特别是在index和columns这两个参数,我们使用了三种分别明显的index,有int型的,字符串型两种,字符型里面又有数字和字母不一,且两者是没有冲突的,下面会演示这些index和columns都有啥坑(以下可能将index称为行名,columns称为列名)。

正常情况下,使用属性选取方式是非常简单的,直接访问对象属性即可

In [4]: df.A
Out[4]:
1    0.124010
2   -1.520924
C   -1.941521
D   -0.613758
5    0.788941
6    0.894871
Name: A, dtype: float64

而如果我们访问不存在的列,将会抛出AttributeError异常(为节约篇幅,以下错误演示去除了堆栈信息,仅保留异常信息,下同)

In [5]: df.G
AttributeError: 'DataFrame' object has no attribute 'G'

当然,属性选取有非常大的局限性,首先,这种方法不能选取到行,只能选取到列

In [6]: df.C
AttributeError: 'DataFrame' object has no attribute 'C'

其次就是其对于列名也有非常严格的要求,首先就是如果列名与Dataframe自带的方法、属性名一致的列都不能使用属性选取

In [7]: df.min
Out[7]:
<bound method DataFrame.min of           A         B         3         45         6
1  0.124010 -1.160599 -1.236521 -2.356423  0.089117  1.090875
2 -1.520924 -0.285893  0.918261  1.250223  2.028298 -0.007042
C -1.941521  0.716184  0.318086 -0.540593 -2.408134  0.526977
D -0.613758  0.718741 -1.420655 -0.182436  0.333909 -1.608134
5  0.788941 -0.541419 -1.195019 -0.924797 -0.261171  1.078212
6  0.894871  0.454283 -0.763443  0.065712 -0.336119  0.182736>

再次就是由于Python的命名规则,开头是数字的,或者带有其他非法符号的显然也不能使用属性选取

In [8]: df.3
SyntaxError: invalid syntax

最后,由于Python对象的动态特性,如果希望通过属性的方式添加一列,也是错误的

In [9]: df.G = np.arange(6)
In [10]: df
Out[10]:A         B         3         4         5         6
1  0.124010 -1.160599 -1.236521 -2.356423  0.089117  1.090875
2 -1.520924 -0.285893  0.918261  1.250223  2.028298 -0.007042
C -1.941521  0.716184  0.318086 -0.540593 -2.408134  0.526977
D -0.613758  0.718741 -1.420655 -0.182436  0.333909 -1.608134
5  0.788941 -0.541419 -1.195019 -0.924797 -0.261171  1.078212
6  0.894871  0.454283 -0.763443  0.065712 -0.336119  0.182736
In [11]: df.G
Out[11]: array([0, 1, 2, 3, 4, 5])

可以看到,我们只是动态地给DataFrame的实例对象加入了一个属性,而如果该列已经存在,是可以对该列进行更改的

In [12]: df.A = np.arange(6)In [13]: df
Out[13]:A         B         3         4         5         6
1  0 -1.160599 -1.236521 -2.356423  0.089117  1.090875
2  1 -0.285893  0.918261  1.250223  2.028298 -0.007042
C  2  0.716184  0.318086 -0.540593 -2.408134  0.526977
D  3  0.718741 -1.420655 -0.182436  0.333909 -1.608134
5  4 -0.541419 -1.195019 -0.924797 -0.261171  1.078212
6  5  0.454283 -0.763443  0.065712 -0.336119  0.182736

所以,以上就是属性选取方法,可见,由于各种限制,属性选取有着非常大的局限,同时,这种操作还很可能得到意想不到的结果,所以我个人并不推介使用这种选取的方法。

2.基础选取

官方文档称这种方式为Basic Selecte,所以这里我也称之为基础选取方式,这种方式就是直接对DataFrame对象使用[]运算符,其实现方式是实现类的__getitem__魔术方法。

使用这种选取方法,非常简单,直接将需要选取的列名放入中括号中即可

In [14]: df['A']
Out[14]:
1    0
2    1
C    2
D    3
5    4
6    5
Name: A, dtype: int32

可以看到,这种方法可以非常方便地选取到一列数据,而当我们访问不存在的列时,将会抛出KeyError,这与Python的魔术方法协议是一致的,这点也是我喜欢Python的原因之一,通过各种类似协议的魔术方法,使得类似的操作能得到同样的结果,比如求长度我就立刻知道应该使用len()而不需要去猜是.length()还是.size()

In [15]: df['G']
KeyError: 'G'

其次就是,这种方法只能选取一列,不可以同时选取到多个列

In [16]: df[('A', 'B')]
KeyError: ('A', 'B')

同时需要注意的是,我们传入的列名,其类型也必须和列名的类型相同

In [17]: df[3]
KeyError: 3
In [18]: df['3']
Out[18]:
1   -1.236521
2    0.918261
C    0.318086
D   -1.420655
5   -1.195019
6   -0.763443
Name: 3, dtype: float64

最后,这种方法还可以对DataFrame进行切片,但此时是对行进行切片

In [19]: df[:'B']
KeyError: 'B'
In [20]: df[:3]
Out[20]:A         B         3         4         5         6
1  0 -1.160599 -1.236521 -2.356423  0.089117  1.090875
2  1 -0.285893  0.918261  1.250223  2.028298 -0.007042
C  2  0.716184  0.318086 -0.540593 -2.408134  0.526977
In [21]: df[:'D':-1]
Out[21]:A         B         3         4         5         6
6  5  0.454283 -0.763443  0.065712 -0.336119  0.182736
5  4 -0.541419 -1.195019 -0.924797 -0.261171  1.078212
D  3  0.718741 -1.420655 -0.182436  0.333909 -1.608134

可以看到,此时我们可以使用坐标进行切片,也可以输入行名,同时切片的第三个参数也是可以使用的,但如果我们尝试对列进行切片,则会抛出KeyError

3.标签选取

标签选取,即严格使用行列名(index)进行选取的方法,其需要使用.loc属性

In [22]: df.loc['C']
Out[22]:
A    2.000000
B    0.716184
3    0.318086
4   -0.540593
5   -2.408134
6    0.526977
Name: C, dtype: float64

可以看到,这种方法其能够快速选定一行,而事实上,该方法可以同时定位行和列或者多行多列,此时只需要默念,先行后列,先行后列,先行后列,并使用逗号分隔

In [23]: df.loc['C', 'A']
Out[23]: 2.0
In [24]: df.loc[('C', 'D'), ('A', 'B')]
Out[24]:A         B
C  2  0.716184
D  3  0.718741

同时,该方法可以进行切片,并且可以行列同时进行,需要说明的是,这里只能接受两个参数,即行和列的切片,不能使用第三个参数

In [25]: df.loc['C':'6', '3':]
Out[25]:3         4         5         6
C  0.318086 -0.540593 -2.408134  0.526977
D -1.420655 -0.182436  0.333909 -1.608134
5 -1.195019 -0.924797 -0.261171  1.078212
6 -0.763443  0.065712 -0.336119  0.182736
In [26]: df.loc['C':'6', '3':, -1]
IndexingError: Too many indexers

如果需要表示全部选中,比如选中所有的行,则必须要写出:表示全部选中,即可以省略后面的参数,默认选中所有的列,但是,需要选中所有的行时,行参数不可以省略

In [27]: df.loc[:, 5]
Out[27]:
1    0.089117
2    2.028298
C   -2.408134
D    0.333909
5   -0.261171
6   -0.336119
Name: 5, dtype: float64

如果我们选择不存在的位置,将会抛出KeyError

In [28]: df.loc['G']
KeyError: 'the label [G] is not in the [index]'

值得注意的是,该方法要求非常严格,其要求输入的行列名必须与DataFrame的行列名类型一致,否则抛出KeyError

In [29]: df.loc[6]
KeyError: 'the label [6] is not in the [index]'

4.坐标选取

大多数刚刚开始使用DataFrame的童鞋可能都非常喜欢使用坐标来选取数据,毕竟如果我们将DataFrame理解为一个二维的数组,那么使用坐标来选取就显得非常的浑然天成,但是这时候,我们会发现前面说到的方法多大都需要时行名列名且要确保其类型也是一致的,这时候传统的直接使用坐标进行选取就会失效。

如果我们仍然需要使用坐标进行数据选取,此时就需要使用.iloc属性

In [30]: df.iloc[0]
Out[30]:
A    0.000000
B   -1.160599
3   -1.236521
4   -2.356423
5    0.089117
6    1.090875
Name: 1, dtype: float64

与标签选取方法类似,这种方法也可以同时定位行和列、多行和多列,同样是先行后列,逗号分隔

In [31]: df.iloc[0, 5]
Out[31]: 1.0908752302725568
In [32]: df.iloc[(0, 2), (2, 3)]
Out[32]:3         4
1 -1.236521 -2.356423
C  0.318086 -0.540593

同样的,也支持切片,使用:表示全部选中,不可以有第三个参数

In [33]: df.iloc[:, :3]
Out[33]:A         B         3
1  0 -1.160599 -1.236521
2  1 -0.285893  0.918261
C  2  0.716184  0.318086
D  3  0.718741 -1.420655
5  4 -0.541419 -1.195019
6  5  0.454283 -0.763443

可以说,这个方法和标签选取的方法,其实是两兄弟,一个是使用标签,一个是使用坐标进行选取,当然,如果我们选取不存在的位置,其也会非常合理地抛出IndexError

In [34]: df.iloc[7]
IndexError: single positional indexer is out-of-bounds

而如果我们输入的参数不是整数,此时将会抛出TypeError

In [35]: df.iloc['C']
TypeError: cannot do positional indexing with these indexers [C] of <class 'str'>

最后需要注意的是,这里说的坐标都是实时的,所以,我们需要注意,不要与标签选取的方法混淆,标签选取无论该数据在表格的什么位置都能够选取到,而定位选取则有可能给我们不一样的结果

In [36]: df.sort_values('B').iloc[1]
Out[35]:
A    4.000000
B   -0.541419
3   -1.195019
4   -0.924797
5   -0.261171
6    1.078212
Name: 5, dtype: float64
In [37]: df.sort_values('B').loc[1]
Out[37]:
A    0.000000
B   -1.160599
3   -1.236521
4   -2.356423
5    0.089117
6    1.090875
Name: 1, dtype: float64

可以看到,即使顺序打乱了,我们仍能根据行名准确得到对应的行,而如果是是用坐标,那么我们得到的就是B列最小的那一行即这里的行名为5的行,所以在选择使用标签选取或者是定位选取的时候,一定要明确自己的需求。

5.条件筛选

当我们需要按列筛选行的时候,根据上面所说的方法,直接使用[],这里是一致的

In [38]: df[df['B'] > 0]
Out[38]:A         B         3         4         5         6
C  2  0.716184  0.318086 -0.540593 -2.408134  0.526977
D  3  0.718741 -1.420655 -0.182436  0.333909 -1.608134
6  5  0.454283 -0.763443  0.065712 -0.336119  0.182736

而按行筛选列时,则有一点不同,正确的方法是

In [39]: df.loc[:, df.loc['C'] > 0]
Out[39]:A         B         3         6
1  0 -1.160599 -1.236521  1.090875
2  1 -0.285893  0.918261 -0.007042
C  2  0.716184  0.318086  0.526977
D  3  0.718741 -1.420655 -1.608134
5  4 -0.541419 -1.195019  1.078212
6  5  0.454283 -0.763443  0.182736

是否觉得很诡异,这里我们可以探究一下pandas是如何实现这个筛选逻辑的

In [40]: df['B'] > 0
Out[40]:
1    False
2    False
C     True
D     True
5    False
6     True
Name: B, dtype: bool
In [41]: df.loc['C'] > 0
Out[41]:
A     True
B     True
3     True
4    False
5    False
6     True
Name: C, dtype: bool

可以看到,当我们取出一列进行大小判断时,其返回的是所有的行的判断结果(Series对象),而如果我们对一行进行判断,则返回的是各个列的判断结果,即pandas通过覆写大小判断运算符将判断操作自动扩展至对象中的每个元素,这点和R的逻辑是契合的。而上面我们也说到过,当我们使用的是[]进行切片时,我们是对行进行切片,而这里,我们刚好就填入了行的判断结果(In [40]),所以,如果我们输入的切片参数是Series对象时,pandas将切出被判断为True的行。对列进行筛选的操作也是同样的,只不过先行后列,所以前面我们需要:来表示我们选中所有的行,同时也就意味着,使用.loc时,可以同时对行和列进行筛选,只需要同时给出判断结果即可

In [42]: df.loc[df['B'] > 0, df.loc['C'] > 0]
Out[42]:A         B         3         6
C  2  0.716184  0.318086  0.526977
D  3  0.718741 -1.420655 -1.608134
6  5  0.454283 -0.763443  0.182736

到这里,相信这个筛选操作就比较好理解了,但是有一点需要注意的是,我们不能使用定位方法进行筛选

In [43]: df.iloc[1] > 0
Out[43]:
A     True
B    False
3     True
4     True
5     True
6    False
Name: 2, dtype: bool

可以看到,即使我们使用定位方法选中的行列并进行判断后,其仍然使用标签进行定位,所以我们可以知道,在pandas的设计当中,标签定位的优先级是大于坐标定位的,因而在使用pandas库时,应该转变思想,要将理念从二维数组中跳脱出来,将pandas理解为一个更像数据库的存在。

最后,也就是多条件联合筛选,由于python内置的布尔运算符andor不可以进行覆写,而此时又需要将布尔运算扩展至每个元素,因而pandas覆写了&|这两个运算符,写过其他语言的小伙伴应该不陌生,要注意的是,这里只需要写一次,不像Java等需要写两次。

In [44]: df[(df['B'] > 0) & (df['3'] > 0)]
Out[44]:A         B         3         4         5         6
C  2  0.716184  0.318086 -0.540593 -2.408134  0.526977
In [45]: df.loc[:, (df.loc['C'] > 0) | (df.loc['5'] > 0)]
Out[45]:A         B         3         6
1  0 -1.160599 -1.236521  1.090875
2  1 -0.285893  0.918261 -0.007042
C  2  0.716184  0.318086  0.526977
D  3  0.718741 -1.420655 -1.608134
5  4 -0.541419 -1.195019  1.078212
6  5  0.454283 -0.763443  0.182736

需要注意的是,由于只有进行判断结束返回的Series对象,才可以进行布尔运算,所以这里需要给每个判断条件加上小括号以提高运算优先级。

6.总结

最后,结合上述内容和我个人的使用经验,总结一些我个人最佳实践

  1. 数据在读取时就需要进行处理,首先一点就是应该将每一行作为一个样本,一列作为一个特征,由于大多数时候是根据属性对样本进行筛选,所以此时筛选起来就会更加的便捷。(df.T可以获得经过转置的DataFrame即行列交换)
  2. 每一个样本即每一行都必须使用唯一的index(行名),这一点有点数据库的主键的意思,这样就能保证我们能准确选到我们需要的数据,至于列名,如果根据前面的规则,则应该不会存在相同的特征,且全部行名和列名统一为一种类型,推介字符串类型。
  3. 当需要选中一列时,直接使用df[col]的方法选中一列,而需要选中一行的时候,直接使用df.loc[row]来选中一行。
  4. 在任何操作中尽可能避免使用坐标进行定位。
  5. 当需要同时对行列进行操作时,使用df.loc[row, loc]并在心里默念先行后列,同时,这种方式的参数比较多样,可以输入判断结果,也可输入单个行列名或者包含多个行列名的列表,可以按需选择。
  6. 更多待补充。

作者:yangmqglobe
链接:https://www.jianshu.com/p/127587a80491

DataFrame数据选取超全攻略相关推荐

  1. 微信小游戏上线字节平台超全攻略

    文章目录 平台要求差异 工程代码迁移 腾讯云开发的适配 除了长期占据主流位置的微信平台,字节跳动平台也是一个非常值得个人开发的小游戏上线的阵地.字节系的很多产品(如抖音.西瓜视频.今日头条)都带有巨大 ...

  2. MySQL数据备份恢复全攻略,让我们通过简单几步找回丢失的数据

    编写初衷 没有人生下来天生就是会计算机的,就拿笔者来说的话,也是从Windows->Centos->Ubuntu一步一步慢慢学习,积累下来的.为了让大家能够更快更高效率的学习,从今天开始, ...

  3. 还不知道怎么制作用户体验地图?这份超全攻略送给你

    用户体验地图就是记录从用户来到你的产品到完成目标离开的全部过程.通过直观地了解:用户如何使用自己的产品?以及在此过程中的整体感受,帮助我们寻找新的机遇去建立更好的用户体验.让企业的团队梳理用户视角.全 ...

  4. 苹果电子邮件怎么注册_忘记了Apple ID账号密码怎么办?超全攻略,帮你解决问题...

    第一种:若只是忘记了Apple ID账号的话,可以通过几种方法来查看 ①第一种就是通过"账号与密码"还原方法 当然,使用这个方法,就要你存储过账号密码了. ②iOS10.2以上版本 ...

  5. 如何录制一个优秀的游戏解说视频?超全攻略,手把手教你!

    想把自己游戏的精彩部分进行解说并分享给大家?怎么才能录制高清的游戏视频过程?有没有手把手针对小白的详细教程?现在就根据自己做游戏解说视频的经验,教大家用超级捕快录制一个游戏解说视频,导出的画质保持10 ...

  6. seo全攻略_SEO优化操作流程是怎样的?有哪些步骤?

    在互联网时代营销,企业必须明白如何捍卫自己的合法权益及如何去捕获用户流量,只有这样才可以在竞争日益激烈的角逐中取胜. 然而,SEO优化是大多数企业优先选择捍卫自己权益的有力武器.那么,SEO优化操作流 ...

  7. 超详细的抖音运营全攻略

    超详细的抖音运营全攻略 截止到 2018 年 6 月,抖音的全球总用户已经达到 8 亿,国内日活跃用户超过 1.5 亿,国内 月活跃用户超过 3 亿,且用户边界不断拓展,用户画像更加丰富多元化:用户活 ...

  8. MySQL与优化有关的命令_MySQL优化全攻略-相关数据库命令

    MySQL优化全攻略-相关数据库命令 更新时间:2006年11月25日 00:00:00   作者: 接下来我们要讨论的是数据库性能优化的另一方面,即运用数据库服务器内建的工具辅助性能分析和优化. ▲ ...

  9. 口袋超萌服务器维护中,平民全攻略1

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 平民全攻略 等级篇:每天商店买4个体力和限时抢购里全部体力,冲等级,有些人说到70级级就可以了,我建议到80级才停,为什么?因为等级高,每周日排名结算时钻 ...

最新文章

  1. 如何手动的用jigloo设计器关联java界面文件 jigloo 设计器失去关联解决
  2. 蓝桥杯:入门训练 序列求和
  3. 微信分享转发功能「PHP版」
  4. Linux 进程等待队列
  5. 纸质图书与电子图书的营销策略研究
  6. 哪里有计算机一级的题库,计算机一级题库带答案哪有?
  7. TraceView(profile) and Systrace
  8. 如何避免面试现场被虐?
  9. C#递归方法遍历目录及子目录
  10. java 对手机号码进行校验
  11. otl c mysql_OTL的使用
  12. 当年“你说什么,我都能实现”的软件公司,后来都是怎么死的?
  13. 程序员转行一般是因为什么,会去哪些行业?
  14. 晶振选型需要注意哪些事项?-台湾TST嘉硕
  15. Magic Leap开发指南(7)-- 眼球追踪(Unity)
  16. 使用 Screen 创建并管理多个 shell
  17. 一个能力和学历的小故事
  18. js刻度尺插件_js滑块刻度尺插件
  19. 一个有经验借鉴的小店铺经营之道
  20. 猿来小课Java视频教程讲师浅谈JAVA体系结构

热门文章

  1. Fastapi学习笔记(一)
  2. 新款 MacBook Pro 评测:更好的性能、免费的刘海和更好的显示器
  3. WringPi库串口通信
  4. 重要性采样(Importance Sampling)详细学习笔记
  5. tomcat8.5集群修改端口,发生临时 dns 错误,请尝试刷新页面
  6. 状压dp、数位dp、概率dp
  7. 学会php想转学java好学吗_Java和PHP哪个好学
  8. Android ROM适配基础
  9. 宏基因组分析步骤Linux,宏基因组分析专题研讨班
  10. 详谈ByteArrayOutputStream的用法