前言
pandas
对象的轴标签信息有很多用途,如
- 利用标识符来标识数据
- 能够显式的和自动对齐数据
- 获取和设置数据子集
在本节中,我们主要关注最后一点,如何对数据切片以及获取和设置数据对象(Series
和 DataFrame
)的子集。
1 索引方法
pandas
目前支持三种多轴索引:
.loc
:
主要是配合标签使用,但是也可与布尔数组一起使用。.loc
会在找不到对应的数据项目时引发 KeyError
。
允许的输入有
- 单个标签,如
5
或a
(注意,这里的5
是索引标签的值,并不是0
起始的整数位置索引)。 - 列表或数组,如
['a', 'b', 'c']
- 标签的切片对象,如
'A':'f'
(注意,与Python
切片不一样,这个切片会包含开始和结束位置的值) - 布尔数组(注意任何
NA
值都将被视为False
) - 带参数的回调函数,能够返回有效的索引值
.iloc
:
主要基于整数位置(0
到 length-1
),也可以和布尔数组一起使用。除了允许越界的切片索引器之外,如果索引越界,那么将会引发 IndexError
。
允许的输入有
- 一个整数,如
5
- 整数列表或数组,如
[4, 3, 0]
- 切片对象,如
1:7
- 布尔数组(注意任何
NA
值都将被视为False
) - 带参数的回调函数,能够返回有效的索引值
.loc
,.iloc
, 和[]
能够接受一个回调函数作为索引器
loc
和 iloc
的访问方式是一样的,例如
Series
:s.loc[indexer]
DataFrame
:df.loc[row_indexer,column_indexer]
同样适用于iloc
。
其中,未列出来的轴被假定为 :
,也就是选取该轴的所有数据
2 基础
使用 []
可以获取数据的低维度切片,使用方式为
我们来举例说明
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]: s = df['A']
In [5]: s[dates[5]]
Out[5]: -0.6736897080883706
你可以将一个列名列表传递给 []
,便能够按这个顺序返回对应的列。
如果传入了不包含在 DataFrame
中的列,将会引发一个异常。
也可以使用这种方式设置多个列。
In [6]: df
Out[6]:
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 [7]: df[['B', 'A']] = df[['A', 'B']]
In [8]: df
Out[8]:
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
设置 Series
和 DataFrame
时,pandas
将自动对齐所有的列
下面的代码不会修改 df
,因为列对齐发生在赋值之前。
In [9]: df[['A', 'B']]
Out[9]:
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 [10]: df.loc[:, ['B', 'A']] = df[['A', 'B']]
In [11]: df[['A', 'B']]
Out[11]:
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 [12]: df.loc[:, ['B', 'A']] = df[['A', 'B']].to_numpy()
In [13]: df[['A', 'B']]
Out[13]:
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
3 属性访问
你可以直接把列名作为一个属性,并以访问属性的方式获取对应的列:
In [14]: sa = pd.Series([1, 2, 3], index=list('abc'))
In [15]: dfa = df.copy()
In [16]: sa.b
Out[16]: 2
In [17]: dfa.A
Out[17]:
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 [18]: sa.a = 5
In [19]: sa
Out[19]:
a 5
b 2
c 3
dtype: int64
# 需要保证 A 列存在
In [20]: dfa.A = list(range(len(dfa.index)))
In [21]: dfa
Out[21]:
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 [22]: dfa['A'] = list(range(len(dfa.index)))
In [23]: dfa
Out[23]:
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
,但可以使用s['min']
- 如果该属性与以下任何列表冲突,则该属性将不可用:
index
,major_axis
,minor_axis
,item
如果使用的是 IPython
环境,可以使用 <Tab>
补全来查看属性
你也可以给将一个字典赋值给 DataFrame
的某行
In [24]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
In [25]: x.iloc[1] = {'x': 9, 'y': 99}
In [26]: x
Out[26]:
x y
0 1 3
1 9 99
2 3 5
你可以使用访问属性的方式来修改 Series
的元素或 DataFrame
的列,
但要注意,如果你试图使用访问属性的方式来创建一个新的列,它会创建一个新的属性而不是一个新列。在 0.21.0
及更高版本中,这将引发一个 UserWarning
。
In [1]: df = pd.DataFrame({'one': [1., 2., 3.]})
In [2]: df.two = [4, 5, 6]
UserWarning: Pandas doesn't allow Series to be assigned into nonexistent columns - see https://pandas.pydata.org/pandas-docs/stable/indexing.html#attribute_access
In [3]: df
Out[3]:
one
0 1.0
1 2.0
2 3.0
访问属性
>>> df.two
[4, 5, 6]
4 切片范围
我们使用 []
操作符来解释切片的用法
对于 Series
,该语法与 ndarray
的工作原理完全相同,返回值的一个切片和相应的标签
In [27]: s[:5]
Out[27]:
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 [28]: s[::2]
Out[28]:
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 [29]: s[::-1]
Out[29]:
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 [30]: s2 = s.copy()
In [31]: s2[:5] = 0
In [32]: s2
Out[32]:
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 [33]: df[:3]
Out[33]:
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 [34]: df[::-1]
Out[34]:
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
5 根据标签选择(.loc)
当您提供与索引类型不兼容(或可转换)的切片器时,.loc
是严格的。例如,在 DatetimeIndex
中使用整数将引发 TypeError
。
In [35]: dfl = pd.DataFrame(np.random.randn(5, 4),
....: columns=list('ABCD'),
....: index=pd.date_range('20130101', periods=5))
....:
In [36]: dfl
Out[36]:
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]
TypeError: cannot do slice indexing on <class 'pandas.tseries.index.DatetimeIndex'> with these indexers [2] of <type 'int'>
切片中的字符串类型可以转换为索引类型
In [37]: dfl.loc['20130102':'20130104']
Out[37]:
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
提供了一套方法,以实现纯粹基于标签的索引,这是一个严格的包含协议
要求的每个标签都必须在索引中,否则将引发 KeyError
。
在切片时,索引的起始和终止边界都会包含。整数切片指的是标签而不是位置索引
例如,对于 Series
In [38]: s1 = pd.Series(np.random.randn(6), index=list('abcdef'))
In [39]: s1
Out[39]:
a 1.431256
b 1.340309
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [40]: s1.loc['c':]
Out[40]:
c -1.170299
d -0.226169
e 0.410835
f 0.813850
dtype: float64
In [41]: s1.loc['b']
Out[41]: 1.3403088497993827
设置值
In [42]: s1.loc['c':] = 0
In [43]: s1
Out[43]:
a 1.431256
b 1.340309
c 0.000000
d 0.000000
e 0.000000
f 0.000000
dtype: float64
对于 DataFrame
In [44]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [45]: df1
Out[45]:
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 [46]: df1.loc[['a', 'b', 'd'], :]
Out[46]:
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 [47]: df1.loc['d':, 'A':'C']
Out[47]:
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 [48]: df1.loc['a']
Out[48]:
A 0.132003
B -0.827317
C -0.076467
D -1.187678
Name: a, dtype: float64
使用布尔数组获取值
In [49]: df1.loc['a'] > 0
Out[49]:
A True
B False
C False
D False
Name: a, dtype: bool
In [50]: df1.loc[:, df1.loc['a'] > 0]
Out[50]:
A
a 0.132003
b 1.130127
c 1.024180
d 0.974466
e 0.545952
f -1.281247
布尔数组中的 NA
值作为 False
In [51]: mask = pd.array([True, False, True, False, pd.NA, False], dtype="boolean")
In [52]: mask
Out[52]:
<BooleanArray>
[True, False, True, False, <NA>, False]
Length: 6, dtype: boolean
In [53]: df1[mask]
Out[53]:
A B C D
a 0.132003 -0.827317 -0.076467 -1.187678
c 1.024180 0.569605 0.875906 -2.211372
显式地获取一个值:
# 相当于 ``df1.at['a','A']``
In [54]: df1.loc['a', 'A']
Out[54]: 0.13200317033032932
5.1 根据标签切片
当使用 .loc
进行切片时,如果开始和停止标签都存在于索引中,则返回位于这两个标签之间的元素(包括它们)
In [55]: s = pd.Series(list('abcde'), index=[0, 3, 2, 5, 4])
In [56]: s.loc[3:5]
Out[56]:
3 b
2 c
5 d
dtype: object
如果这两个标签中至少有一个不存在,但索引已经排过序,并且可以与开始和结束标签进行比较,那么切片仍然可以按照预期工作,将会选择在这两个标签值范围之间的标签:
In [57]: s.sort_index()
Out[57]:
0 a
2 c
3 b
4 e
5 d
dtype: object
In [58]: s.sort_index().loc[1:6]
Out[58]:
2 c
3 b
4 e
5 d
dtype: object
但是,如果两个标签中至少有一个不存在且索引未排序,则会引发一个错误
例如,对于上面的例子,s.loc[1:6]
将会抛出 KeyError
异常
如果索引存在重复标签
In [59]: s = pd.Series(list('abcdef'), index=[0, 3, 2, 5, 4, 2])
In [60]: s.loc[3:5]
Out[60]:
3 b
2 c
5 d
dtype: object
如果索引切片的开始或结束位置是重复的标签,那么将会抛出 KeyError
异常
In [8]: s.loc[2:5]
---------------------------------------------------------------------------
KeyError
...
6 根据位置选择(.iloc)
Pandas
提供了一套方法,以获得纯粹基于整数的索引。语义跟 Python
和 NumPy
切片一致。
这些都是 0
起始的索引,在切片时,包含起始但不包括结束位置。
尝试使用非整数,甚至是有效的标签都会引发一个 IndexError
异常
对于 Series
In [61]: s1 = pd.Series(np.random.randn(5), index=list(range(0, 10, 2)))
In [62]: s1
Out[62]:
0 0.695775
2 0.341734
4 0.959726
6 -1.110336
8 -0.619976
dtype: float64
In [63]: s1.iloc[:3]
Out[63]:
0 0.695775
2 0.341734
4 0.959726
dtype: float64
In [64]: s1.iloc[3]
Out[64]: -1.110336102891167
设置值
In [65]: s1.iloc[:3] = 0
In [66]: s1
Out[66]:
0 0.000000
2 0.000000
4 0.000000
6 -1.110336
8 -0.619976
dtype: float64
对于 DataFrame
In [67]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list(range(0, 12, 2)),
....: columns=list(range(0, 8, 2)))
....:
In [68]: df1
Out[68]:
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 [69]: df1.iloc[:3]
Out[69]:
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 [70]: df1.iloc[1:5, 2:4]
Out[70]:
4 6
2 0.301624 -2.179861
4 1.462696 -1.743161
6 1.314232 0.690579
8 0.014871 3.357427
通过整数列表选择:
In [71]: df1.iloc[[1, 3, 5], [1, 3]]
Out[71]:
2 6
2 -0.154951 -2.179861
6 -0.345352 0.690579
10 -1.236269 -0.487602
In [72]: df1.iloc[1:3, :]
Out[72]:
0 2 4 6
2 0.403310 -0.154951 0.301624 -2.179861
4 -1.369849 -0.954208 1.462696 -1.743161
In [73]: df1.iloc[:, 1:3]
Out[73]:
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 [74]: df1.iloc[1, 1]
Out[74]: -0.1549507744249032
使用整数位置获取截面(等同于 df.xs(1)
)
In [75]: df1.iloc[1]
Out[75]:
0 0.403310
2 -0.154951
4 0.301624
6 -2.179861
Name: 2, dtype: float64
超出范围的分片索引会像在 Python
和 NumPy
中一样被优雅地处理。
# these are allowed in Python/NumPy.
In [76]: x = list('abcdef')
In [77]: x
Out[77]: ['a', 'b', 'c', 'd', 'e', 'f']
In [78]: x[4:10]
Out[78]: ['e', 'f']
In [79]: x[8:10]
Out[79]: []
In [80]: s = pd.Series(x)
In [81]: s
Out[81]:
0 a
1 b
2 c
3 d
4 e
5 f
dtype: object
In [82]: s.iloc[4:10]
Out[82]:
4 e
5 f
dtype: object
In [83]: s.iloc[8:10]
Out[83]: Series([], dtype: object)
请注意,使用超出边界的切片可能会导致一个空轴(例如,返回一个空的 DataFrame
)
In [84]: dfl = pd.DataFrame(np.random.randn(5, 2), columns=list('AB'))
In [85]: dfl
Out[85]:
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 [86]: dfl.iloc[:, 2:3]
Out[86]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]
In [87]: dfl.iloc[:, 1:3]
Out[87]:
B
0 -2.182937
1 0.084844
2 1.519970
3 0.600178
4 0.132885
In [88]: dfl.iloc[4:6]
Out[88]:
A B
4 0.27423 0.132885
单个索引器或索引器列表中有任何元素超出边界,会引发 IndexError
。
>>> dfl.iloc[[4, 5, 6]]
IndexError: positional indexers are out-of-bounds
>>> dfl.iloc[:, 4]
IndexError: single positional indexer is out-of-bounds
7 根据可调用函数选择
.loc
, .iloc
, 和 []
能够接受一个可调用函数作为索引器
可调用对象必须是具有一个参数(Series
或 DataFrame
)的函数,该函数返回有效的输出以进行索引
In [89]: df1 = pd.DataFrame(np.random.randn(6, 4),
....: index=list('abcdef'),
....: columns=list('ABCD'))
....:
In [90]: df1
Out[90]:
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 [91]: df1.loc[lambda df: df['A'] > 0, :]
Out[91]:
A B C D
c 0.299368 -0.863838 0.408204 -1.048089
e 1.289997 0.082423 -0.055758 0.536580
In [92]: df1.loc[:, lambda df: ['A', 'B']]
Out[92]:
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 [93]: df1.iloc[:, lambda df: [0, 1]]
Out[93]:
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 [94]: df1[lambda df: df.columns[0]]
Out[94]:
a -0.023688
b -0.251905
c 0.299368
d -0.025747
e 1.289997
f -0.489682
Name: A, dtype: float64
也可以在 Series
中使用
In [95]: df1['A'].loc[lambda s: s > 0]
Out[95]:
c 0.299368
e 1.289997
Name: A, dtype: float64
使用这些方法和索引器,你可以在不使用临时变量的情况下对数据选择操作进行串联
In [96]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [97]: (bb.groupby(['year', 'team']).sum()
....: .loc[lambda df: df['r'] > 100])
....:
Out[97]:
stint g ab r h X2b X3b hr rbi sb cs bb so ibb hbp sh sf gidp
year team
2007 CIN 6 379 745 101 203 35 2 36 125.0 10.0 1.0 105 127.0 14.0 1.0 1.0 15.0 18.0
DET 5 301 1062 162 283 54 4 37 144.0 24.0 7.0 97 176.0 3.0 10.0 4.0 8.0 28.0
HOU 4 311 926 109 218 47 6 14 77.0 10.0 4.0 60 212.0 3.0 9.0 16.0 6.0 17.0
LAN 11 413 1021 153 293 61 3 36 154.0 7.0 5.0 114 141.0 8.0 9.0 3.0 8.0 29.0
NYN 13 622 1854 240 509 101 3 61 243.0 22.0 4.0 174 310.0 24.0 23.0 18.0 15.0 48.0
SFN 5 482 1305 198 337 67 6 40 171.0 26.0 7.0 235 188.0 51.0 8.0 16.0 6.0 41.0
TEX 2 198 729 115 200 40 4 28 115.0 21.0 4.0 73 140.0 4.0 5.0 2.0 8.0 16.0
TOR 4 459 1408 187 378 96 2 58 223.0 4.0 2.0 190 265.0 16.0 12.0 4.0 16.0 38.0
8 组合位置和标签索引
如果你想获取 'A'
列的第 0
和第 2
个元素,你可以这样做:
In [98]: dfd = pd.DataFrame({'A': [1, 2, 3],
....: 'B': [4, 5, 6]},
....: index=list('abc'))
....:
In [99]: dfd
Out[99]:
A B
a 1 4
b 2 5
c 3 6
In [100]: dfd.loc[dfd.index[[0, 2]], 'A']
Out[100]:
a 1
c 3
Name: A, dtype: int64
这也可以用 .iloc
获取,通过使用位置索引来选择内容
In [101]: dfd.iloc[[0, 2], dfd.columns.get_loc('A')]
Out[101]:
a 1
c 3
Name: A, dtype: int64
可以使用 .get_indexer
获取多个索引:
In [101]: dfd.iloc[[0, 2], dfd.columns.get_loc('A')]
Out[101]:
a 1
c 3
Name: A, dtype: int64
9 列表中包含缺失标签的索引已被弃用
警告:
对于包含一个或多个缺失标签的列表,使用 .loc
或 []
将不再重新索引,而是使用 .reindex
在以前的版本中,只要索引列表中存在至少一个有效标签,就可以使用 .loc[list-of-labels]
但是现在,只要索引列表中存在缺失的标签将引发 KeyError
。推荐的替代方法是使用 .reindex()
。
例如
In [103]: s = pd.Series([1, 2, 3])
In [104]: s
Out[104]:
0 1
1 2
2 3
dtype: int64
索引列表的标签都存在
In [105]: s.loc[[1, 2]]
Out[105]:
1 2
2 3
dtype: int64
先前的版本
In [4]: s.loc[[1, 2, 3]]
Out[4]:
1 2.0
2 3.0
3 NaN
dtype: float64
但是,现在
In [4]: s.loc[[1, 2, 3]]
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-8-28925a59f003> in <module>
----> 1 s.loc[[1, 2, 3]]
...
KeyError: "Passing list-likes to .loc or [] with any missing labels is no longer supported. The following labels were missing: Int64Index([3], dtype='int64'). See https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#deprecate-loc-reindex-listlike"
reindex
索引标签列表中包含不存在的标签,使用 reindex
In [106]: s.reindex([1, 2, 3])
Out[106]:
1 2.0
2 3.0
3 NaN
dtype: float64
另外,如果你只想选择有效的键,可以使用下面的方法,同时保留了数据的 dtype
In [107]: labels = [1, 2, 3]
In [108]: s.loc[s.index.intersection(labels)]
Out[108]:
1 2
2 3
dtype: int64
对于 .reindex()
,如果有重复的索引将会引发异常
In [109]: s = pd.Series(np.arange(4), index=['a', 'a', 'b', 'c'])
In [110]: labels = ['c', 'd']
In [17]: s.reindex(labels)
ValueError: cannot reindex from a duplicate axis
通常,您可以将所需的标签与当前轴做交集,然后重新索引
In [111]: s.loc[s.index.intersection(labels)].reindex(labels)
Out[111]:
c 3.0
d NaN
dtype: float64
但是,如果你的索引结果包含重复标签,还是会引发异常
In [41]: labels = ['a', 'd']
In [42]: s.loc[s.index.intersection(labels)].reindex(labels)
ValueError: cannot reindex from a duplicate axis
10 随机抽样
使用 sample()
方法可以从 Series
或 DataFrame
中随机选择行或列。
该方法默认会对行进行采样,并接受一个特定的行数、列数,或数据子集。
In [112]: s = pd.Series([0, 1, 2, 3, 4, 5])
# 不传递参数,返回任意一行
In [113]: s.sample()
Out[113]:
4 4
dtype: int64
# 选择指定数量的行
In [114]: s.sample(n=3)
Out[114]:
0 0
4 4
1 1
dtype: int64
# 返回百分比的数据
In [115]: s.sample(frac=0.5)
Out[115]:
5 5
3 3
1 1
dtype: int64
默认情况下,sample
每行最多返回一次,但也可以使用 replace
参数进行替换采样
In [116]: s = pd.Series([0, 1, 2, 3, 4, 5])
# Without replacement (default):
In [117]: s.sample(n=6, replace=False)
Out[117]:
0 0
1 1
5 5
3 3
2 2
4 4
dtype: int64
# With replacement:
In [118]: s.sample(n=6, replace=True)
Out[118]:
0 0
4 4
3 3
2 2
4 4
4 4
dtype: int64
默认情况下,每一行被选中的概率相等,但是如果你想让每一行有不同的概率,你可以为 sample
函数的 weights
参数设置抽样权值
In [119]: s = pd.Series([0, 1, 2, 3, 4, 5])
In [120]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4]
In [121]: s.sample(n=3, weights=example_weights)
Out[121]:
5 5
4 4
3 3
dtype: int64
# Weights will be re-normalized automatically
In [122]: example_weights2 = [0.5, 0, 0, 0, 0, 0]
In [123]: s.sample(n=1, weights=example_weights2)
Out[123]:
0 0
dtype: int64
这些权重可以是一个列表、一个 NumPy
数组或一个 Series
,但它们的长度必须与你要抽样的对象相同。
缺失的值将被视为权重为零,并且不允许使用 inf
值。如果权重之和不等于 1
,则将所有权重除以权重之和,将其重新归一化。例如
In [119]: s = pd.Series([0, 1, 2, 3, 4, 5])
In [120]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4]
In [121]: s.sample(n=3, weights=example_weights)
Out[121]:
5 5
4 4
3 3
dtype: int64
# Weights will be re-normalized automatically
In [122]: example_weights2 = [0.5, 0, 0, 0, 0, 0]
In [123]: s.sample(n=1, weights=example_weights2)
Out[123]:
0 0
dtype: int64
当应用于 DataFrame
时,您可以通过简单地将列名作为字符串传递给 weights
作为采样权重(前提是您要采样的是行而不是列)。
In [124]: df2 = pd.DataFrame({'col1': [9, 8, 7, 6],
.....: 'weight_column': [0.5, 0.4, 0.1, 0]})
.....:
In [125]: df2.sample(n=3, weights='weight_column')
Out[125]:
col1 weight_column
1 8 0.4
0 9 0.5
2 7 0.1
sample
还允许用户使用 axis
参数对列进行抽样。
In [126]: df3 = pd.DataFrame({'col1': [1, 2, 3], 'col2': [2, 3, 4]})
In [127]: df3.sample(n=1, axis=1)
Out[127]:
col1
0 1
1 2
2 3
最后,我们还可以使用 random_state
参数为 sample
的随机数生成器设置一个种子,它将接受一个整数(作为种子)或一个 NumPy
RandomState
对象
In [128]: df4 = pd.DataFrame({'col1': [1, 2, 3], 'col2': [2, 3, 4]})
# With a given seed, the sample will always draw the same rows.
In [129]: df4.sample(n=2, random_state=2)
Out[129]:
col1 col2
2 3 4
1 2 3
In [130]: df4.sample(n=2, random_state=2)
Out[130]:
col1 col2
2 3 4
1 2 3
当为该轴设置一个不存在的键时,.loc/[]
操作可以执行放大
在 Series
的情况下,这实际上是一个追加操作
In [131]: se = pd.Series([1, 2, 3])
In [132]: se
Out[132]:
0 1
1 2
2 3
dtype: int64
In [133]: se[5] = 5.
In [134]: se
Out[134]:
0 1.0
1 2.0
2 3.0
5 5.0
dtype: float64
可以通过 .loc
在任一轴上放大 DataFrame
In [135]: dfi = pd.DataFrame(np.arange(6).reshape(3, 2),
.....: columns=['A', 'B'])
.....:
In [136]: dfi
Out[136]:
A B
0 0 1
1 2 3
2 4 5
In [137]: dfi.loc[:, 'C'] = dfi.loc[:, 'A']
In [138]: dfi
Out[138]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
这就像 DataFrame
的 append
操作
In [139]: dfi.loc[3] = 5
In [140]: dfi
Out[140]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
3 5 5 5
11 快速获取和设置标量值
由于用 []
做索引必须处理很多情况(单标签访问、分片、布尔索引等),所以需要一些开销来搞清楚你的意图
如果你只想访问一个标量值,最快的方法是使用 at
和 iat
方法,这两个方法在所有的数据结构上都实现了
与 loc
类似,at
提供了基于标签的标量查找,而 iat
提供了基于整数的查找,与 iloc
类似
In [141]: s.iat[5]
Out[141]: 5
In [142]: df.at[dates[5], 'A']
Out[142]: -0.6736897080883706
In [143]: df.iat[3, 0]
Out[143]: 0.7215551622443669
同时,你也可以根据这些索引进行设置值
In [144]: df.at[dates[5], 'E'] = 7
In [145]: df.iat[3, 0] = 7
如果索引标签不存在,会放大数据
In [146]: df.at[dates[-1] + pd.Timedelta('1 day'), 0] = 7
In [147]: df
Out[147]:
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
12 布尔索引
另一种常见的操作是使用布尔向量来过滤数据。运算符包括:
|(or)
、&(and)
、~ (not)
这些必须用括号来分组,因为默认情况下,Python
会将 df['A'] > 2 & df['B'] < 3
这样的表达式评估为 df['A'] > (2 & df['B']) < 3
,而理想的执行顺序是 (df['A'] > 2) & (df['B'] < 3)
使用一个布尔向量来索引一个 Series
,其工作原理和 NumPy
ndarray
一样。
In [148]: s = pd.Series(range(-3, 4))
In [149]: s
Out[149]:
0 -3
1 -2
2 -1
3 0
4 1
5 2
6 3
dtype: int64
In [150]: s[s > 0]
Out[150]:
4 1
5 2
6 3
dtype: int64
In [151]: s[(s < -1) | (s > 0.5)]
Out[151]:
0 -3
1 -2
4 1
5 2
6 3
dtype: int64
In [152]: s[~(s < 0)]
Out[152]:
3 0
4 1
5 2
6 3
dtype: int64
您可以使用一个与 DataFrame
的索引长度相同的布尔向量从 DataFrame
中选择行
In [153]: df[df['A'] > 0]
Out[153]:
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
的 map
函数可用于产生更复杂的标准
In [154]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
.....: 'b': ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
.....: 'c': np.random.randn(7)})
.....:
# only want 'two' or 'three'
In [155]: criterion = df2['a'].map(lambda x: x.startswith('t'))
In [156]: df2[criterion]
Out[156]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# equivalent but slower
In [157]: df2[[x.startswith('t') for x in df2['a']]]
Out[157]:
a b c
2 two y 0.041290
3 three x 0.361719
4 two y -0.238075
# Multiple criteria
In [158]: df2[criterion & (df2['b'] == 'x')]
Out[158]:
a b c
3 three x 0.361719
我们可以使用布尔向量结合其他索引表达式,在多个轴上索引
In [159]: df2.loc[criterion & (df2['b'] == 'x'), 'b':'c']
Out[159]:
b c
3 x 0.361719
iloc
支持两种布尔索引。如果索引器是一个布尔值 Series
,就会引发异常。
例如,在下面的例子中,df.iloc[s.values, 1]
是正确的。但是 df.iloc[s,1]
会引发 ValueError
。
In [160]: df = pd.DataFrame([[1, 2], [3, 4], [5, 6]],
.....: index=list('abc'),
.....: columns=['A', 'B'])
.....:
In [161]: s = (df['A'] > 2)
In [162]: s
Out[162]:
a False
b True
c True
Name: A, dtype: bool
In [163]: df.loc[s, 'B']
Out[163]:
b 4
c 6
Name: B, dtype: int64
In [164]: df.iloc[s.values, 1]
Out[164]:
b 4
c 6
Name: B, dtype: int64
13 使用 isin 索引
isin()
方法,顾名思义,就是判断 pandas
对象的每个元素是否存在传入的对象(Series
、DataFram
、dict
以及可迭代对象)中,返回一个布尔值 DataFrame
In [165]: s = pd.Series(np.arange(5), index=np.arange(5)[::-1], dtype='int64')
In [166]: s
Out[166]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [167]: s.isin([2, 4, 6])
Out[167]:
4 False
3 False
2 True
1 False
0 True
dtype: bool
In [168]: s[s.isin([2, 4, 6])]
Out[168]:
2 2
0 4
dtype: int64
同样,该方法也适用于 Index
对象,当你不知道所要寻找的标签中哪些是真实存在的时候,这一方法是很有用的
In [169]: s[s.index.isin([2, 4, 6])]
Out[169]:
4 0
2 2
dtype: int64
# 注意与下面代码的区别
In [170]: s.reindex([2, 4, 6])
Out[170]:
2 2.0
4 0.0
6 NaN
dtype: float64
对于 MultiIndex
,还可以指定索引的级别
In [171]: s_mi = pd.Series(np.arange(6),
.....: index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']]))
.....:
In [172]: s_mi
Out[172]:
0 a 0
b 1
c 2
1 a 3
b 4
c 5
dtype: int64
In [173]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])]
Out[173]:
0 c 2
1 a 3
dtype: int64
In [174]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)]
Out[174]:
0 a 0
c 2
1 a 3
c 5
dtype: int64
DataFrame
也有 isin()
方法,如果传递的是数组、列表或序列,会返回一个与原 DataFrame
大小相同的布尔 DataFrame
In [175]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
.....: 'ids2': ['a', 'n', 'c', 'n']})
.....:
In [176]: values = ['a', 'b', 1, 3]
In [177]: df.isin(values)
Out[177]:
vals ids ids2
0 True True True
1 False True False
2 True False False
3 False False False
通常,您希望将特定的值与特定的列相匹配。只需将 values
设置为一个字典,其中键是列名,值是要检查的值的列表。
In [178]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [179]: df.isin(values)
Out[179]:
vals ids ids2
0 True True False
1 False True False
2 True False False
3 False False False
将 DataFrame
的 isin
与 any()
和 all()
方法结合起来,可以快速选择满足给定条件的数据子集。
In [180]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [181]: row_mask = df.isin(values).all(1)
In [182]: df[row_mask]
Out[182]:
vals ids ids2
0 1 a a
14 where()
和 mask()
方法
用布尔向量从 Series
中选择值,一般会返回数据的一个子集。为了保证选择的数据与原始数据的形状相同,可以使用 Series
和 DataFrame
中的 where
方法
返回选定的行
In [183]: s[s > 0]
Out[183]:
3 1
2 2
1 3
0 4
dtype: int64
返回一个与原始数据形状相同的 Series
In [184]: s.where(s > 0)
Out[184]:
4 NaN
3 1.0
2 2.0
1 3.0
0 4.0
dtype: float64
下面的代码与 df.where(df < 0)
功能一样
In [185]: df[df < 0]
Out[185]:
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
接受一个可选的 other
参数,用于指定条件为 False
的值的替换值
In [186]: df.where(df < 0, -df)
Out[186]:
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 [187]: s2 = s.copy()
In [188]: s2[s2 < 0] = 0
In [189]: s2
Out[189]:
4 0
3 1
2 2
1 3
0 4
dtype: int64
In [190]: df2 = df.copy()
In [191]: df2[df2 < 0] = 0
In [192]: df2
Out[192]:
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 [193]: df_orig = df.copy()
In [194]: df_orig.where(df > 0, -df, inplace=True)
In [195]: df_orig
Out[195]:
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
注意:
pandas
的 df1.where(m, df2)
与 numpy
的 np.where(m, df1, df2)
相等
In [196]: df.where(df < 0, -df) == np.where(df < 0, df, -df)
Out[196]:
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
会与布尔输入对齐,与 .loc
的部分选择和部分设置类似
In [197]: df2 = df.copy()
In [198]: df2[df2[1:4] > 0] = 3
In [199]: df2
Out[199]:
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
还可以接受 axis
和 level
参数来对齐输入
In [200]: df2 = df.copy()
In [201]: df2.where(df2 > 0, df2['A'], axis='index')
Out[201]:
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 [202]: df2 = df.copy()
In [203]: df.apply(lambda x, y: x.where(x > 0, y), y=df['A'])
Out[203]:
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
的条件参数和 other
参数接受一个可调用函数,函数必须传入一个参数,并返回有效的输出作为条件和 other
参数。
In [204]: df3 = pd.DataFrame({'A': [1, 2, 3],
.....: 'B': [4, 5, 6],
.....: 'C': [7, 8, 9]})
.....:
In [205]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[205]:
A B C
0 11 14 7
1 12 5 8
2 13 6 9
mask
mask()
是 where
的逆布尔运算
In [206]: s.mask(s >= 0)
Out[206]:
4 NaN
3 NaN
2 NaN
1 NaN
0 NaN
dtype: float64
In [207]: df.mask(df >= 0)
Out[207]:
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
15 使用 numpy 函数进行有条件的放大设置
where()
的替代方法是使用 numpy.where()
。通过与设置新列相结合,可以对数据进行放大,其中的值是根据条件确定的。
In [208]: df = pd.DataFrame({'col1': list('ABBC'), 'col2': list('ZZXY')})
In [209]: df['color'] = np.where(df['col2'] == 'Z', 'green', 'red')
In [210]: df
Out[210]:
col1 col2 color
0 A Z green
1 B Z green
2 B X red
3 C Y red
考虑到在下面的数据,你有两个选择。你想在第二列有 'Z'
的时候,将新的列颜色设置为 'green'
。你可以执行以下操作
In [208]: df = pd.DataFrame({'col1': list('ABBC'), 'col2': list('ZZXY')})
In [209]: df['color'] = np.where(df['col2'] == 'Z', 'green', 'red')
In [210]: df
Out[210]:
col1 col2 color
0 A Z green
1 B Z green
2 B X red
3 C Y red
如果有多个条件,可以使用 numpy.select()
。
例如,如果说对应三个条件有三种颜色可以选择,第四种颜色作为备用,可以做如下处理
In [211]: conditions = [
.....: (df['col2'] == 'Z') & (df['col1'] == 'A'),
.....: (df['col2'] == 'Z') & (df['col1'] == 'B'),
.....: (df['col1'] == 'B')
.....: ]
.....:
In [212]: choices = ['yellow', 'blue', 'purple']
In [213]: df['color'] = np.select(conditions, choices, default='black')
In [214]: df
Out[214]:
col1 col2 color
0 A Z yellow
1 B Z blue
2 B X purple
3 C Y black
16 重复数据
如果你想识别和删除 DataFrame
中的重复行,可以使用下面两个方法:
duplicate
: 返回一个布尔向量,其长度与数据行数一样,指示对应的行是否重复drop_duplicates
: 删除重复的行
默认情况下,第一个观察到的行被认为是唯一的(即保留重复的第一个),但是每个方法都有一个 keep
参数来指定要保存的目标
keep='first'(default)
: 保留重复数据的第一个keep='last'
: 保留重复数据的最后一个keep=False
: 删除所有存在重复的行
In [281]: 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 [282]: df2
Out[282]:
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 [283]: df2.duplicated('a')
Out[283]:
0 False
1 True
2 False
3 True
4 True
5 False
6 False
dtype: bool
In [284]: df2.duplicated('a', keep='last')
Out[284]:
0 True
1 False
2 True
3 True
4 False
5 False
6 False
dtype: bool
In [285]: df2.duplicated('a', keep=False)
Out[285]:
0 True
1 True
2 True
3 True
4 True
5 False
6 False
dtype: bool
In [286]: df2.drop_duplicates('a')
Out[286]:
a b c
0 one x -1.067137
2 two x -0.211056
5 three x -1.964475
6 four x 1.298329
In [287]: df2.drop_duplicates('a', keep='last')
Out[287]:
a b c
1 one y 0.309500
4 two x -0.390820
5 three x -1.964475
6 four x 1.298329
In [288]: df2.drop_duplicates('a', keep=False)
Out[288]:
a b c
5 three x -1.964475
6 four x 1.298329
此外,还可以传递列名列表来标识重复行
In [289]: df2.duplicated(['a', 'b'])
Out[289]:
0 False
1 False
2 False
3 False
4 True
5 False
6 False
dtype: bool
In [290]: df2.drop_duplicates(['a', 'b'])
Out[290]:
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.deproicated
然后执行切片。也包含同样的 keep
参数
In [291]: df3 = pd.DataFrame({'a': np.arange(6),
.....: 'b': np.random.randn(6)},
.....: index=['a', 'a', 'b', 'c', 'b', 'a'])
.....:
In [292]: df3
Out[292]:
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 [293]: df3.index.duplicated()
Out[293]: array([False, True, False, False, True, True])
In [294]: df3[~df3.index.duplicated()]
Out[294]:
a b
a 0 1.440455
b 2 1.038402
c 3 -0.894409
In [295]: df3[~df3.index.duplicated(keep='last')]
Out[295]:
a b
c 3 -0.894409
b 4 0.683536
a 5 3.082764
In [296]: df3[~df3.index.duplicated(keep=False)]
Out[296]:
a b
c 3 -0.894409
17 类似字典个 get 方法
每个 Series
或 DataFrame
都有一个可以返回默认值的 get
方法
In [297]: s = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
In [298]: s.get('a') # equivalent to s['a']
Out[298]: 1
In [299]: s.get('x', default=-1)
Out[299]: -1
18 使用索引或列标签查找值
有时,您希望提取一组给定行标签和列标签序列的值,这可以通过使用 DataFrame.melt
和 DataFrame.loc
来实现
In [300]: df = pd.DataFrame({'col': ["A", "A", "B", "B"],
.....: 'A': [80, 23, np.nan, 22],
.....: 'B': [80, 55, 76, 67]})
.....:
In [301]: df
Out[301]:
col A B
0 A 80.0 80
1 A 23.0 55
2 B NaN 76
3 B 22.0 67
In [302]: melt = df.melt('col')
In [303]: melt = melt.loc[melt['col'] == melt['variable'], 'value']
In [304]: melt.reset_index(drop=True)
Out[304]:
0 80.0
1 23.0
2 76.0
3 67.0
Name: value, dtype: float64
19 query() 方法
DataFrame
对象有一个 query()
方法,它允许使用表达式进行选择。
例如,想要后去 b
列值位于 a
列 和 c
列之间的行
In [215]: n = 10
In [216]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [217]: df
Out[217]:
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
# pure python
In [218]: df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Out[218]:
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 [219]: df.query('(a < b) & (b < c)')
Out[219]:
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
的列,则会使用名为 a
的命名索引
In [220]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [221]: df.index.name = 'a'
In [222]: df
Out[222]:
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 [223]: df.query('a < b and b < c')
Out[223]:
b c
a
2 3 4
如果你不想或不能命名索引,你可以在查询表达式中使用 index
指代
In [224]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [225]: df
Out[225]:
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 [226]: df.query('index < b < c')
Out[226]:
b c
2 5 6
注意:
如果索引的名称与列名重叠,则列名优先。例如
In [227]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [228]: df.index.name = 'a'
In [229]: df.query('a > 2') # 使用列而不是索引
Out[229]:
a
a
1 3
3 3
你仍然可以使用 index
来引用索引
In [230]: df.query('index > 2')
Out[230]:
a
a
3 3
4 2
如果出于某些原因,您有一个名为 index
的列,那么您也可以使用 ilevel_0
来引用索引,但此时您应该考虑将您的列重命名为不那么模糊的名称
19.1 MultiIndex query() 语法
对包含多级索引的 DataFrame
查询,可以直接使用 level
名称,就像使用列名一样方便
In [231]: n = 10
In [232]: colors = np.random.choice(['red', 'green'], size=n)
In [233]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [234]: colors
Out[234]:
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
'green', 'green'], dtype='<U5')
In [235]: foods
Out[235]:
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
'eggs'], dtype='<U4')
In [236]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [237]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [238]: df
Out[238]:
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 [239]: df.query('color == "red"')
Out[239]:
0 1
color food
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
如果 MultiIndex
的 level
没有命名,你可以使用特殊的名称来引用它们
In [240]: df.index.names = [None, None]
In [241]: df
Out[241]:
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 [242]: df.query('ilevel_0 == "red"')
Out[242]:
0 1
red ham 0.194889 -0.381994
ham 0.318587 2.089075
eggs -0.728293 -0.090255
通常 ilevel_0
的意思是索引级别为 0
,代表 index
的第 1
个 level
19.2 query() 示例
当你有多个 DataFrame
对象,且它们之间存在共同的列(或索引),你可以将相同的查询同时应用于这些对象,而不需要指定一一指定
In [243]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [244]: df
Out[244]:
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 [245]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [246]: df2
Out[246]:
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 [247]: expr = '0.0 <= a <= c <= 0.5'
In [248]: map(lambda frame: frame.query(expr), [df, df2])
Out[248]: <map at 0x7f7110fdd910>
19.3 query() 的 Python 和 pandas 语法比较
类似 numpy
的语法
In [249]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [250]: df
Out[250]:
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 [251]: df.query('(a < b) & (b < c)')
Out[251]:
a b c
0 7 8 9
In [252]: df[(df['a'] < df['b']) & (df['b'] < df['c'])]
Out[252]:
a b c
0 7 8 9
去掉括号会更简单一些
In [253]: df.query('a < b & b < c')
Out[253]:
a b c
0 7 8 9
使用英语替代符号
In [254]: df.query('a < b and b < c')
Out[254]:
a b c
0 7 8 9
更简洁的方式是
In [255]: df.query('a < b < c')
Out[255]:
a b c
0 7 8 9
19.4 in 和 not in 操作
query()
还支持 Python
的 in
和 not
比较运算符的使用,是 Series
或 DataFrame
的 isin
方法的一个简单替代
In [256]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....: 'c': np.random.randint(5, size=12),
.....: 'd': np.random.randint(9, size=12)})
.....:
In [257]: df
Out[257]:
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 [258]: df.query('a in b')
Out[258]:
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 [259]: df[df['a'].isin(df['b'])]
Out[259]:
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 [260]: df.query('a not in b')
Out[260]:
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 [261]: df[~df['a'].isin(df['b'])]
Out[261]:
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 [262]: df.query('a in b and c < d')
Out[262]:
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 [263]: df[df['b'].isin(df['a']) & (df['c'] < df['d'])]
Out[263]:
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
注意:
因为 numexpr
不支持 in
和 not in
操作,所以会被 Python
执行。例如
df.query('a in b + c + d')
(b + c + d)
会被 numexpr
执行,而 in
操作是被 Python
执行。通常其他任何操作都可以使用 numexpr
执行
19.5 == 和 list 的使用
使用 ==
和 !=
对比列和值列表的方式与 in/not in
类似
In [264]: df.query('b == ["a", "b", "c"]')
Out[264]:
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
# pure Python
In [265]: df[df['b'].isin(["a", "b", "c"])]
Out[265]:
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 [266]: df.query('c == [1, 2]')
Out[266]:
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 [267]: df.query('c != [1, 2]')
Out[267]:
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
# using in/not in
In [268]: df.query('[1, 2] in c')
Out[268]:
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 [269]: df.query('[1, 2] not in c')
Out[269]:
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
# pure Python
In [270]: df[df['c'].isin([1, 2])]
Out[270]:
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
19.6 布尔操作
可以用 not
或 ~
运算符对布尔表达式取反
In [271]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [272]: df['bools'] = np.random.rand(len(df)) > 0.5
In [273]: df.query('~bools')
Out[273]:
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 [274]: df.query('not bools')
Out[274]:
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 [275]: df.query('not bools') == df[~df['bools']]
Out[275]:
a b c bools
2 True True True True
7 True True True True
8 True True True True
当然,表达式也可以任意的复杂的组合
# 简洁的 query 语法
In [276]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# 纯 Python 实现
In [277]: longer = df[(df['a'] < df['b'])
.....: & (df['b'] < df['c'])
.....: & (~df['bools'])
.....: | (df['bools'] > 2)]
.....:
In [278]: shorter
Out[278]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [279]: longer
Out[279]:
a b c bools
7 0.275396 0.691034 0.826619 False
In [280]: shorter == longer
Out[280]:
a b c bools
7 True True True True
19.7 query() 的性能
query
使用 numexpr
会比纯 Python
在大数据框的查询上速度更快一些
20 索引对象
索引几乎都是不可变的,但是可以设置和更改它们的 name
属性。您可以使用 rename
、set_names
直接设置这些属性,它们默认会返回一个拷贝
In [315]: ind = pd.Index([1, 2, 3])
In [316]: ind.rename("apple")
Out[316]: Int64Index([1, 2, 3], dtype='int64', name='apple')
In [317]: ind
Out[317]: Int64Index([1, 2, 3], dtype='int64')
In [318]: ind.set_names(["apple"], inplace=True)
In [319]: ind.name = "bob"
In [320]: ind
Out[320]: Int64Index([1, 2, 3], dtype='int64', name='bob')
set_names
、set_levels
和 set_codes
还有一个可选的 level
参数
In [321]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'])
In [322]: index
Out[322]:
MultiIndex([(0, 'one'),
(0, 'two'),
(1, 'one'),
(1, 'two'),
(2, 'one'),
(2, 'two')],
names=['first', 'second'])
In [323]: index.levels[1]
Out[323]: Index(['one', 'two'], dtype='object', name='second')
In [324]: index.set_levels(["a", "b"], level=1)
Out[324]:
MultiIndex([(0, 'a'),
(0, 'b'),
(1, 'a'),
(1, 'b'),
(2, 'a'),
(2, 'b')],
names=['first', 'second'])
20.1 索引对象的集合操作
索引对象的集合操作包括 union
、intersection
和 difference
例如,差集
In [325]: a = pd.Index(['c', 'b', 'a'])
In [326]: b = pd.Index(['c', 'e', 'd'])
In [327]: a.difference(b)
Out[327]: Index(['a', 'b'], dtype='object')
并集和交集
>>> a.union(b)
Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
>>> a.intersection(b)
Index(['c'], dtype='object')
还可以使用 symmetric_difference
操作,它返回出现在 idx1
或 idx2
中的元素,但不同时出现在两个索引中的元素。这相当于
idx1.difference(idx2).union(idx2.difference(idx1))
In [328]: idx1 = pd.Index([1, 2, 3, 4])
In [329]: idx2 = pd.Index([2, 3, 4, 5])
In [330]: idx1.symmetric_difference(idx2)
Out[330]: Int64Index([1, 5], dtype='int64')
注意:集合操作产生的索引将按升序排序
当在具有不同 dtype
的索引之间执行 Index.union()
时,索引必须能转换为一个公共 dtype
。通常是 object dtype
比如,整数索引和浮点索引取并集
In [331]: idx1 = pd.Index([0, 1, 2])
In [332]: idx2 = pd.Index([0.5, 1.5])
In [333]: idx1.union(idx2)
Out[333]: Float64Index([0.0, 0.5, 1.0, 1.5, 2.0], dtype='float64')
20.2 缺失值
提示:虽然索引也支持缺失值(NaN
),但是最好不要使用,因为有些操作默认会忽略缺失值
可以使用 Index.fillna
用于指定的标量值来填充缺失值
In [334]: idx1 = pd.Index([1, np.nan, 3, 4])
In [335]: idx1
Out[335]: Float64Index([1.0, nan, 3.0, 4.0], dtype='float64')
In [336]: idx1.fillna(2)
Out[336]: Float64Index([1.0, 2.0, 3.0, 4.0], dtype='float64')
In [337]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011-01-01'),
.....: pd.NaT,
.....: pd.Timestamp('2011-01-03')])
.....:
In [338]: idx2
Out[338]: DatetimeIndex(['2011-01-01', 'NaT', '2011-01-03'], dtype='datetime64[ns]', freq=None)
In [339]: idx2.fillna(pd.Timestamp('2011-01-02'))
Out[339]: DatetimeIndex(['2011-01-01', '2011-01-02', '2011-01-03'], dtype='datetime64[ns]', freq=None)
21 设置和重置索引
有时,你会从数据集中加载或创建 DataFrame
,并希望添加一些索引。
一般有以下几种方法
21.1 设置索引
DataFrame
有一个 set_index()
方法,它接受一个列名或列名列表,来创建一个新的索引或重新创建索引
In [340]: data
Out[340]:
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 [341]: indexed1 = data.set_index('c')
In [342]: indexed1
Out[342]:
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 [343]: indexed2 = data.set_index(['a', 'b'])
In [344]: indexed2
Out[344]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
append
关键字参数允许你保留现有的索引,并将给定的列追加到索引
In [345]: frame = data.set_index('c', drop=False)
In [346]: frame = frame.set_index(['a', 'b'], append=True)
In [347]: frame
Out[347]:
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 [348]: data.set_index('c', drop=False)
Out[348]:
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 [349]: data.set_index(['a', 'b'], inplace=True)
In [350]: data
Out[350]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
21.2 重置索引
为了方便起见,DataFrame
上有一个名为 reset_index()
的函数,它将索引值添加到 DataFrame
的列中,并设置一个简单的整数索引。相当于 set_index()
的逆操作
In [351]: data
Out[351]:
c d
a b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
In [352]: data.reset_index()
Out[352]:
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
新列的列名为索引的 name
属性,你可以使用 level
关键字删除部分索引
In [353]: frame
Out[353]:
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 [354]: frame.reset_index(level=1)
Out[354]:
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
,如果为真,则直接丢弃索引,而不是将索引值放入 DataFrame
的列中
为索引赋值
data.index = index
22 返回视图或拷贝
在设置 pandas
对象中的值时,必须小心避免所谓的链接索引。
对于下面的一个例子
In [355]: dfmi = pd.DataFrame([list('abcd'),
.....: list('efgh'),
.....: list('ijkl'),
.....: list('mnop')],
.....: columns=pd.MultiIndex.from_product([['one', 'two'],
.....: ['first', 'second']]))
.....:
In [356]: dfmi
Out[356]:
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 [357]: dfmi['one']['second']
Out[357]:
0 b
1 f
2 j
3 n
Name: second, dtype: object
In [358]: dfmi.loc[:, ('one', 'second')]
Out[358]:
0 b
1 f
2 j
3 n
Name: (one, second), dtype: object
这两者产生相同的结果,那么您应该使用哪一种呢?应该如何理解这些操作的顺序呢?
对于第一种方法,dfmi['one']
先获取第一个 level
的数据,然后 ['second']
是对前一步的结果进一步取值的。调用了两次获取属性的方法 __getitem__
,这些步骤是分开的,像是链条一样一个接着一个
而 df.loc[:,('one','second')]
会解析为嵌套元组 (slice(None),('one','second'))
并调用一次 __getitem__
,这种操作的速度会更快。
22.1 执行顺序
使用链式索引时,索引操作的类型和顺序部分决定了返回的结果是原始对象的切片还是切片的拷贝。
pandas
之所以有 SettingWithCopyWarning
,是因为通常为一个切片的拷贝赋值是无意识的,而是链式索引返回一个切片的拷贝所造成的错误
如果你想让 pandas
或多或少地支持对链式索引表达式的赋值,可以将 mode.chained_assignment
设置为下面的值
'warn'
: 默认值,打印出SettingWithCopyWarning
信息'raise'
: 引发SettingWithCopyException
异常None
: 完全消除警告
In [359]: dfb = pd.DataFrame({'a': ['one', 'one', 'two',
.....: 'three', 'two', 'one', 'six'],
.....: 'c': np.arange(7)})
.....:
# 将会显示 SettingWithCopyWarning
# 但是设置值的操作还是会生效
In [360]: 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
注意:这些设置规则适用于所有 .loc/.iloc
以下是推荐使用的访问方法,在 .loc
中使用固定索引来访问单个或多个数据
In [361]: dfc = pd.DataFrame({'a': ['one', 'one', 'two',
.....: 'three', 'two', 'one', 'six'],
.....: 'c': np.arange(7)})
.....:
In [362]: dfd = dfc.copy()
# 设置多个值
In [363]: mask = dfd['a'].str.startswith('o')
In [364]: dfd.loc[mask, 'c'] = 42
In [365]: dfd
Out[365]:
a c
0 one 42
1 one 42
2 two 2
3 three 3
4 two 4
5 one 42
6 six 6
# 设置单个值
In [366]: dfd = dfc.copy()
In [367]: dfd.loc[2, 'a'] = 11
In [368]: dfd
Out[368]:
a c
0 one 0
1 one 1
2 11 2
3 three 3
4 two 4
5 one 5
6 six 6
下面的方法只是偶尔发挥作用,所以应该避免使用
In [369]: dfd = dfc.copy()
In [370]: dfd['a'][2] = 111
In [371]: dfd
Out[371]:
a c
0 one 0
1 one 1
2 111 2
3 three 3
4 two 4
5 one 5
6 six 6
最后的例子根本不起作用,所以应该避免
>>> pd.set_option('mode.chained_assignment','raise')
>>> dfd.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