最近在写个性化推荐的论文,经常用到Python来处理数据,被pandas和numpy中的数据选取和索引问题绕的比较迷糊,索性把这篇官方文档翻译出来,方便自查和学习,翻译过程中难免很多不到位的地方,但大致能看懂,错误之处欢迎指正~
Python pandas 0.19.1 Indexing and Selecting Data 原文链接 http://pandas.pydata.org/pandas-docs/stable/indexing.html
数据索引和选取
pandas对象中的轴标签信息有很多作用:
- 使用已知指示来确定数据(即提供元数据),这对于分析、可视化以及交互式控制台的显示都十分重要
- 使能够实现自动和显式的数据对齐
- 允许直观地获取和设置数据集的子集
在这一部分,我们将致力于最终的目的:即如何切片,切丁以及一般地获取和设置pandas对象的子集。文章将主要集中在Series和DataFrame上,因为它们潜力很大。希望未来在高维数据结构(包括panel)上投入更多的精力,尤其是在基于标签的高级索引方面。
提示:Python和bumpy的索引操作[ ]和属性操作. 为pandas数据结构提供了非常快速和简便的方式。如果你已经知道如何操作Python字典和Numpy数组的话,这就没什么新的了。然而,由于数据的类型无法提前预知,直接使用标准操作将会有一些优化的限制。对于产品代码来说,我们建议你可以利用本文展示的优化的pandas数据使用方法。
警告:一个设置操作是会返回一个复制还是一个引用可能取决于具体情况。这种有时被称为“链式赋值”,我们应当避免这种情况。
警告:在0.15.0版本中,与其他pandas对象一样,index不再是ndarray的子类,而是PandasObject的子类。这个影响不大。
多样的索引方法
为了实现更简便的基于位置的索引,对象选取方法添加了一些用户的请求。pandas现在支持三种类型的多轴索引。
- .loc是最基本的基于标签的索引,但是也可以用于布尔数组。当item无法找到时,.loc将会产生KeyError。合法的输入有:
-
- 一个单独的标签,如5或“a”,(注意5是作为索引标签,而不是一个整数的位置索引)
- 一个列表或者数组标签[“a”,”b”,”c”]
- 一个带有标签“a”:“f”的切片对象(注意,与Python切片相反,这种切片的第一个和最后一个都包含在内!)
- 一个布尔数组
- 一个可调用的函数(调用Series, DataFrame或Panel)并返回索引的有效输出(上面中的一个)
- .iloc是最基本的基于整数位置的索引(从轴的第0位到第length-1位),但是也可以用于布尔数组。除了允许超范围索引的索引器之外,如果一个请求的索引超出了索引范围,.iloc将会产生IndexError。合法的输入有:
-
- 一个整数。如5
- 一个列表或整数数组。如[3,0,4]
- 一个整型的切片对象,如1:7
- 一个布尔数组
- 一个可调用的函数(调用Series, DataFrame或Panel)并返回索引的有效输出(上面中的一个)
- .ix支持基于整数和标签的混合索引。它主要是基于标签的,但是除非对应的轴是整数类型,否则它将会回到整数位置进行访问。.ix是最普适的,它能够支持.loc和.iloc的任何输入。.ix还支持浮点型的标签。当处理基于位置和标签的混合的层次索引时.ix特别有用。
然而,基于整数的轴只支持基于标签的索引方式,而不支持基于位置的索引。因此,在此种情况下,使用.iloc或者.loc通常会
更加明确。
- .loc, .iloc, .ix和[ ]索引能够接受一个可调用对象作为索引器。
使用以下标记从一个多轴对象中获取值(使用.loc为例,但同样适用于.iloc和.ix)。任何的轴访问器都可能是空的切片:假定不规范的轴。(如p.loc[‘a’]等价于p.loc[‘a’,:,:])
Object Type |
Indexers |
Series |
s.loc[indexer] |
DataFrame |
df.loc[row_indexer,column_indexer] |
Panel |
p.loc[item_indexer,major_indexer,minor_indexer] |
基础知识
正如在上一章节中介绍数据结构中所提到的那样,使用[ ]进行索引的主要功能(相当于Python中的__getitem__)是选取出低维切片。因此,
对象类型 |
选取 |
返回值类型 |
Series |
series[label] |
标量值 |
DataFrame |
frame[colname] |
对应colname的Series |
Panel |
panel[itemname] |
对应itemname的DataFrame |
这里我们构建了一个简单的时间序列数据集来说明索引功能:
In [1]: dates = pd.date_range('1/1/2000', periods=8)
In [2]: df = pd.DataFrame(np.random.randn(8, 4), index=dates, columns=['A', 'B', 'C', 'D'])
In [3]: df
Out[3]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
In [4]: panel = pd.Panel({'one' : df, 'two' : df - df.mean()})
In [5]: panel
Out[5]:
Dimensions: 2 (items) x 8 (major_axis) x 4 (minor_axis)
Items axis: one to two
Major_axis axis: 2000-01-01 00:00:00 to 2000-01-08 00:00:00
Minor_axis axis: A to D
注意:除非特殊说明,所有的索引功能都是通用的,不只适用于该时间序列。
因此,根据上述,我们使用[ ]能够实现最基本的索引:
In [6]: s = df['A']
In [7]: s[dates[5]]
Out[7]: -0.67368970808837059
In [8]: panel['two']
Out[8]:
A B C D
2000-01-01 0.409571 0.113086 -0.610826 -0.936507
2000-01-02 1.152571 0.222735 1.017442 -0.845111
2000-01-03 -0.921390 -1.708620 0.403304 1.270929
2000-01-04 0.662014 -0.310822 -0.141342 0.470985
2000-01-05 -0.484513 0.962970 1.174465 -0.888276
2000-01-06 -0.733231 0.509598 -0.580194 0.724113
2000-01-07 0.345164 0.972995 -0.816769 -0.840143
2000-01-08 -0.430188 -0.761943 -0.446079 1.044010
你可以向[ ]中传递列的列表来按照顺序选取多列。如果某列不再DataFrame中,将引发一个异常。也可以通过这种方式设置多个列。
In [9]: df
Out[9]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
In [10]: df[['B', 'A']] = df[['A', 'B’]] #交换两个列的值
In [11]: df
Out[11]:
A B C D
2000-01-01 -0.282863 0.469112 -1.509059 -1.135632
2000-01-02 -0.173215 1.212112 0.119209 -1.044236
2000-01-03 -2.104569 -0.861849 -0.494929 1.071804
2000-01-04 -0.706771 0.721555 -1.039575 0.271860
2000-01-05 0.567020 -0.424972 0.276232 -1.087401
2000-01-06 0.113648 -0.673690 -1.478427 0.524988
2000-01-07 0.577046 0.404705 -1.715002 -1.039268
2000-01-08 -1.157892 -0.370647 -1.344312 0.844885
当将这种变换就地应用到列的子集的时候,你可能会发现这个方法的有用之处。
警告:当从.loc, .iloc和.ix设置Series和DataFrame时,pandas会将所有轴对齐。
这不会改变df,因为在赋值之前就进行了列对齐。
In [12]: df[['A', 'B']]
Out[12]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
In [13]: df.loc[:,['B', 'A']] = df[['A', 'B']]#这种方法无法使列A和列B的值对调
In [14]: df[['A', 'B']]
Out[14]:
A B
2000-01-01 -0.282863 0.469112
2000-01-02 -0.173215 1.212112
2000-01-03 -2.104569 -0.861849
2000-01-04 -0.706771 0.721555
2000-01-05 0.567020 -0.424972
2000-01-06 0.113648 -0.673690
2000-01-07 0.577046 0.404705
2000-01-08 -1.157892 -0.370647
正确的做法是使用原始值
In [15]: df.loc[:,['B', 'A']] = df[['A', 'B']].values
In [16]: df[['A', 'B']]
Out[16]:
A B
2000-01-01 0.469112 -0.282863
2000-01-02 1.212112 -0.173215
2000-01-03 -0.861849 -2.104569
2000-01-04 0.721555 -0.706771
2000-01-05 -0.424972 0.567020
2000-01-06 -0.673690 0.113648
2000-01-07 0.404705 0.577046
2000-01-08 -0.370647 -1.157892
属性访问
你或许能够直接把Series的index,一个DataFrame的column,一个Panel的item作为一种属性来访问。
In [17]: sa = pd.Series([1,2,3],index=list('abc'))
In [18]: dfa = df.copy()
In [19]: sa.b #直接使用Series.index来访问数据
Out[19]: 2
In [20]: dfa.A #直接使用DataFrame.column来访问一列
Out[20]:
2000-01-01 0.469112
2000-01-02 1.212112
2000-01-03 -0.861849
2000-01-04 0.721555
2000-01-05 -0.424972
2000-01-06 -0.673690
2000-01-07 0.404705
2000-01-08 -0.370647
Freq: D, Name: A, dtype: float64
In [21]: panel.one #直接使用Panel.item来访问一个item
Out[21]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
你可以使用属性访问来修改一个现存的Series的元素或一个DataFrame的列,但是要小心;如果你试图使用属性访问来创建一个新的列,它会创建一个新的属性而不是一个新的列。
In [22]: sa.a = 5
In [23]: sa
Out[23]:
a 5
b 2
c 3
dtype: int64
In [24]: dfa.A = list(range(len(dfa.index))) # 如果A列已经存在的话这样做是可行的
In [25]: dfa
Out[25]:
A B C D
2000-01-01 0 -0.282863 -1.509059 -1.135632
2000-01-02 1 -0.173215 0.119209 -1.044236
2000-01-03 2 -2.104569 -0.494929 1.071804
2000-01-04 3 -0.706771 -1.039575 0.271860
2000-01-05 4 0.567020 0.276232 -1.087401
2000-01-06 5 0.113648 -1.478427 0.524988
2000-01-07 6 0.577046 -1.715002 -1.039268
2000-01-08 7 -1.157892 -1.344312 0.844885
In [26]: dfa['A'] = list(range(len(dfa.index))) # 使用这种方法创建一个新列
In [27]: dfa
Out[27]:
A B C D
2000-01-01 0 -0.282863 -1.509059 -1.135632
2000-01-02 1 -0.173215 0.119209 -1.044236
2000-01-03 2 -2.104569 -0.494929 1.071804
2000-01-04 3 -0.706771 -1.039575 0.271860
2000-01-05 4 0.567020 0.276232 -1.087401
2000-01-06 5 0.113648 -1.478427 0.524988
2000-01-07 6 0.577046 -1.715002 -1.039268
2000-01-08 7 -1.157892 -1.344312 0.844885
警告:
- 只有当index列是一个有效的Python标识符的时候,你才可以使用这种方式。比如s.1就不可行。
- 如果属性和现存的方法名冲突的话,这种方式也不可行。如s.min
- 同样,如果属性名和任意一个如下的名字冲突的话也不可行:index,major_axis, minor_axis, items, labels
- 无论哪种情况,标准的索引总是可行的,如s[“1”], s[“min”]和s[“index”]都能够访问相应的元素或列。
- 从0.13.0版本开始,Series和Panel都能够通过这种方式访问。
如果你使用的是IPyton的环境,你可能也使用tab-完成键来查看这些访问属性。
你也可以向一个DataFrame的一行分配一个dict。
In [28]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
In [29]: x.iloc[1] = dict(x=9, y=99)
In [30]: x
Out[30]:
x y
0 1 3
1 9 99
2 3 5
切片范围
鲁棒性和一致性最强的沿任意轴的切片方法是在使用位置选择部分详细介绍的.iloc方法。现在,我们介绍一下使用[ ]操作进行切片的语义。
对于Series来说,这个语法对应的就是ndarray,返回的是值的切片和相关的标签:
In [31]: s[:5]
Out[31]:
2000-01-01 0.469112
2000-01-02 1.212112
2000-01-03 -0.861849
2000-01-04 0.721555
2000-01-05 -0.424972
Freq: D, Name: A, dtype: float64
In [32]: s[::2]#步长为2
Out[32]:
2000-01-01 0.469112
2000-01-03 -0.861849
2000-01-05 -0.424972
2000-01-07 0.404705
Freq: 2D, Name: A, dtype: float64
In [33]: s[::-1]#倒序,步长为1
Out[33]:
2000-01-08 -0.370647
2000-01-07 0.404705
2000-01-06 -0.673690
2000-01-05 -0.424972
2000-01-04 0.721555
2000-01-03 -0.861849
2000-01-02 1.212112
2000-01-01 0.469112
Freq: -1D, Name: A, dtype: float64
需要注意的是设置操作也是如此:
In [34]: s2 = s.copy()
In [35]: s2[:5] = 0
In [36]: s2
Out[36]:
2000-01-01 0.000000
2000-01-02 0.000000
2000-01-03 0.000000
2000-01-04 0.000000
2000-01-05 0.000000
2000-01-06 -0.673690
2000-01-07 0.404705
2000-01-08 -0.370647
Freq: D, Name: A, dtype: float64
对于DataFrame来说,在[ ]中的切片是对行的操作。由于它的普适性,所以这样非常方便。
In [37]: df[:3]#取df的前3行
Out[37]:
A B C D
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
In [38]: df[::-1]
Out[38]:
A B C D
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885
2000-01-07 0.404705 0.577046 -1.715002 -1.039268
2000-01-06 -0.673690 0.113648 -1.478427 0.524988
2000-01-05 -0.424972 0.567020 0.276232 -1.087401
2000-01-04 0.721555 -0.706771 -1.039575 0.271860
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804
2000-01-02 1.212112 -0.173215 0.119209 -1.044236
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632
使用标签选择
警告:对于一个设置操作,是返回一个复制还是引用取决于当时的上下文。这叫做“链式赋值”,这种情况应当避免。See Returning a View versus Copy
警告:当你的切片器与索引类型不兼容(或不可转换)时,.loc是非常严格的。例如在一个DatatimeIndex中使用整数的话,将会引发一个TypeError。
In [39]: dfl = pd.DataFrame(np.random.randn(5,4), columns=list('ABCD'), index=pd.date_range('20130101',periods=5))
In [40]: dfl
Out[40]:
A B C D
2013-01-01 1.075770 -0.109050 1.643563 -1.469388
2013-01-02 0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524 0.413738 0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061
2013-01-05 0.895717 0.805244 -1.206412 2.565646
In [4]: dfl.loc[2:3]#因为df1的index的类型是datatimeindex,不能使用整数索引
TypeError: cannot do slice indexing on with these indexers [2] of
在切片中的string能够转换为index的类型,这样才能正常切片。
In [41]: dfl.loc['20130102':'20130104']#这样才可以
Out[41]:
A B C D
2013-01-02 0.357021 -0.674600 -1.776904 -0.968914
2013-01-03 -1.294524 0.413738 0.276662 -0.472035
2013-01-04 -0.013960 -0.362543 -0.006154 -0.923061
pandas提供了一系列的方法来基于标签进行索引。这是一个严格基于包的协议。在你请求的标签中,需要至少有一个是在index中的,不然就会引发一个KeyError。与常规Python语法不通,切片范围的第一个和最后一个都被包含在内。整数是有效的标签,但是他们必须作为标签而不是位置。
.loc属性是基础的访问方法。下面的是有效的输入:
- 一个单独的标签,如5或“a”,(注意5是作为索引标签,而不是一个整数的位置索引)
- 一个列表或者数组标签[“a”,”b”,”c”]
- 一个带有标签“a”:“f”的切片对象(注意,与Python切片相反,这种切片的第一个和最后一个都包含在内!)
- 一个布尔数组
- 一个可调用的函数(调用Series, DataFrame或Panel)并返回索引的有效输出(上面中的一个)
In [42]: s1 = pd.Series(np.random.randn(6),index=list('abcdef'))
In [43]: s1
Out[43]:
a 1.431256
b 1.340309
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [44]: s1.loc['c':]
Out[44]:
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [45]: s1.loc['b']
Out[45]: 1.3403088497993827
要注意的是设置操作也同样适用:
In [46]: s1.loc['c':] = 0
In [47]: s1
Out[47]:
a 1.431256
b 1.340309
c 0.000000
d 0.000000
e 0.000000
f 0.000000
dtype: float64
对于一个DataFrame来说:
In [48]: df1 = pd.DataFrame(np.random.randn(6,4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [49]: df1
Out[49]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
c 1.024180 0.569605 0.875906 -2.211372
d 0.974466 -2.006747 -0.410001 -0.078638
e 0.545952 -1.219217 -1.226825 0.769804
f -1.281247 -0.727707 -0.121306 -0.097883
In [50]: df1.loc[['a', 'b', 'd'], :]
Out[50]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
b 1.130127 -1.436737 -1.413681 1.607920
d 0.974466 -2.006747 -0.410001 -0.078638
通过标签切片访问:
In [51]: df1.loc['d':, 'A':'C']
Out[51]:
A B C
d 0.974466 -2.006747 -0.410001
e 0.545952 -1.219217 -1.226825
f -1.281247 -0.727707 -0.121306
使用一个标签来获取截取部分(相当于df.xs(“a"))
In [52]: df1.loc['a']
Out[52]:
A 0.132003
B -0.827317
C -0.076467
D -1.187678
Name: a, dtype: float64
使用一个布尔型数组获取值
In [53]: df1.loc['a'] > 0
Out[53]:
A True
B False
C False
D False
Name: a, dtype: bool
In [54]: df1.loc[:, df1.loc['a'] > 0]
Out[54]:
A
a 0.132003
b 1.130127
c 1.024180
d 0.974466
e 0.545952
f -1.281247
直接获取值(相当于过时的df.get_value(“a”,”A”))
# this is also equivalent to ``df1.at['a','A']``
In [55]: df1.loc['a', 'A']
Out[55]: 0.13200317033032932
使用位置选择
警告:对于一个设置操作,是返回一个复制还是引用取决于当时的上下文。这叫做“链式赋值”,这种情况应当避免。
pandas提供了一系列的方法来基于整数进行索引。该语法紧跟Python和numpy切片。这些是0-based索引。切片时,范围内的第一个和最后一个都包含在内。如果试图使用非整数,即使是一个有效的标签也将会引发一个IndexError。 See Returning a View versus Copy
.loc属性是基础的访问方法。下面的是有效的输入:
- 一个单独的标签,如5
- 一个列表或者整数数组[4,3,0]
- 一个带有整数的切片对象1:7
- 一个布尔数组
- 一个可调用的函数
In [56]: s1 = pd.Series(np.random.randn(5), index=list(range(0,10,2)))
In [57]: s1
Out[57]:
0 0.695775
2 0.341734
4 0.959726
6 -1.110336
8 -0.619976
dtype: float64
In [58]: s1.iloc[:3]
Out[58]:
0 0.695775
2 0.341734
4 0.959726
dtype: float64
In [59]: s1.iloc[3]#注意,.iloc是针对位置索引,此处的3指的是第“3”个数(即第4个数),而如果使用s1.loc[2]则指的不是第“2”个数,而是index为2的那个数。
Out[59]: -1.1103361028911669
要注意的是设置操作也同样如此:
In [60]: s1.iloc[:3] = 0
In [61]: s1
Out[61]:
0 0.000000
2 0.000000
4 0.000000
6 -1.110336
8 -0.619976
dtype: float64
对于一个DataFrame来说:
In [62]: df1 = pd.DataFrame(np.random.randn(6,4),
....: index=list(range(0,12,2)),
....: columns=list(range(0,8,2)))
....:
In [63]: df1
Out[63]:
0 2 4 6
0 0.149748 -0.732339 0.687738 0.176444
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
6 -0.826591 -0.345352 1.314232 0.690579
8 0.995761 2.396780 0.014871 3.357427
10 -0.317441 -1.236269 0.896171 -0.487602
通过整数切片来选择:
In [64]: df1.iloc[:3]
Out[64]:
0 2 4 6
0 0.149748 -0.732339 0.687738 0.176444
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [65]: df1.iloc[1:5, 2:4]
Out[65]:
4 6
2 0.301624 -2.179861
4 1.462696 -1.743161
6 1.314232 0.690579
8 0.014871 3.357427
通过整数列表来选择:
In [66]: df1.iloc[[1, 3, 5], [1, 3]]
Out[66]:
2 6
2 -0.154951 -2.179861
6 -0.345352 0.690579
10 -1.236269 -0.487602
In [67]: df1.iloc[1:3, :]
Out[67]:
0 2 4 6
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [68]: df1.iloc[:, 1:3]
Out[68]:
2 4
0 -0.732339 0.687738
2 -0.154951 0.301624
4 -0.954208 1.462696
6 -0.345352 1.314232
8 2.396780 0.014871
10 -1.236269 0.896171
# 这相当于df1.iat[1,1]
In [69]: df1.iloc[1, 1]
Out[69]: -0.15495077442490321
使用一个整数位置来获取一个截取部分(相当于df.xs(1))
In [70]: df1.iloc[1]
Out[70]:
0 0.403310
2 -0.154951
4 0.301624
6 -2.179861
Name: 2, dtype: float64
正如在Python或Numpy中那样,超出索引范围的切片也是被允许的。
# 这些在python/numpy中是允许的。
# 在pandas中从0.14.0版本才可行
In [71]: x = list('abcdef')
In [72]: x
Out[72]: ['a', 'b', 'c', 'd', 'e', 'f']
In [73]: x[4:10]
Out[73]: ['e', 'f']
In [74]: x[8:10]
Out[74]: []
In [75]: s = pd.Series(x)
In [76]: s
Out[76]:
0 a
1 b
2 c
3 d
4 e
5 f
dtype: object
In [77]: s.iloc[4:10]
Out[77]:
4 e
5 f
dtype: object
In [78]: s.iloc[8:10]
Out[78]: Series([], dtype: object)
注意:在0.14.0以前的版本中,iloc不能接收超出索引范围的切片,例如超出索引对象长度的值就不可以。
注意到这样做会导致一个空轴(如返回一个空的DataFrame)
In [79]: dfl = pd.DataFrame(np.random.randn(5,2), columns=list('AB'))
In [80]: dfl
Out[80]:
A B
0 -0.082240 -2.182937
1 0.380396 0.084844
2 0.432390 1.519970
3 -0.493662 0.600178
4 0.274230 0.132885
In [81]: dfl.iloc[:, 2:3]
Out[81]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]
In [82]: dfl.iloc[:, 1:3]
Out[82]:
B
0 -2.182937
1 0.084844
2 1.519970
3 0.600178
4 0.132885
In [83]: dfl.iloc[4:6]
Out[83]:
A B
4 0.27423 0.132885
一个单独的超出范围的索引器将会引发IndexError. 一个其元素超出了索引范围的索引列表也会引发IndexError。
dfl.iloc[[4, 5, 6]]
IndexError: positional indexers are out-of-bounds
dfl.iloc[:, 4]
IndexError: single positional indexer is out-of-bounds
使用可调用函数来选择
.loc, .iloc, .ix和[ ]索引都可以接收一个可调用函数作为索引。这个可调用函数必须是有一个参数的函数(Series, DataFrame或者Panel),并为索引返回有效的输出。
In [84]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [85]: df1
Out[85]:
A B C D
a -0.023688 2.410179 1.450520 0.206053
b -0.251905 -2.213588 1.063327 1.266143
c 0.299368 -0.863838 0.408204 -1.048089
d -0.025747 -0.988387 0.094055 1.262731
e 1.289997 0.082423 -0.055758 0.536580
f -0.489682 0.369374 -0.034571 -2.484478
In [86]: df1.loc[lambda df: df.A > 0, :]#A列大于0的行,所有列
Out[86]:
A B C D
c 0.299368 -0.863838 0.408204 -1.048089
e 1.289997 0.082423 -0.055758 0.536580
In [87]: df1.loc[:, lambda df: ['A', 'B']]#所有行,A,B两列
Out[87]:
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
In [88]: df1.iloc[:, lambda df: [0, 1]]#所有行,第0和第1列
Out[88]:
A B
a -0.023688 2.410179
b -0.251905 -2.213588
c 0.299368 -0.863838
d -0.025747 -0.988387
e 1.289997 0.082423
f -0.489682 0.369374
In [89]: df1[lambda df: df.columns[0]]所有行的第0列
Out[89]:
a -0.023688
b -0.251905
c 0.299368
d -0.025747
e 1.289997
f -0.489682
Name: A, dtype: float64
你可以在Series中使用可调用函数
In [90]: df1.A.loc[lambda s: s > 0]#A列,大于0的行
Out[90]:
c 0.299368
e 1.289997
Name: A, dtype: float64
使用这种方法/索引时,你可以不用临时变量就能够进行数据选择操作。
In [91]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [92]: (bb.groupby(['year', 'team']).sum()
....: .loc[lambda df: df.r > 100])
....:
Out[92]:
stint g ab r h X2b X3b hr rbi sb cs bb \
year team
2007 CIN 6 379 745 101 203 35 2 36 125.0 10.0 1.0 105
DET 5 301 1062 162 283 54 4 37 144.0 24.0 7.0 97
HOU 4 311 926 109 218 47 6 14 77.0 10.0 4.0 60
LAN 11 413 1021 153 293 61 3 36 154.0 7.0 5.0 114
NYN 13 622 1854 240 509 101 3 61 243.0 22.0 4.0 174
SFN 5 482 1305 198 337 67 6 40 171.0 26.0 7.0 235
TEX 2 198 729 115 200 40 4 28 115.0 21.0 4.0 73
TOR 4 459 1408 187 378 96 2 58 223.0 4.0 2.0 190
so ibb hbp sh sf gidp
year team
2007 CIN 127.0 14.0 1.0 1.0 15.0 18.0
DET 176.0 3.0 10.0 4.0 8.0 28.0
HOU 212.0 3.0 9.0 16.0 6.0 17.0
LAN 141.0 8.0 9.0 3.0 8.0 29.0
NYN 310.0 24.0 23.0 18.0 15.0 48.0
SFN 188.0 51.0 8.0 16.0 6.0 41.0
TEX 140.0 4.0 5.0 2.0 8.0 16.0
TOR 265.0 16.0 12.0 4.0 16.0 38.0
随机样本的选择
从一个Series或DataFrame或Panel的行或列中使用sample()方法来选择随机样本。这个方法默认是对列进行取样,并接收一个特定的行/列返回,或者行的一部分。
In [93]: s = pd.Series([0,1,2,3,4,5])
#当没有任何参数传进去的时候,返回1行
In [94]: s.sample()
Out[94]:
4 4
dtype: int64
# 还可以传入一个参数n来指定行数:
In [95]: s.sample(n=3)
Out[95]:
0 0
4 4
1 1
dtype: int64
# 或者行数的百分比
In [96]: s.sample(frac=0.5)
Out[96]:
5 5
3 3
1 1
dtype: int64
默认情况下,sample方法将会使每行最多返回一次,但是你也可以使用替换选项进行替换:
In [97]: s = pd.Series([0,1,2,3,4,5])
# 不使用替换(默认):
In [98]: s.sample(n=6, replace=False)
Out[98]:
0 0
1 1
5 5
3 3
2 2
4 4
dtype: int64
# 使用替换:
In [99]: s.sample(n=6, replace=True)
Out[99]:
0 0
4 4
3 3
2 2
4 4
4 4
dtype: int64
默认情况下,每行被取样的概率是相等的,但是如果你想让每行有不同的概率被抽到,你可以想sample传入weights关键字来设置抽样权重。这些权重可以是一个列表,一个numpy数组或者一个Series,但是他们的长度必须和你抽样的对象的长度一致。缺失值的权重将被设为0,inf值不被允许。如果所有权重的和不为1,他们将使用各权重除以目前权重的和进行重新归一化。例如:
In [100]: s = pd.Series([0,1,2,3,4,5])
In [101]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4]
In [102]: s.sample(n=3, weights=example_weights)
Out[102]:
5 5
4 4
3 3
dtype: int64
# 权重将会自动重新归一化
In [103]: example_weights2 = [0.5, 0, 0, 0, 0, 0]
In [104]: s.sample(n=1, weights=example_weights2)
Out[104]:
0 0
dtype: int64
对于一个DataFrame来说,你可以使用一个DataFrame的一列作为抽样权重(加入你正在对行进行抽样而不是列),把列名作为一个字符串穿进去就可以。
In [105]: df2 = pd.DataFrame({'col1':[9,8,7,6], 'weight_column':[0.5, 0.4, 0.1, 0]})
In [106]: df2.sample(n = 3, weights = 'weight_column')
Out[106]:
col1 weight_column
1 8 0.4
0 9 0.5
2 7 0.1
sample还允许用户使用轴参数来对列进行抽样.
In [107]: df3 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
In [108]: df3.sample(n=1, axis=1)
Out[108]:
col1
0 1
1 2
2 3
最后,你还可以使用random_state参数来为sample的随机数生成器设置一个种子,它将会接收一个整数或者一个numpy RandomState 对象。
In [109]: df4 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
# 如果给定了一个种子,样本将会始终抽取同样的几行
In [110]: df4.sample(n=2, random_state=2)
Out[110]:
col1 col2
2 3 4
1 2 3
In [111]: df4.sample(n=2, random_state=2)
Out[111]:
col1 col2
2 3 4
1 2 3
使用扩展来设置
当为一个轴设置一个不存在的键值时,.loc/.ix/[ ]操作可以进行扩容。
在Series中,这其实是一个追加操作。
In [112]: se = pd.Series([1,2,3])
In [113]: se
Out[113]:
0 1
1 2
2 3
dtype: int64
In [114]: se[5] = 5.
In [115]: se
Out[115]:
0 1.0
1 2.0
2 3.0
5 5.0
dtype: float64
一个DataFrame可以通过.loc或轴进行扩容
In [116]: dfi = pd.DataFrame(np.arange(6).reshape(3,2),
.....: columns=['A','B'])
.....:
In [117]: dfi
Out[117]:
A B
0 0 1
1 2 3
2 4 5
In [118]: dfi.loc[:,'C'] = dfi.loc[:,'A']
In [119]: dfi
Out[119]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
In [120]: dfi.loc[3] = 5
In [121]: dfi
Out[121]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
3 5 5 5
标量值的快速获取和设置
由于使用[ ]进行索引必须管理很多情况(单标签访问,切片,布尔索引等等),它会花费一些性能来识别你究竟请求的是什么。如果你想要访问一个标量,最快速的方法是使用at和iat方法,他们能够适用于所有数据结构。
at提供基于标签的标量查找(与loc类似),而iat提供基于整数的标量查找(与iloc类似)。
In [122]: s.iat[5]
Out[122]: 5
In [123]: df.at[dates[5], 'A']
Out[123]: -0.67368970808837059
In [124]: df.iat[3, 0]
Out[124]: 0.72155516224436689
你也可以使用同样的索引进行设置
In [125]: df.at[dates[5], 'E'] = 7
In [126]: df.iat[3, 0] = 7
如果索引缺失的话,at方法可对对象进行原地扩充
In [127]: df.at[dates[-1]+1, 0] = 7
In [128]: df
Out[128]:
A B C D E 0
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632 NaN NaN
2000-01-02 1.212112 -0.173215 0.119209 -1.044236 NaN NaN
2000-01-03 -0.861849 -2.104569 -0.494929 1.071804 NaN NaN
2000-01-04 7.000000 -0.706771 -1.039575 0.271860 NaN NaN
2000-01-05 -0.424972 0.567020 0.276232 -1.087401 NaN NaN
2000-01-06 -0.673690 0.113648 -1.478427 0.524988 7.0 NaN
2000-01-07 0.404705 0.577046 -1.715002 -1.039268 NaN NaN
2000-01-08 -0.370647 -1.157892 -1.344312 0.844885 NaN NaN
2000-01-09 NaN NaN NaN NaN NaN 7.0
布尔索引
另一个常见的操作是使用布尔向量来过滤数据。操作是|对应或,&对应与,~对应非。这些必须使用括号进行分组。
使用一个布尔向量来对一个Series进行索引与对一个numpy的多维数组进行索引是一样一样的:
In [129]: s = pd.Series(range(-3, 4))
In [130]: s
Out[130]:
0 -3
1 -2
2 -1
3 0
4 1
5 2
6 3
dtype: int64
In [131]: s[s > 0]
Out[131]:
4 1
5 2
6 3
dtype: int64
In [132]: s[(s < -1) | (s > 0.5)]
Out[132]:
0 -3
1 -2
4 1
5 2
6 3
dtype: int64
In [133]: s[~(s < 0)]
Out[133]:
3 0
4 1
5 2
6 3
dtype: int64
你可以使用一个与DataFrame的index相同长度的布尔向量从一个DataFrame中选择列(例如,一些来自DataFrame的一列的东西)
In [134]: df[df['A'] > 0]
Out[134]:
A B C D E 0
2000-01-01 0.469112 -0.282863 -1.509059 -1.135632 NaN NaN
2000-01-02 1.212112 -0.173215 0.119209 -1.044236 NaN NaN
2000-01-04 7.000000 -0.706771 -1.039575 0.271860 NaN NaN
2000-01-07 0.404705 0.577046 -1.715002 -1.039268 NaN NaN
Series的list和map方法也可以用来产生更为复杂的匹配标准:
In [135]: df2 = pd.DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
.....: 'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
.....: 'c' : np.random.randn(7)})
.....:
# 只想要'two' 或 'three'
In [136]: criterion = df2['a'].map(lambda x: x.startswith('t'))
In [137]: df2[criterion]
Out[137]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# 实现同样的效果,但是更慢的方法
In [138]: df2[[x.startswith('t') for x in df2['a']]]
Out[138]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# 多准则匹配
In [139]: df2[criterion & (df2['b'] == 'x')]
Out[139]:
a b c
3 three x 0.361719
注意,使用布尔向量和其他索引表达式共同索引时,使用选择方法 通过标签选择,通过位置选择和先进索引,你可能会选出不只一个轴的数据。
In [140]: df2.loc[criterion & (df2['b'] == 'x'),'b':'c']
Out[140]:
b c
3 x 0.361719
使用isin索引
考虑到Series的isin方法,它能够返回一个布尔向量,Series的元素在传递列表的地方显示为True。这使你能够选择那些一列或多列中有你需要的值的行。
In [141]: s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64')
In [142]: s
Out[142]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [143]: s.isin([2, 4, 6])
Out[143]:
4 False
3 False
2 True
1 False
0 True
dtype: bool
In [144]: s[s.isin([2, 4, 6])]
Out[144]:
2 2
0 4
dtype: int64
该方法同样适用于Index对象,当你不知道那个标签是真的存在的时候,这种方法也同样适用。
In [145]: s[s.index.isin([2, 4, 6])]
Out[145]:
4 0
2 2
dtype: int64
# 将它与下面的作对比
In [146]: s[[2, 4, 6]]
Out[146]:
2 2.0
4 0.0
6 NaN
dtype: float64
另外,MultiIndex方法能够允许选取一个单独的level来用于成员资格审查
In [147]: s_mi = pd.Series(np.arange(6),
.....: index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']]))
.....:
In [148]: s_mi
Out[148]:
0 a 0
b 1
c 2
1 a 3
b 4
c 5
dtype: int64
In [149]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])]
Out[149]:
0 c 2
1 a 3
dtype: int64
In [150]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)]
Out[150]:
0 a 0
c 2
1 a 3
c 5
dtype: int64
DataFrame也有isin方法。当使用isin时,需要传入一个值的集合,数组或字典都可以。如果是数组,isin返回一个布尔型的DataFrame,它和原DataFrame的shape一样,而且元素在值序列中的地方显示为True.
In [151]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
.....: 'ids2': ['a', 'n', 'c', 'n']})
.....:
In [152]: values = ['a', 'b', 1, 3]
In [153]: df.isin(values)
Out[153]:
ids ids2 vals
0 True True True
1 True False False
2 False False True
3 False False False
通常情况下,你会想要使用特定的列来匹配特定的值。只需要使值成为一个dict,其key是列,value是你想要检索的item列表即可。
In [154]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [155]: df.isin(values)
Out[155]:
ids ids2 vals
0 True False True
1 True False False
2 False False True
3 False False False
将DataFrame的isin方法和any( )和all( )方法混合起来以一个给定的标准快速选择你的数据的子集。选择一行数据,它的每一列都有各自的标准:
In [156]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [157]: row_mask = df.isin(values).all(1)
In [158]: df[row_mask]
Out[158]:
ids ids2 vals
0 a a 1
where( )方法和伪装
使用一个布尔向量从一个Series中选取值通常会返回一个数据的子集。为了保证选取的输出与源数据有相同的规模,你可以使用Series和DataFrame中的where方法。
只返回选取的行:
In [159]: s[s > 0]
Out[159]:
3 1
2 2
1 3
0 4
dtype: int64
返回一个与源数据具有相同规模的Series
In [160]: s.where(s > 0)
Out[160]:
4 NaN
3 1.0
2 2.0
1 3.0
0 4.0
dtype: float64
使用一个布尔标准从一个DataFrame中选取值也能保留输入数据的规模。在底层使用where作为实现。等价于df.where(df<0)
In [161]: df[df < 0]
Out[161]:
A B C D
2000-01-01 -2.104139 -1.309525 NaN NaN
2000-01-02 -0.352480 NaN -1.192319 NaN
2000-01-03 -0.864883 NaN -0.227870 NaN
2000-01-04 NaN -1.222082 NaN -1.233203
2000-01-05 NaN -0.605656 -1.169184 NaN
2000-01-06 NaN -0.948458 NaN -0.684718
2000-01-07 -2.670153 -0.114722 NaN -0.048048
2000-01-08 NaN NaN -0.048788 -0.808838
另外,在返回的复制数据中,where需要一个可选的其他参数来当条件为假时进行替换。
In [162]: df.where(df < 0, -df)
Out[162]:
A B C D
2000-01-01 -2.104139 -1.309525 -0.485855 -0.245166
2000-01-02 -0.352480 -0.390389 -1.192319 -1.655824
2000-01-03 -0.864883 -0.299674 -0.227870 -0.281059
2000-01-04 -0.846958 -1.222082 -0.600705 -1.233203
2000-01-05 -0.669692 -0.605656 -1.169184 -0.342416
2000-01-06 -0.868584 -0.948458 -2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 -0.168904 -0.048048
2000-01-08 -0.801196 -1.392071 -0.048788 -0.808838
你或许想要基于一些布尔标准来设置值。这能够直观地这样做:
In [163]: s2 = s.copy()
In [164]: s2[s2 < 0] = 0
In [165]: s2
Out[165]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [166]: df2 = df.copy()
In [167]: df2[df2 < 0] = 0
In [168]: df2
Out[168]:
A B C D
2000-01-01 0.000000 0.000000 0.485855 0.245166
2000-01-02 0.000000 0.390389 0.000000 1.655824
2000-01-03 0.000000 0.299674 0.000000 0.281059
2000-01-04 0.846958 0.000000 0.600705 0.000000
2000-01-05 0.669692 0.000000 0.000000 0.342416
2000-01-06 0.868584 0.000000 2.297780 0.000000
2000-01-07 0.000000 0.000000 0.168904 0.000000
2000-01-08 0.801196 1.392071 0.000000 0.000000
默认情况下,where将会返回一个数据的修改副本。有一个可选择的参数inplace,它能够在源数据上直接修改,而不产生一个副本。
In [169]: df_orig = df.copy()
In [170]: df_orig.where(df > 0, -df, inplace=True);
In [171]: df_orig
Out[171]:
A B C D
2000-01-01 2.104139 1.309525 0.485855 0.245166
2000-01-02 0.352480 0.390389 1.192319 1.655824
2000-01-03 0.864883 0.299674 0.227870 0.281059
2000-01-04 0.846958 1.222082 0.600705 1.233203
2000-01-05 0.669692 0.605656 1.169184 0.342416
2000-01-06 0.868584 0.948458 2.297780 0.684718
2000-01-07 2.670153 0.114722 0.168904 0.048048
2000-01-08 0.801196 1.392071 0.048788 0.808838
注意 DataFrame.where()和numpy.where()的识别标志
不同。大体上,df1.where(m, df2)
等价于np.where(m, df1, df2)
.
In [172]: df.where(df < 0, -df) == np.where(df < 0, df, -df)
Out[172]:
A B C D
2000-01-01 True True True True
2000-01-02 True True True True
2000-01-03 True True True True
2000-01-04 True True True True
2000-01-05 True True True True
2000-01-06 True True True True
2000-01-07 True True True True
2000-01-08 True True True True
校准
与此同时,where使输入的环境对齐(ndarray 或DataFrame),因此部分选择与设置是可能的。这与使用.ix进行部分设置相似(但是在内容上倒不如轴标签)
In [173]: df2 = df.copy()
In [174]: df2[ df2[1:4] > 0 ] = 3
In [175]: df2
Out[175]:
A B C D
2000-01-01 -2.104139 -1.309525 0.485855 0.245166
2000-01-02 -0.352480 3.000000 -1.192319 3.000000
2000-01-03 -0.864883 3.000000 -0.227870 3.000000
2000-01-04 3.000000 -1.222082 3.000000 -1.233203
2000-01-05 0.669692 -0.605656 -1.169184 0.342416
2000-01-06 0.868584 -0.948458 2.297780 -0.684718
2000-01-07 -2.670153 -0.114722 0.168904 -0.048048
2000-01-08 0.801196 1.392071 -0.048788 -0.808838
在使用where时,where也可以接收axis和level参数来使输入对齐。
In [176]: df2 = df.copy()
In [177]: df2.where(df2>0,df2['A'],axis='index')
Out[177]:
A B C D
2000-01-01 -2.104139 -2.104139 0.485855 0.245166
2000-01-02 -0.352480 0.390389 -0.352480 1.655824
2000-01-03 -0.864883 0.299674 -0.864883 0.281059
2000-01-04 0.846958 0.846958 0.600705 0.846958
2000-01-05 0.669692 0.669692 0.669692 0.342416
2000-01-06 0.868584 0.868584 2.297780 0.868584
2000-01-07 -2.670153 -2.670153 0.168904 -2.670153
2000-01-08 0.801196 1.392071 0.801196 0.801196
这个方法与下面的方法相同,但是比下面的快。
In [178]: df2 = df.copy()
In [179]: df.apply(lambda x, y: x.where(x>0,y), y=df['A'])
Out[179]:
A B C D
2000-01-01 -2.104139 -2.104139 0.485855 0.245166
2000-01-02 -0.352480 0.390389 -0.352480 1.655824
2000-01-03 -0.864883 0.299674 -0.864883 0.281059
2000-01-04 0.846958 0.846958 0.600705 0.846958
2000-01-05 0.669692 0.669692 0.669692 0.342416
2000-01-06 0.868584 0.868584 2.297780 0.868584
2000-01-07 -2.670153 -2.670153 0.168904 -2.670153
2000-01-08 0.801196 1.392071 0.801196 0.801196
where能够接收一个可调用函数作为条件和其他参数。这个函数必须有一个参数(Series 或者 DataFrame),并返回有效的输出作为条件或其他参数。
In [180]: df3 = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [4, 5, 6],
.....: 'C': [7, 8, 9]})
.....:
In [181]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[181]:
A B C
0 11 14 7
1 12 5 8
2 13 6 9
伪装
伪装是where的逆布尔运算。
In [182]: s.mask(s >= 0)
Out[182]:
4 NaN
3 NaN
2 NaN
1 NaN
0 NaN
dtype: float64
In [183]: df.mask(df >= 0)
Out[183]:
A B C D
2000-01-01 -2.104139 -1.309525 NaN NaN
2000-01-02 -0.352480 NaN -1.192319 NaN
2000-01-03 -0.864883 NaN -0.227870 NaN
2000-01-04 NaN -1.222082 NaN -1.233203
2000-01-05 NaN -0.605656 -1.169184 NaN
2000-01-06 NaN -0.948458 NaN -0.684718
2000-01-07 -2.670153 -0.114722 NaN -0.048048
2000-01-08 NaN NaN -0.048788 -0.808838
query()
方法 (实验性的)
DataFrame
对象有一个query()方法能够允许使用一个表达式来选取数据。
你可以获取到frame中列b中介于列a和列c之间的值,例如:
In [184]: n = 10
In [185]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [186]: df
Out[186]:
a b c
0 0.438921 0.118680 0.863670
1 0.138138 0.577363 0.686602
2 0.595307 0.564592 0.520630
3 0.913052 0.926075 0.616184
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
6 0.792342 0.216974 0.564056
7 0.397890 0.454131 0.915716
8 0.074315 0.437913 0.019794
9 0.559209 0.502065 0.026437
# 纯Python
In [187]: df[(df.a < df.b) & (df.b < df.c)]
Out[187]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
# query
In [188]: df.query('(a < b) & (b < c)')
Out[188]:
a b c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
如果没有名为a的列,这样做的话将会把操作作用于一个命了名的index。
In [189]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [190]: df.index.name = 'a'
In [191]: df
Out[191]:
b c
a
0 0 4
1 0 1
2 3 4
3 4 3
4 1 4
5 0 3
6 0 1
7 3 4
8 2 3
9 1 1
In [192]: df.query('a < b and b < c')
Out[192]:
b c
a
2 3 4
如果你不想或不能为你的index命名,你可以在你的query表达式中使用“index”这个名字。
In [193]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [194]: df
Out[194]:
b c
0 3 1
1 3 0
2 5 6
3 5 2
4 7 4
5 0 1
6 2 5
7 0 1
8 6 0
9 7 9
In [195]: df.query('index < b < c')
Out[195]:
b c
2 5 6
注意 如果你的index的名字和一个列名相同,那列名将会被赋予优先值。如
In [196]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [197]: df.index.name = 'a'
In [198]: df.query('a > 2') # 使用的是列a,而不是index
Out[198]:
a
a
1 3
3 3
即使index已经命名了,你还是可以在query表达式中使用“index”这个名字对index列进行使用:
In [199]: df.query('index > 2')
Out[199]:
a
a
3 3
4 2
如果由于某些原因你有一列名为index,那么你可以使用ilevel_0来使用index,但是这时你最好应该考虑给你的列换个名字。
MultiIndex
的query()
语法
你也可以把 MultiIndex
和一个DataFrame的levels当做frame中的列进行使用。
In [200]: n = 10
In [201]: colors = np.random.choice(['red', 'green'], size=n)
In [202]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [203]: colors
Out[203]:
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
'green', 'green'],
dtype='|S5')
In [204]: foods
Out[204]:
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
'eggs'],
dtype='|S4')
In [205]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [206]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [207]: df
Out[207]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [208]: df.query('color == "red"')
Out[208]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
如果 MultiIndex的levels未命名,你可以使用特殊名字来使用它们:
In [209]: df.index.names = [None, None]
In [210]: df
Out[210]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
green eggs -0.748199 1.318931
eggs -2.029766 0.792652
ham 0.461007 -0.542749
ham -0.305384 -0.479195
eggs 0.095031 -0.270099
eggs -0.707140 -0.773882
eggs 0.229453 0.304418
In [211]: df.query('ilevel_0 == "red"')
Out[211]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
惯例是level_0,它意味着为index的第0level“索引level0”
query()
使用示例
一个query()的使用示例是,当你有一个有着共同列名(或索引level/名称)的子集DataFrame的对象集,你可以向所有frame传递相同的query,而不用指定哪个frame是你要查询的。
In [212]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [213]: df
Out[213]:
a b c
0 0.224283 0.736107 0.139168
1 0.302827 0.657803 0.713897
2 0.611185 0.136624 0.984960
3 0.195246 0.123436 0.627712
4 0.618673 0.371660 0.047902
5 0.480088 0.062993 0.185760
6 0.568018 0.483467 0.445289
7 0.309040 0.274580 0.587101
8 0.258993 0.477769 0.370255
9 0.550459 0.840870 0.304611
In [214]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [215]: df2
Out[215]:
a b c
0 0.357579 0.229800 0.596001
1 0.309059 0.957923 0.965663
2 0.123102 0.336914 0.318616
3 0.526506 0.323321 0.860813
4 0.518736 0.486514 0.384724
5 0.190804 0.505723 0.614533
6 0.891939 0.623977 0.676639
7 0.480559 0.378528 0.460858
8 0.420223 0.136404 0.141295
9 0.732206 0.419540 0.604675
10 0.604466 0.848974 0.896165
11 0.589168 0.920046 0.732716
In [216]: expr = '0.0 <= a <= c <= 0.5'
In [217]: map(lambda frame: frame.query(expr), [df, df2])
Out[217]:
[ a b c
8 0.258993 0.477769 0.370255, a b c
2 0.123102 0.336914 0.318616]
query()
Python与pandas语法比较
完全的numpy形式的语法
In [218]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [219]: df
Out[219]:
a b c
0 7 8 9
1 1 0 7
2 2 7 2
3 6 2 2
4 2 6 3
5 3 8 2
6 1 7 2
7 5 1 5
8 9 8 0
9 1 5 0
In [220]: df.query('(a < b) & (b < c)')
Out[220]:
a b c
0 7 8 9
In [221]: df[(df.a < df.b) & (df.b < df.c)]
Out[221]:
a b c
0 7 8 9
去掉括号会更好一点 (通过绑定比较操作符&
/|
)
In [222]: df.query('a < b & b < c')
Out[222]:
a b c
0 7 8 9
使用英语来代替符号
In [223]: df.query('a < b and b < c')
Out[223]:
a b c
0 7 8 9
可能和你在纸上写的非常相近
In [224]: df.query('a < b < c')
Out[224]:
a b c
0 7 8 9
in
和 not in
操作
在比较操作中,query()也支持Python的特殊用法in和not in,为调用isin方法提供了一个简洁的语法。
#获取列a和列b中有重复值的所有行
In [225]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....: 'c': np.random.randint(5, size=12),
.....: 'd': np.random.randint(9, size=12)})
.....:
In [226]: df
Out[226]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [227]: df.query('a in b')
Out[227]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
# 纯Python语法的写法
In [228]: df[df.a.isin(df.b)]
Out[228]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
In [229]: df.query('a not in b')
Out[229]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# 纯Python
In [230]: df[~df.a.isin(df.b)]
Out[230]:
a b c d
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
你可以将这种方法和其他表达式混合来实现非常简洁的查询:
# 列a和列b有重复值且列c的值小于列d的值的所有行
In [231]: df.query('a in b and c < d')
Out[231]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
# 纯Python
In [232]: df[df.b.isin(df.a) & (df.c < df.d)]
Out[232]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
10 f c 0 6
11 f c 1 2
注意:in和not in在Python中进行了评估,因为numexpr没有与该操作相等的操作。然而,只有表达式中的in和not in自身在普通Python中被评估了。例如,在表达式
df.query('a in b + c + d')
(b + c + d)通过numexpr进行评估,然后in操作就在普通Python评估了。通常来说,任何能够使用numexpr评估的操作都是这样。
==
操作和list对象的特殊用法
使用==/!=对一列数值进行比较和in/not in的机制是相似的
In [233]: df.query('b == ["a", "b", "c"]')
Out[233]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
# 纯Python
In [234]: df[df.b.isin(["a", "b", "c"])]
Out[234]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
6 d b 3 3
7 d b 2 1
8 e c 4 3
9 e c 2 0
10 f c 0 6
11 f c 1 2
In [235]: df.query('c == [1, 2]')
Out[235]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [236]: df.query('c != [1, 2]')
Out[236]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# 使用 in/not in
In [237]: df.query('[1, 2] in c')
Out[237]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
In [238]: df.query('[1, 2] not in c')
Out[238]:
a b c d
1 a a 4 7
4 c b 3 6
5 c b 0 2
6 d b 3 3
8 e c 4 3
10 f c 0 6
# 纯 Python
In [239]: df[df.c.isin([1, 2])]
Out[239]:
a b c d
0 a a 2 6
2 b a 1 6
3 b a 2 1
7 d b 2 1
9 e c 2 0
11 f c 1 2
布尔操作
你可以使用not或~操作来否定布尔表达式
In [240]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [241]: df['bools'] = np.random.rand(len(df)) > 0.5
In [242]: df.query('~bools')
Out[242]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [243]: df.query('not bools')
Out[243]:
a b c bools
2 0.697753 0.212799 0.329209 False
7 0.275396 0.691034 0.826619 False
8 0.190649 0.558748 0.262467 False
In [244]: df.query('not bools') == df[~df.bools]
Out[244]:
a b c bools
2 True True True True
7 True True True True
8 True True True True
当然,表达式也可以变得任意复杂
# 剪短的查询语法
In [245]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# 相等的纯Python方法
In [246]: longer = df[(df.a < df.b) & (df.b < df.c) & (~df.bools) | (df.bools > 2)]
In [247]: shorter
Out[247]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [248]: longer
Out[248]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [249]: shorter == longer
Out[249]:
a b c bools
7 True True True True
query()的性能
对于大型frame来说,DataFrame.query()
使用 numexpr比Python快一些
注意 当你的frame超过200,000行时DataFrame.query()的快速才能体现出来
这个图表是使用numpy.random.randn()生成的3列浮点值生成的
.
重复数据
如果你想要识别并删除一个DataFrame中的重复行,有两种方法:duplicated和
drop_duplicates。每种都需要传入一个列参数来识别重复行。
- duplicated返回一个布尔向量,该向量的长度是行数,且它会指出每一行是不是重复行
- drop_duplicates会删除重复行
默认情况下,几个重复行的第一行会被留下,但是每种方法都有一个keep参数来决定要留下哪一行。
keep='first'
(默认): 将第一行视为非重复行并留下
keep='last'
:将最后一行视为非重复行并留下
keep=False
: 删除所有行/将所有行都标记为重复行
In [250]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],
.....: 'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'],
.....: 'c': np.random.randn(7)})
.....:
In [251]: df2
Out[251]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [252]: df2.duplicated('a')
Out[252]:
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
In [253]: df2.duplicated('a', keep='last')
Out[253]:
0 True
1 False
2 True
3 True
4 False
5 False
6 False
dtype: bool
In [254]: df2.duplicated('a', keep=False)
Out[254]:
0 True
1 True
2 True
3 True
4 True
5 False
6 False
dtype: bool
In [255]: df2.drop_duplicates('a')
Out[255]:
a b c
0 one x -1.067137
2 two x -0.211056
5 three x -1.964475
6 four x 1.298329
In [256]: df2.drop_duplicates('a', keep='last')
Out[256]:
a b c
1 one y 0.309500
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [257]: df2.drop_duplicates('a', keep=False)
Out[257]:
a b c
5 three x -1.964475
6 four x 1.298329
同样,你可以传入一个列来识别特定重复行
In [258]: df2.duplicated(['a', 'b'])
Out[258]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
dtype: bool
In [259]: df2.drop_duplicates(['a', 'b'])
Out[259]:
a b c
0 one x -1.067137
1 one y 0.309500
2 two x -0.211056
3 two y -1.842023
5 three x -1.964475
6 four x 1.298329
使用index.duplicated然后进行切片可以根据index的值去重。keep参数在这个方法中同样适用。
In [260]: df3 = pd.DataFrame({'a': np.arange(6),
.....: 'b': np.random.randn(6)},
.....: index=['a', 'a', 'b', 'c', 'b', 'a'])
.....:
In [261]: df3
Out[261]:
a b
a 0 1.440455
a 1 2.456086
b 2 1.038402
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [262]: df3.index.duplicated()
Out[262]: array([False, True, False, False, True, True], dtype=bool)
In [263]: df3[~df3.index.duplicated()]
Out[263]:
a b
a 0 1.440455
b 2 1.038402
c 3 -0.894409
In [264]: df3[~df3.index.duplicated(keep='last')]
Out[264]:
a b
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [265]: df3[~df3.index.duplicated(keep=False)]
Out[265]:
a b
c 3 -0.894409
类似于字典的 get()
方法
Series, DataFrame, 和 Panel都有一个get方法能够返回一个默认值
In [266]: s = pd.Series([1,2,3], index=['a','b','c'])
In [267]: s.get('a') #等价于s['a']
Out[267]: 1
In [268]: s.get('x', default=-1)
Out[268]: -1
select()
方法
另一个从一个Series, DataFrame, 或 Panel对象中获取切片的方式是select方法。这个方法应该在只有当没有其他直接的方式可用时才用。select能够作用于轴标签并返回一个布尔值。例如:
In [269]: df.select(lambda x: x == 'A', axis=1)
Out[269]:
A
2000-01-01 0.355794
2000-01-02 1.635763
2000-01-03 0.854409
2000-01-04 -0.216659
2000-01-05 2.414688
2000-01-06 -1.206215
2000-01-07 0.779461
2000-01-08 -0.878999
lookup()
方法
有些时候你想要按照某种特定顺序来获取行或列,那么lookup方法就能够实现,并返回一个numpy数组。例如,
In [270]: dflookup = pd.DataFrame(np.random.rand(20,4), columns = ['A','B','C','D'])
In [271]: dflookup.lookup(list(range(0,10,2)), ['B','C','A','B','D'])
Out[271]: array([ 0.3506, 0.4779, 0.4825, 0.9197, 0.5019])
索引对象
pandas的Index类和它的子类可以被视为实施一个有序的多集。允许重复。然而,如果你试图将一个有重复项的索引对象转换为一个集合,将会引发一个异常。
Index
还为查找、数据规整和重新索引提供了必要的基础。创建一个Index的最简单的方式是向Index传递一个list或其他的序列。
In [272]: index = pd.Index(['e', 'd', 'a', 'b'])
In [273]: index
Out[273]: Index([u'e', u'd', u'a', u'b'], dtype='object')
In [274]: 'd' in index
Out[274]: True
你也可以给index起个名字,并存储在索引中:
In [275]: index = pd.Index(['e', 'd', 'a', 'b'], name='something')
In [276]: index.name
Out[276]: ‘something'
如果名字是个集合,将会在控制台显示:
In [277]: index = pd.Index(list(range(5)), name='rows')
In [278]: columns = pd.Index(['A', 'B', 'C'], name='cols')
In [279]: df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns)
In [280]: df
Out[280]:
cols A B C
rows
0 1.295989 0.185778 0.436259
1 0.678101 0.311369 -0.528378
2 -0.674808 -1.103529 -0.656157
3 1.889957 2.076651 -1.102192
4 -1.211795 -0.791746 0.634724
In [281]: df['A']
Out[281]:
rows
0 1.295989
1 0.678101
2 -0.674808
3 1.889957
4 -1.211795
Name: A, dtype: float64
设置元数据
索引“大部分是不可变的”,但是设置和改变他们的元数据却是可能的,比如索引名(或者,对于MultiIndex来说,level和标签)
你可以使用rename,set_name ,set_levels set_labels来直接设置这些属性。它们默认返回一个副本,然而,你可以令关键字inplace=True来使数据直接原地改变。
MultiIndexes的 Advanced Indexing 用法。
In [282]: ind = pd.Index([1, 2, 3])
In [283]: ind.rename("apple")
Out[283]: Int64Index([1, 2, 3], dtype='int64', name=u'apple')
In [284]: ind
Out[284]: Int64Index([1, 2, 3], dtype='int64')
In [285]: ind.set_names(["apple"], inplace=True)
In [286]: ind.name = "bob"
In [287]: ind
Out[287]: Int64Index([1, 2, 3], dtype='int64', name=u'bob')
set_names
, set_levels
, 和 set_labels
也有一个可选参数level
In [288]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])
In [289]: index
Out[289]:
MultiIndex(levels=[[0, 1, 2], [u'one', u'two']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
In [290]: index.levels[1]
Out[290]: Index([u'one', u'two'], dtype='object', name=u'second')
In [291]: index.set_levels(["a", "b"], level=1)
Out[291]:
MultiIndex(levels=[[0, 1, 2], [u'a', u'b']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
names=[u'first', u'second'])
Index对象的设置操作
警告 在0.15.0版本中,为了在某些特定索引类型上的数值操作,+和-操作已经过时。+可以使用.union( )或 | 代替,-可以使用.difference( )代替。
The two main operations are These can be directly called as instance methods or used via overloaded operators. Difference is provided via the .difference()
method.
两个主要的操作是union (|)和
intersection (&)。它们可以作为实例方法被直接调用或通过重载操作使用。Difference 是由
.difference()方法实现的。
In [292]: a = pd.Index(['c', 'b', 'a'])
In [293]: b = pd.Index(['c', 'e', 'd'])
In [294]: a | b
Out[294]: Index([u'a', u'b', u'c', u'd', u'e'], dtype='object')
In [295]: a & b
Out[295]: Index([u'c'], dtype='object')
In [296]: a.difference(b)
Out[296]: Index([u'a', u'b'], dtype='object')
symmetric_difference (^)操作也同样能用,它能够返回idx1或idx2中出现的元素,但不能二者都返回。这等价于使用idx1.difference(idx2).union(idx2.difference(idx1))来创建索引,没有重复项。
In [297]: idx1 = pd.Index([1, 2, 3, 4])
In [298]: idx2 = pd.Index([2, 3, 4, 5])
In [299]: idx1.symmetric_difference(idx2)
Out[299]: Int64Index([1, 5], dtype='int64')
In [300]: idx1 ^ idx2
Out[300]: Int64Index([1, 5], dtype='int64')
缺失值
重要: 虽然Index能够保留缺失值(NaN),但如果你不想出现什么乱七八糟的结果,最好还是避免缺失值。例如,有些操作会隐性地直接排除缺失值。
Index.fillna能够使用指定值来填充缺失值。
In [301]: idx1 = pd.Index([1, np.nan, 3, 4])
In [302]: idx1
Out[302]: Float64Index([1.0, nan, 3.0, 4.0], dtype='float64')
In [303]: idx1.fillna(2)
Out[303]: Float64Index([1.0, 2.0, 3.0, 4.0], dtype='float64')
In [304]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011-01-01'), pd.NaT, pd.Timestamp('2011-01-03')])
In [305]: idx2
Out[305]: DatetimeIndex(['2011-01-01', 'NaT', '2011-01-03'], dtype='datetime64[ns]', freq=None)
In [306]: idx2.fillna(pd.Timestamp('2011-01-02'))
Out[306]: DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], dtype='datetime64[ns]', freq=None)
设置/重置索引
有时候你会向一个DataFrame中加载或创建一个数据集,并给这个后来加入的数据子集加上索引。有以下几种方法:
设置索引
DataFrame有一个set_index方法,能够接收一个列名(对于常规Index来说)或一个列名list(对于MutiIndex来说)来创建一个新的,带有索引的DataFrame:
In [307]: data
Out[307]:
a b c d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
In [308]: indexed1 = data.set_index('c')
In [309]: indexed1
Out[309]:
a b d
c
z bar one 1.0
y bar two 2.0
x foo one 3.0
w foo two 4.0
In [310]: indexed2 = data.set_index(['a', 'b'])
In [311]: indexed2
Out[311]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
关键字append能够使你保留当前index,并把给定的列追加到一个MultiIndex中:
In [312]: frame = data.set_index('c', drop=False)
In [313]: frame = frame.set_index(['a', 'b'], append=True)
In [314]: frame
Out[314]:
c d
c a b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
set_index中的其他选项能够使你保留索引列或者原地加上索引。
In [315]: data.set_index('c', drop=False)
Out[315]:
a b c d
c
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [316]: data.set_index(['a', 'b'], inplace=True)
In [317]: data
Out[317]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
重置索引
考虑到方便性,DataFrame有一个新的功能叫做reset_index,它能够把index值转换为DataFrame的列并设置一个简单的整数索引。它是set_index的逆运算。
In [318]: data
Out[318]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
In [319]: data.reset_index()
Out[319]:
a b c d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
输出的结果更像是一个SQL表或一个数组记录。来自index的列名被存储在names属性里。
你可以使用level关键字来移除index的一部分。
In [320]: frame
Out[320]:
c d
c a b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [321]: frame.reset_index(level=1)
Out[321]:
a c d
c b
z one bar z 1.0
y two bar y 2.0
x one foo x 3.0
w two foo w 4.0
reset_index有一个可选参数drop,当drop=True时会直接丢弃index,而不是将index值放在DataFrame的列中。
注意 reset_index方法在以前的版本中叫做delevel
添加ad hoc索引
如果你自己创建了一个index,你可以将它分配到索引字段
返回视图vs副本
在一个pandas对象中设置值时,必须小心避免链式索引的出现。这是个例子:
In [322]: dfmi = pd.DataFrame([list('abcd'),
.....: list('efgh'),
.....: list('ijkl'),
.....: list('mnop')],
.....: columns=pd.MultiIndex.from_product([['one','two'],
.....: ['first','second']]))
.....:
In [323]: dfmi
Out[323]:
one two
first second first second
0 a b c d
1 e f g h
2 i j k l
3 m n o p
比较一下这两个访问方法:
In [324]: dfmi['one']['second']
Out[324]:
0 b
1 f
2 j
3 n
Name: second, dtype: object
In [325]: dfmi.loc[:,('one','second')]
Out[325]:
0 b
1 f
2 j
3 n
Name: (one, second), dtype: object
这两种方法产生的结果一样,所以你该用哪一种?了解它们的操作顺序、了解为什么第二种方法(.loc)比第一种好得多,是非常有意义的。
dfmi [“one”] 选取了列的第一个level,并返回一个单独索引的DataFrame,另一个Python操作ddmi_with_one[“second”]选取“second”索引的Series。这由可变的dfmi_with_one来指示,因为pandas把这些操作视为独立事件。例如对__getitem__的单独调用,所以它必须把他们作为线性运算,他们一个接一个地发生。
相反,df。loc[:,(“one”,”second”)]向单独调用的__getitem__传入了一个嵌套元组(slice(None),('one','second’)).这能够使pandas把它们作为一个单一的实体进行处理。此外,这个操作命令比第一种方法要快得多,并且如果需要的话,还能够允许同时索引多个轴。
为什么使用链式索引进行分配时会报错?
前面部分的问题仅仅是性能问题。为什么会有SettingWithCopy警告?当你的操作会多花费不必要的几毫秒时,我们通常不会报出警告!
但是事实证明,链式索引会导致不可预知的结果。要了解这一点,想想Python解释器如果执行这个代码的:
dfmi.loc[:,('one','second')] = value
# 变为
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
但这个代码的处理方式是完全不同的:
dfmi['one']['second'] = value
# 变为
dfmi.__getitem__('one').__setitem__('second', value)
看到这里的__getitem__了吗?除了一些简单的情况之外,我们很难预测它到底会返回一个视图还是一个副本(这取决于数组的内存布局,pandas可不能保证这个),也不能预测__setitem__是将会直接修改dfmi还是修改一个用完即扔的临时对象。这也是SettingWithCopy在警告你的东西!
注意 你可能会想在第一个例子中我们是否应该考虑到loc的特性。但是我们能肯定在修改索引时,dfmi.loc是dfmi自身,所以dfmi.loc.__getitem__
/dfmi.loc.__setitem__操作是直接作用在dfmi自身上的。当然,
dfmi.loc.__getitem__ (idx)或许是dfmi的一个视图或副本。
有些时候,当没有明显的链式索引时,SettingWithCopy警告也会产生。这是SettingWithCopy的一些设计上的bug,pandas可能会试着发出警告,如果你这样做的话:
def do_something(df):
foo = df[['bar', 'baz']] # foo是一个视图还是副本,没人知道啊!
#许多行在此省略
foo['quux'] = value # 我们不知道这个操作到底有没有修改到df啊!
return foo
唉!真无奈啊!
评估事项
此外,在链式表达式中,命令将决定是否返回一个副本。如果一个表达式会在一个副本或切片上设置值,那势必会引发一个SettingWithCopy异常(0.13.0以后的版本)
你可以通过选项mode.chained_assignment来控制一个链式分配,它能够接收值['raise','warn',None]。
In [326]: dfb = pd.DataFrame({'a' : ['one', 'one', 'two',
.....: 'three', 'two', 'one', 'six'],
.....: 'c' : np.arange(7)})
.....:
# 这将会引发SettingWithCopyWarning
# 但是frame的值确实被设置了
In [327]: dfb['c'][dfb.a.str.startswith('o')] = 42
然而这是在副本上的操作,并木有什么卵用。
>>> pd.set_option('mode.chained_assignment','warn')
>>> dfb[dfb.a.str.startswith('o')]['c'] = 42
Traceback (most recent call last)
...
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
一个链式分配也可以在设置一个混合类型的frame时让人猝不及防地出现。
注意 这些设置规则对.loc/.iloc/.ix通用
这才是正确的访问方法
In [328]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
In [329]: dfc.loc[0,'A'] = 11
In [330]: dfc
Out[330]:
A B
0 11 1
1 bbb 2
2 ccc 3
下面的方法有时能够正常使用,但不能保证任何时候都正常,所以应该避免使用
In [331]: dfc = dfc.copy()
In [332]: dfc['A'][0] = 111
In [333]: dfc
Out[333]:
A B
0 111 1
1 bbb 2
2 ccc 3
下面的方法是错的,所以别用啊
>>> pd.set_option('mode.chained_assignment','raise')
>>> dfc.loc[0]['A'] = 1111
Traceback (most recent call last)
...
SettingWithCopyException:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
警告 链式分配警告/异常是为了把可能无效的分配告知用户。有时候有可能是误报。