在本章中,我们将着重于对来自 Pandas 对象的数据进行索引和选择。 这很重要,因为有效利用 Pandas 需要对索引和选择数据有充分的了解。 我们将在本章中讨论的主题包括:
- 基本索引
- 标签,整数和混合索引
- 多重索引
- 布尔索引
- 索引操作
在上一章中,我们已经讨论了有关序列和数据帧的基本索引,但是为了完整起见,这里我们将包括一些示例。 在这里,我们列出了根据 IMF 数据得出的 2013 年第 4 季度原油现货价格的时间序列。
In [642]:SpotCrudePrices_2013_Data={
'U.K. Brent' : {'2013-Q1':112.9, '2013-Q2':103.0, '2013-Q3':110.1, '2013-Q4':109.4},
'Dubai':{'2013-Q1':108.1, '2013-Q2':100.8, '2013-Q3':106.1,'2013-Q4':106.7},
'West Texas Intermediate':{'2013-Q1':94.4, '2013-Q2':94.2, '2013-Q3':105.8,'2013-Q4':97.4}}
SpotCrudePrices_2013=pd.DataFrame.from_dict(SpotCrudePrices_2013_Data)
SpotCrudePrices_2013
Out[642]: Dubai U.K. Brent West Texas Intermediate
2013-Q1 108.1 112.9 94.4
2013-Q2 100.8 103.0 94.2
2013-Q3 106.1 110.1 105.8
2013-Q4 106.7 109.4 97.4
我们可以使用[]
运算符为迪拜原油的可用时间段选择价格:
In [644]: dubaiPrices=SpotCrudePrices_2013['Dubai']; dubaiPrices
Out[644]: 2013-Q1 108.1
2013-Q2 100.8
2013-Q3 106.1
2013-Q4 106.7
Name: Dubai, dtype: float64
我们可以将列列表传递给[]
运算符,以便以特定顺序选择列:
In [647]: SpotCrudePrices_2013[['West Texas Intermediate','U.K. Brent']]
Out[647]: West Texas Intermediate U.K. Brent
2013-Q1 94.4 112.9
2013-Q2 94.2 103.0
2013-Q3 105.8 110.1
2013-Q4 97.4 109.4
如果我们指定未在数据帧中列出的列,则将出现KeyError
异常:
In [649]: SpotCrudePrices_2013['Brent Blend']
--------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-649-cd2d76b24875> in <module>()
...
KeyError: u'no item named Brent Blend'
我们可以通过使用get
运算符并在不存在该列的情况下指定默认值来避免此错误,如下所示:
In [650]: SpotCrudePrices_2013.get('Brent Blend','N/A')
Out[650]: 'N/A'
请注意,无法使用数据帧中的括号运算符[]
选择行。
因此,在以下情况下会出现错误:
In [755]:SpotCrudePrices_2013['2013-Q1']
--------------------------------------------------
KeyError Traceback (most recent call last)
...
KeyError: u'no item named 2013-Q1'
这是创作者为避免歧义而做出的设计决定。 对于序列,没有歧义,可以使用[]
运算符选择行:
In [756]: dubaiPrices['2013-Q1']
Out[756]: 108.1
我们将在本章后面看到如何使用一种较新的索引运算符执行行选择。
可以直接从序列,数据帧或面板中检索值作为属性,如下所示:
In [650]: SpotCrudePrices_2013.Dubai
Out[650]: 2013-Q1 108.1
2013-Q2 100.8
2013-Q3 106.1
2013-Q4 106.7
Name: Dubai, dtype: float64
但是,这仅在索引元素是有效的 Python 标识符时才有效,如下所示:
In [653]: SpotCrudePrices_2013."West Texas Intermediate"
File "<ipython-input-653-2a782563c15a>", line 1
SpotCrudePrices_2013."West Texas Intermediate"
^
SyntaxError: invalid syntax
否则,由于列名中的空格,我们将像前面的情况一样获得SyntaxError
。 有效的 Python 标识符必须遵循以下词汇约定:
identifier::= (letter|"_") (letter | digit | "_")*
因此,有效的 Python 标识符不能包含空格。 有关更多详细信息,请参见 Python 词法分析文档。
我们可以通过重命名列索引名称来解决这些问题,以便它们都是有效的标识符:
In [654]: SpotCrudePrices_2013
Out[654]: Dubai U.K. Brent West Texas Intermediate
2013-Q1 108.1 112.9 94.4
2013-Q2 100.8 103.0 94.2
2013-Q3 106.1 110.1 105.8
2013-Q4 106.7 109.4 97.4
In [655]:SpotCrudePrices_2013.columns=['Dubai','UK_Brent',
'West_Texas_Intermediate']
SpotCrudePrices_2013
Out[655]: Dubai UK_Brent West_Texas_Intermediate
2013-Q1 108.1 112.9 94.4
2013-Q2 100.8 103.0 94.2
2013-Q3 106.1 110.1 105.8
2013-Q4 106.7 109.4 97.4
然后,我们可以根据需要选择 West Texas Intermediate 的价格:
In [656]:SpotCrudePrices_2013.West_Texas_Intermediate
Out[656]:2013-Q1 94.4
2013-Q2 94.2
2013-Q3 105.8
2013-Q4 97.4
Name: West_Texas_Intermediate, dtype: float64
我们还可以通过指定列索引号以选择第 1 列(英国布伦特)来选择价格,如下所示:
In [18]: SpotCrudePrices_2013[[1]]
Out[18]: U.K. Brent
2013-Q1 112.9
2013-Q2 103.0
2013-Q3 110.1
2013-Q4 109.4
正如我们在第 3 章“pandas 数据结构”中有关 NumPy ndarray
的部分中所看到的那样,我们可以使用[]
运算符对范围进行切片。 切片运算符的语法与 NumPy 的语法完全匹配:
ar[startIndex: endIndex: stepValue]
如果未指定,则默认值如下:
startIndex
为 0endIndex
为arraysize - 1
stepValue
为 1
对于数据帧,[]
跨行切片如下:
获取前两行:
In [675]: SpotCrudePrices_2013[:2]
Out[675]: Dubai UK_Brent West_Texas_Intermediate
2013-Q1 108.1 112.9 94.4
2013-Q2 100.8 103.0 94.2
获取从索引 2 开始的所有行:
In [662]: SpotCrudePrices_2013[2:]
Out[662]: Dubai UK_Brent West_Texas_Intermediate
2013-Q3 106.1 110.1 105.8
2013-Q4 106.7 109.4 97.4
从第 0 行开始,以两间隔获取行:
In [664]: SpotCrudePrices_2013[::2]
Out[664]: Dubai UK_Brent West_Texas_Intermediate
2013-Q1 108.1 112.9 94.4
2013-Q3 106.1 110.1 105.8
反转数据帧中的行顺序:
In [677]: SpotCrudePrices_2013[::-1]
Out[677]: Dubai UK_Brent West_Texas_Intermediate
2013-Q4 106.7 109.4 97.4
2013-Q3 106.1 110.1 105.8
2013-Q2 100.8 103.0 94.2
2013-Q1 108.1 112.9 94.4
对于序列,其行为也很直观:
In [666]: dubaiPrices=SpotCrudePrices_2013['Dubai']
获取最后三行或除第一行外的所有行:
In [681]: dubaiPrices[1:]
Out[681]: 2013-Q2 100.8
2013-Q3 106.1
2013-Q4 106.7
Name: Dubai, dtype: float64
获取除最后一行以外的所有行:
In [682]: dubaiPrices[:-1]
Out[682]: 2013-Q1 108.1
2013-Q2 100.8
2013-Q3 106.1
Name: Dubai, dtype: float64
反转行:
In [683]: dubaiPrices[::-1]
Out[683]: 2013-Q4 106.7
2013-Q3 106.1
2013-Q2 100.8
2013-Q1 108.1
Name: Dubai, dtype: float64
除了标准索引运算符[]
和属性运算符外,pandas 中还提供了一些运算符,以使索引工作更轻松,更方便。 通过标签索引,我们通常是指通过标题名称进行索引,该标题名称在大多数情况下往往是字符串值。 这些运算符如下:
.loc
运算符:它允许基于标签的索引.iloc
运算符:它允许基于整数的索引.ix
运算符:它允许混合基于标签和整数的索引
现在,我们将注意力转向这些运算符。
.loc
运算符支持基于纯标签的索引。 它接受以下内容作为有效输入:
- 单个标签,例如
['March']
,[88]
或['Dubai']
。 请注意,在标签是整数的情况下,它不是引用索引的整数位置,而是引用整数本身作为标签。 - 标签列表或数组,例如
['Dubai', 'UK Brent']
。 - 带标签的切片对象,例如
'May':'Aug'
。 - 布尔数组。
对于我们的说明性数据集,我们使用以下城市的平均下雪天气温度数据:
- http://www.currentresults.com/Weather/New-York/Places/new-york-city-snowfall-totals-snow-accumulation-averages.php
- http://www.currentresults.com/Weather/New-York/Places/new-york-city-temperatures-by-month-average.php
创建数据帧
In [723]: NYC_SnowAvgsData={'Months' :
['January','February','March',
'April', 'November', 'December'],
'Avg SnowDays' : [4.0,2.7,1.7,0.2,0.2,2.3],
'Avg Precip. (cm)' : [17.8,22.4,9.1,1.5,0.8,12.2],
'Avg Low Temp. (F)' : [27,29,35,45,42,32] }
In [724]: NYC_SnowAvgsData
Out[724]:{'Avg Low Temp. (F)': [27, 29, 35, 45, 42, 32],
'Avg Precip. (cm)': [17.8, 22.4, 9.1, 1.5, 0.8, 12.2],
'Avg SnowDays': [4.0, 2.7, 1.7, 0.2, 0.2, 2.3],
'Months': ['January', 'February', 'March', 'April',
'November', 'December']}
In [726]:NYC_SnowAvgs=pd.DataFrame(NYC_SnowAvgsData,
index=NYC_SnowAvgsData['Months'],
columns=['Avg SnowDays','Avg Precip. (cm)',
'Avg Low Temp. (F)'])
NYC_SnowAvgs
Out[726]: Avg SnowDays Avg Precip. (cm) Avg Low Temp. (F)
January 4.0 17.8 27
February 2.7 22.4 29
March 1.7 9.1 35
April 0.2 1.5 45
November 0.2 0.8 42
December 2.3 12.2 32
使用单个标签:
In [728]: NYC_SnowAvgs.loc['January']
Out[728]: Avg SnowDays 4.0
Avg Precip. (cm) 17.8
Avg Low Temp. (F) 27.0
Name: January, dtype: float64
使用标签列表:
In [730]: NYC_SnowAvgs.loc[['January','April']]
Out[730]: Avg SnowDays Avg Precip. (cm) Avg Low Temp. (F)
January 4.0 17.8 27
April 0.2 1.5 45
使用标签范围:
In [731]: NYC_SnowAvgs.loc['January':'March']
Out[731]: Avg SnowDays Avg Precip. (cm) Avg Low Temp. (F)
January 4.0 17.8 27
February 2.7 22.4 29
March 1.7 9.1 35
请注意,在数据帧上使用.loc
,.iloc
和.ix
运算符时,必须始终首先指定行索引。 这与[]
运算符相反,后者只能直接选择列。 因此,如果执行以下操作,则会出现错误:
In [771]: NYC_SnowAvgs.loc['Avg SnowDays']
KeyError: 'Avg SnowDays'
正确的方法是使用冒号(:
)运算符专门选择所有行,如下所示:
In [772]: NYC_SnowAvgs.loc[:,'Avg SnowDays']
Out[772]: January 4.0
February 2.7
March 1.7
April 0.2
November 0.2
December 2.3
Name: Avg SnowDays, dtype: float64
在这里,我们看到如何选择一个特定的坐标值,即三月份的平均下雪天数:
In [732]: NYC_SnowAvgs.loc['March','Avg SnowDays']
Out[732]: 1.7
还支持这种替代样式:
In [733]: NYC_SnowAvgs.loc['March']['Avg SnowDays']
Out[733]: 1.7
以下是使用方括号运算符[]
的前述情况的等效内容:
In [750]: NYC_SnowAvgs['Avg SnowDays']['March']
Out[750]: 1.7
再次注意,但是,首先使用.loc
运算符指定行索引值将得到Keyerror
。 这是前面讨论的事实的结果,即[]
运算符不能用于直接选择行。 必须首先选择列以获得序列,然后可以按行选择。 因此,如果使用以下任一方法,则将获得KeyError: u'no item named March'
:
In [757]: NYC_SnowAvgs['March']['Avg SnowDays']
要么
In [758]: NYC_SnowAvgs['March']
我们可以使用.loc
运算符来选择行:
In [759]: NYC_SnowAvgs.loc['March']
Out[759]: Avg SnowDays 1.7
Avg Precip. (cm) 9.1
Avg Low Temp. (F) 35.0
Name: March, dtype: float64
现在,我们将展示如何使用布尔数组选择平均下雪天少于一个的月份:
In [763]: NYC_SnowAvgs.loc[NYC_SnowAvgs['Avg SnowDays']<1,:]
Out[763]: Avg SnowDays Avg Precip. (cm) Avg Low Temp. (F)
April 0.2 1.5 45
November 0.2 0.8 42
或者,对于前面提到的现货原油价格,在2013-Q1
行中,选择与价格高于每桶 110 美元的原油品牌相对应的列:
In [768]: SpotCrudePrices_2013.loc[:,SpotCrudePrices_2013.loc['2013-Q1']>110]
Out[768]: UK_Brent
2013-Q1 112.9
2013-Q2 103.0
2013-Q3 110.1
2013-Q4 109.4
请注意,前面的参数涉及实际计算布尔数组的布尔运算符<
和>
,例如:
In [769]: SpotCrudePrices_2013.loc['2013-Q1']>110
Out[769]: Dubai False
UK_Brent True
West_Texas_Intermediate False
Name: 2013-Q1, dtype: bool
.iloc
运算符支持基于整数的位置索引。 它接受以下内容作为输入:
- 一个整数,例如 7
- 整数列表或数组,例如
[2, 3]
- 具有整数的切片对象,例如
1:4
让我们创建以下内容:
In [777]: import scipy.constants as phys
import math
In [782]: sci_values=pd.DataFrame([[math.pi, math.sin(math.pi),
math.cos(math.pi)],
[math.e,math.log(math.e),
phys.golden],
[phys.c,phys.g,phys.e],
[phys.m_e,phys.m_p,phys.m_n]],
index=list(range(0,20,5)))
Out[782]: 0 1 2
0 3.141593e+00 1.224647e-16 -1.000000e+00
5 2.718282e+00 1.000000e+00 1.618034e+00
10 2.997925e+08 9.806650e+00 1.602177e-19
15 9.109383e-31 1.672622e-27 1.674927e-27
我们可以使用整数切片在前两行中选择非物理常数:
In [789]: sci_values.iloc[:2]
Out[789]: 0 1 2
0 3.141593 1.224647e-16 -1.000000
5 2.718282 1.000000e+00 1.618034
或者,我们可以在第三行中使用光速和重力加速度:
In [795]: sci_values.iloc[2,0:2]
Out[795]: 0 2.997925e+08
1 9.806650e+00
dtype: float64
请注意,.iloc
的参数严格位于位置,与索引值无关。 因此,请考虑以下情况,我们错误地认为可以通过使用以下命令获得第三行:
In [796]: sci_values.iloc[10]
------------------------------------------------------
IndexError Traceback (most recent call last)
...
IndexError: index 10 is out of bounds for axis 0 with size 4
在这里,我们得到前面结果中的IndexError
; 因此,现在,我们应改为使用标签索引运算符.loc
,如下所示:
In [797]: sci_values.loc[10]
Out[797]: 0 2.997925e+08
1 9.806650e+00
2 1.602177e-19
Name: 10, dtype: float64
要切出特定的行,我们可以使用以下命令:
In [802]: sci_values.iloc[2:3,:]
Out[802]: 0 1 2
10 299792458 9.80665 1.602177e-19
要使用整数位置获取横截面,请使用以下命令:
In [803]: sci_values.iloc[3]
Out[803]: 0 9.109383e-31
1 1.672622e-27
2 1.674927e-27
Name: 15, dtype: float64
如果我们尝试切片超过数组的末尾,则我们将获得IndexError
,如下所示:
In [805]: sci_values.iloc[6,:]
--------------------------------------------------------
IndexError Traceback (most recent call last)
IndexError: index 6 is out of bounds for axis 0 with size 4
.iat
和.at
运算符可用于快速选择标量值。 最好的说明如下:
In [806]: sci_values.iloc[3,0]
Out[806]: 9.1093829099999999e-31
In [807]: sci_values.iat[3,0]
Out[807]: 9.1093829099999999e-31
In [808]: %timeit sci_values.iloc[3,0]
10000 loops, best of 3: 122 μs per loop
In [809]: %timeit sci_values.iat[3,0]
10000 loops, best of 3: 28.4 μs per loop
因此,我们可以看到.iat
比.iloc
/ .ix
运算符快得多。 .at
与.loc
的情况相同。
.ix
运算符的行为类似于.loc
和.iloc
运算符的混合,其中.loc
行为优先。 它采用以下作为可能的输入:
- 单个标签或整数
- 整数或标签列表
- 整数切片或标签切片
- 布尔数组
让我们通过将股票指数收盘价数据保存到文件(stock_index_closing.csv
)并将其读取来重新创建以下数据帧:
TradingDate,Nasdaq,S&P 500,Russell 2000
2014/01/30,4123.13,1794.19,1139.36
2014/01/31,4103.88,1782.59,1130.88
2014/02/03,3996.96,1741.89,1094.58
2014/02/04,4031.52,1755.2,1102.84
2014/02/05,4011.55,1751.64,1093.59
2014/02/06,4057.12,1773.43,1103.93
该数据的来源是这里。 这是我们将 CSV 数据读入数据帧的方法:
In [939]: stockIndexDataDF=pd.read_csv('./stock_index_data.csv')
In [940]: stockIndexDataDF
Out[940]: TradingDate Nasdaq S&P 500 Russell 2000
0 2014/01/30 4123.13 1794.19 1139.36
1 2014/01/31 4103.88 1782.59 1130.88
2 2014/02/03 3996.96 1741.89 1094.58
3 2014/02/04 4031.52 1755.20 1102.84
4 2014/02/05 4011.55 1751.64 1093.59
5 2014/02/06 4057.12 1773.43 1103.93
从前面的示例中可以看到,创建的数据帧具有基于整数的行索引。 我们立即将索引设置为交易日期,以便根据交易日期对其进行索引,以便可以使用.ix
运算符:
In [941]: stockIndexDF=stockIndexDataDF.set_index('TradingDate')
In [942]:stockIndexDF
Out[942]: Nasdaq S&P 500 Russell 2000
TradingDate
2014/01/30 4123.13 1794.19 1139.36
2014/01/31 4103.88 1782.59 1130.88
2014/02/03 3996.96 1741.89 1094.58
2014/02/04 4031.52 1755.20 1102.84
2014/02/05 4011.55 1751.64 1093.59
2014/02/06 4057.12 1773.43 1103.93
现在,我们展示使用.ix
运算符的示例:
Using a single label:
In [927]: stockIndexDF.ix['2014/01/30']
Out[927]: Nasdaq 4123.13
S&P 500 1794.19
Russell 2000 1139.36
Name: 2014/01/30, dtype: float64
Using a list of labels:
In [928]: stockIndexDF.ix[['2014/01/30']]
Out[928]: Nasdaq S&P 500 Russell 2000
2014/01/30 4123.13 1794.19 1139.36
In [930]: stockIndexDF.ix[['2014/01/30','2014/01/31']]
Out[930]: Nasdaq S&P 500 Russell 2000
2014/01/30 4123.13 1794.19 1139.36
2014/01/31 4103.88 1782.59 1130.88
请注意,使用单个标签与使用仅包含单个标签的列表之间的输出差异。 前者产生序列,而后者产生一个数据帧:
In [943]: type(stockIndexDF.ix['2014/01/30'])
Out[943]: pandas.core.series.Series
In [944]: type(stockIndexDF.ix[['2014/01/30']])
Out[944]: pandas.core.frame.DataFrame
对于前者,索引器是一个标量; 对于后者,索引器是一个列表。 列表索引器用于选择多个列。 一个数据帧的多列切片只能生成另一个数据帧,因为它是 2D 的。 因此,在后一种情况下返回的是一个数据帧。 使用基于标签的切片:
In [932]: tradingDates=stockIndexDataDF.TradingDate
In [934]: stockIndexDF.ix[tradingDates[:3]]
Out[934]: Nasdaq S&P 500 Russell 2000
2014/01/30 4123.13 1794.19 1139.36
2014/01/31 4103.88 1782.59 1130.88
2014/02/03 3996.96 1741.89 1094.58
使用单个整数:
In [936]: stockIndexDF.ix[0]
Out[936]: Nasdaq 4123.13
S&P 500 1794.19
Russell 2000 1139.36
Name: 2014/01/30, dtype: float64
使用整数列表:
In [938]: stockIndexDF.ix[[0,2]]
Out[938]: Nasdaq S&P 500 Russell 2000
TradingDate
2014/01/30 4123.13 1794.19 1139.36
2014/02/03 3996.96 1741.89 1094.58
使用整数切片:
In [947]: stockIndexDF.ix[1:3]
Out[947]: Nasdaq S&P 500 Russell 2000
TradingDate
2014/01/31 4103.88 1782.59 1130.88
2014/02/03 3996.96 1741.89 1094.58
使用布尔数组:
In [949]: stockIndexDF.ix[stockIndexDF['Russell 2000']>1100]
Out[949]: Nasdaq S&P 500 Russell 2000
TradingDate
2014/01/30 4123.13 1794.19 1139.36
2014/01/31 4103.88 1782.59 1130.88
2014/02/04 4031.52 1755.20 1102.84
2014/02/06 4057.12 1773.43 1103.93
与.loc
的情况一样,必须首先为.ix
运算符指定行索引。
现在我们转到多重索引的主题。 多级或分层索引很有用,因为它使 Pandas 用户可以使用序列和数据帧等数据结构来选择和按摩多维数据。 为了开始,让我们将以下数据保存到文件中:stock_index_prices.csv
并读入:
TradingDate,PriceType,Nasdaq,S&P 500,Russell 2000
2014/02/21,open,4282.17,1841.07,1166.25
2014/02/21,close,4263.41,1836.25,1164.63
2014/02/21,high,4284.85,1846.13,1168.43
2014/02/24,open,4273.32,1836.78,1166.74
2014/02/24,close,4292.97,1847.61,1174.55
2014/02/24,high,4311.13,1858.71,1180.29
2014/02/25,open,4298.48,1847.66,1176
2014/02/25,close,4287.59,1845.12,1173.95
2014/02/25,high,4307.51,1852.91,1179.43
2014/02/26,open,4300.45,1845.79,1176.11
2014/02/26,close,4292.06,1845.16,1181.72
2014/02/26,high,4316.82,1852.65,1188.06
2014/02/27,open,4291.47,1844.9,1179.28
2014/02/27,close,4318.93,1854.29,1187.94
2014/02/27,high,4322.46,1854.53,1187.94
2014/02/28,open,4323.52,1855.12,1189.19
2014/02/28,close,4308.12,1859.45,1183.03
2014/02/28,high,4342.59,1867.92,1193.5
In [950]:sharesIndexDataDF=pd.read_csv('./stock_index_prices.csv')
In [951]: sharesIndexDataDF
Out[951]:
TradingDate PriceType Nasdaq S&P 500 Russell 2000
0 2014/02/21 open 4282.17 1841.07 1166.25
1 2014/02/21 close 4263.41 1836.25 1164.63
2 2014/02/21 high 4284.85 1846.13 1168.43
3 2014/02/24 open 4273.32 1836.78 1166.74
4 2014/02/24 close 4292.97 1847.61 1174.55
5 2014/02/24 high 4311.13 1858.71 1180.29
6 2014/02/25 open 4298.48 1847.66 1176.00
7 2014/02/25 close 4287.59 1845.12 1173.95
8 2014/02/25 high 4307.51 1852.91 1179.43
9 2014/02/26 open 4300.45 1845.79 1176.11
10 2014/02/26 close 4292.06 1845.16 1181.72
11 2014/02/26 high 4316.82 1852.65 1188.06
12 2014/02/27 open 4291.47 1844.90 1179.28
13 2014/02/27 close 4318.93 1854.29 1187.94
14 2014/02/27 high 4322.46 1854.53 1187.94
15 2014/02/28 open 4323.52 1855.12 1189.19
16 2014/02/28 close 4308.12 1859.45 1183.03
17 2014/02/28 high 4342.59 1867.92 1193.50
在这里,我们从交易日期和priceType
列创建一个多重索引:
In [958]: sharesIndexDF=sharesIndexDataDF.set_index(['TradingDate','PriceType'])
In [959]: mIndex=sharesIndexDF.index; mIndex
Out[959]: MultiIndex
[(u'2014/02/21', u'open'), (u'2014/02/21', u'close'), (u'2014/02/21', u'high'), (u'2014/02/24', u'open'), (u'2014/02/24', u'close'), (u'2014/02/24', u'high'), (u'2014/02/25', u'open'), (u'2014/02/25', u'close'), (u'2014/02/25', u'high'), (u'2014/02/26', u'open'), (u'2014/02/26', u'close'), (u'2014/02/26', u'high'), (u'2014/02/27', u'open'), (u'2014/02/27', u'close'), (u'2014/02/27', u'high'), (u'2014/02/28', u'open'), (u'2014/02/28', u'close'), (u'2014/02/28', u'high')]
In [960]: sharesIndexDF
Out[960]: Nasdaq S&P 500 Russell 2000
TradingDate PriceType
2014/02/21 open 4282.17 1841.07 1166.25
close 4263.41 1836.25 1164.63
high 4284.85 1846.13 1168.43
2014/02/24 open 4273.32 1836.78 1166.74
close 4292.97 1847.61 1174.55
high 4311.13 1858.71 1180.29
2014/02/25 open 4298.48 1847.66 1176.00
close 4287.59 1845.12 1173.95
high 4307.51 1852.91 1179.43
2014/02/26 open 4300.45 1845.79 1176.11
close 4292.06 1845.16 1181.72
high 4316.82 1852.65 1188.06
2014/02/27 open 4291.47 1844.90 1179.28
close 4318.93 1854.29 1187.94
high 4322.46 1854.53 1187.94
2014/02/28 open 4323.52 1855.12 1189.19
close 4308.12 1859.45 1183.03
high 4342.59 1867.92 1193.50
经过检查,我们发现多重索引包含一个元组列表。 将get_level_values
函数与适当的参数一起应用将为索引的每个级别生成标签列表:
In [962]: mIndex.get_level_values(0)
Out[962]: Index([u'2014/02/21', u'2014/02/21', u'2014/02/21', u'2014/02/24', u'2014/02/24', u'2014/02/24', u'2014/02/25', u'2014/02/25', u'2014/02/25', u'2014/02/26', u'2014/02/26', u'2014/02/26', u'2014/02/27', u'2014/02/27', u'2014/02/27', u'2014/02/28', u'2014/02/28', u'2014/02/28'], dtype=object)
In [963]: mIndex.get_level_values(1)
Out[963]: Index([u'open', u'close', u'high', u'open', u'close', u'high', u'open', u'close', u'high', u'open', u'close', u'high', u'open', u'close', u'high', u'open', u'close', u'high'], dtype=object)
但是,如果传递给get_level_values()
的值无效或超出范围,则会抛出IndexError
:
In [88]: mIndex.get_level_values(2)
---------------------------------------------------------
IndexError Traceback (most recent call last)
...
您可以使用多重索引的数据帧实现分层索引:
In [971]: sharesIndexDF.ix['2014/02/21']
Out[971]: Nasdaq S&P 500 Russell 2000
PriceType
open 4282.17 1841.07 1166.25
close 4263.41 1836.25 1164.63
high 4284.85 1846.13 1168.43
In [976]: sharesIndexDF.ix['2014/02/21','open']
Out[976]: Nasdaq 4282.17
S&P 500 1841.07
Russell 2000 1166.25
Name: (2014/02/21, open), dtype: float64
我们可以使用多重索引进行切片:
In [980]: sharesIndexDF.ix['2014/02/21':'2014/02/24']
Out[980]: Nasdaq S&P 500 Russell 2000
TradingDate PriceType
2014/02/21 open 4282.17 1841.07 1166.25
close 4263.41 1836.25 1164.63
high 4284.85 1846.13 1168.43
2014/02/24 open 4273.32 1836.78 1166.74
close 4292.97 1847.61 1174.55
high 4311.13 1858.71 1180.29
我们可以尝试在较低级别进行切片:
In [272]:
sharesIndexDF.ix[('2014/02/21','open'):('2014/02/24','open')]
------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-272-65bb3364d980> in <module>()
----> 1 sharesIndexDF.ix[('2014/02/21','open'):('2014/02/24','open')]
...
KeyError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
但是,这会导致KeyError
出现非常奇怪的错误消息。 这里要学习的关键知识是,多重索引的当前版本要求对标签进行排序,以使较低级别的切片例程正常工作。
为此,您可以利用sortlevel()
方法对多重索引中的轴的标签进行排序。 为了安全起见,在使用多重索引切片之前,请先进行排序。 因此,我们可以执行以下操作:
In [984]: sharesIndexDF.sortlevel(0).ix[('2014/02/21','open'):('2014/02/24','open')]
Out[984]: Nasdaq S&P 500 Russell 2000
TradingDate PriceType
2014/02/21 open 4282.17 1841.07 1166.25
2014/02/24 close 4292.97 1847.61 1174.55
high 4311.13 1858.71 1180.29
open 4273.32 1836.78 1166.74
我们还可以传递一个元组列表:
In [985]: sharesIndexDF.ix[[('2014/02/21','close'),('2014/02/24','open')]]
Out[985]: Nasdaq S&P 500 Russell 2000
TradingDate PriceType
2014/02/21 close 4263.41 1836.25 1164.63
2014/02/24 open 4273.32 1836.78 1166.74
2 rows × 3 columns
请注意,通过指定一个元组列表,而不是前面的示例中的范围,我们仅显示打开的PriceType
的值,而不显示TradingDate 2014/02/24
的全部三个值。
swaplevel
函数可在多重索引中交换级别:
In [281]: swappedDF=sharesIndexDF[:7].swaplevel(0, 1, axis=0)
swappedDF
Out[281]: Nasdaq S&P 500 Russell 2000
PriceType TradingDate
open 2014/02/21 4282.17 1841.07 1166.25
close 2014/02/21 4263.41 1836.25 1164.63
high 2014/02/21 4284.85 1846.13 1168.43
open 2014/02/24 4273.32 1836.78 1166.74
close 2014/02/24 4292.97 1847.61 1174.55
high 2014/02/24 4311.13 1858.71 1180.29
open 2014/02/25 4298.48 1847.66 1176.00
7 rows × 3 columns
reorder_levels
函数更通用,允许您指定级别的顺序:
In [285]: reorderedDF=sharesIndexDF[:7].reorder_levels(['PriceType',
'TradingDate'],
axis=0)
reorderedDF
Out[285]: Nasdaq S&P 500 Russell 2000
PriceType TradingDate
open 2014/02/21 4282.17 1841.07 1166.25
close 2014/02/21 4263.41 1836.25 1164.63
high 2014/02/21 4284.85 1846.13 1168.43
open 2014/02/24 4273.32 1836.78 1166.74
close 2014/02/24 4292.97 1847.61 1174.55
high 2014/02/24 4311.13 1858.71 1180.29
open 2014/02/25 4298.48 1847.66 1176.00
7 rows × 3 columns
xs
方法提供了一种基于特定索引级别值选择数据的快捷方式:
In [287]: sharesIndexDF.xs('open',level='PriceType')
Out[287]:
Nasdaq S&P 500 Russell 2000
TradingDate
2014/02/21 4282.17 1841.07 1166.25
2014/02/24 4273.32 1836.78 1166.74
2014/02/25 4298.48 1847.66 1176.00
2014/02/26 4300.45 1845.79 1176.11
2014/02/27 4291.47 1844.90 1179.28
2014/02/28 4323.52 1855.12 1189.19
6 rows × 3 columns
对于上述命令,更复杂的选择是使用swaplevel
在TradingDate
和PriceType
级别之间切换,然后执行以下选择:
In [305]: sharesIndexDF.swaplevel(0, 1, axis=0).ix['open']
Out[305]: Nasdaq S&P 500 Russell 2000
TradingDate
2014/02/21 4282.17 1841.07 1166.25
2014/02/24 4273.32 1836.78 1166.74
2014/02/25 4298.48 1847.66 1176.00
2014/02/26 4300.45 1845.79 1176.11
2014/02/27 4291.47 1844.90 1179.28
2014/02/28 4323.52 1855.12 1189.19
6 rows × 3 columns
使用.xs
具有与上一节有关面向整数的索引的横截面相同的效果。
我们使用布尔索引来过滤或选择部分数据。 运算符如下:
运算符 | 符号 |
---|---|
或 | | |
与 | & |
非 | ~ |
这些运算符一起使用时,必须使用括号进行分组。 使用上一部分中较早的数据帧,在这里,我们显示纳斯达克收盘价高于 4300 的交易日期:
In [311]: sharesIndexDataDF.ix[(sharesIndexDataDF['PriceType']=='close') & \
(sharesIndexDataDF['Nasdaq']>4300) ]
Out[311]: PriceType Nasdaq S&P 500 Russell 2000
TradingDate
2014/02/27 close 4318.93 1854.29 1187.94
2014/02/28 close 4308.12 1859.45 1183.03
2 rows × 4 columns
您还可以创建布尔条件,在其中可以使用数组过滤掉部分数据:
In [316]: highSelection=sharesIndexDataDF['PriceType']=='high'
NasdaqHigh=sharesIndexDataDF['Nasdaq']<4300
sharesIndexDataDF.ix[highSelection & NasdaqHigh]
Out[316]: TradingDate PriceType Nasdaq S&P 500 Russell 2000
2014/02/21 high 4284.85 1846.13 1168.43
因此,前面的代码段显示了整个交易时段中纳斯达克综合指数保持在 4300 水平以下的数据集中的唯一日期。
与前几节中使用的标准运算符相比,这些方法使用户可以通过布尔索引实现更多功能。 isin
方法获取值列表,并在序列或数据帧中与列表中的值匹配的位置返回带有True
的布尔数组。 这使用户可以检查序列中是否存在一个或多个元素。 这是使用序列的插图:
In [317]:stockSeries=pd.Series(['NFLX','AMZN','GOOG','FB','TWTR'])
stockSeries.isin(['AMZN','FB'])
Out[317]:0 False
1 True
2 False
3 True
4 False
dtype: bool
在这里,我们使用布尔数组选择一个包含我们感兴趣的值的子序列:
In [318]: stockSeries[stockSeries.isin(['AMZN','FB'])]
Out[318]: 1 AMZN
3 FB
dtype: object
对于我们的数据帧示例,我们切换到一个更有趣的数据集,该数据集是针对那些对人类生物学有偏爱,对澳大利亚哺乳动物进行分类(属于我的宠物)的数据集:
In [324]: australianMammals=
{'kangaroo': {'Subclass':'marsupial',
'Species Origin':'native'},
'flying fox' : {'Subclass':'placental',
'Species Origin':'native'},
'black rat': {'Subclass':'placental',
'Species Origin':'invasive'},
'platypus' : {'Subclass':'monotreme',
'Species Origin':'native'},
'wallaby' : {'Subclass':'marsupial',
'Species Origin':'native'},
'palm squirrel' : {'Subclass':'placental',
'Origin':'invasive'},
'anteater': {'Subclass':'monotreme', 'Origin':'native'},
'koala': {'Subclass':'marsupial', 'Origin':'native'}
}
有关哺乳动物的更多信息:有袋动物是袋装哺乳动物,单峰类是产卵的,胎盘可生幼年。 该信息的来源是这里。
上一个图像的来源是 Bennett 的小袋鼠。
In [328]: ozzieMammalsDF=pd.DataFrame(australianMammals)
In [346]: aussieMammalsDF=ozzieMammalsDF.T; aussieMammalsDF
Out[346]: Subclass Origin
anteater monotreme native
black rat placental invasive
flying fox placental native
kangaroo marsupial native
koala marsupial native
palm squirrel placental invasive
platypus monotreme native
wallaby marsupial native
8 rows × 2 columns
让我们尝试选择澳大利亚本土的哺乳动物:
In [348]: aussieMammalsDF.isin({'Subclass':['marsupial'],'Origin':['native']})
Out[348]: Subclass Origin
anteater False True
black rat False False
flying fox False True
kangaroo True True
koala True True
palm squirrel False False
platypus False True
wallaby True True
8 rows × 2 columns
传递给isin
的一组值可以是数组或字典。 这种方法有些奏效,但是我们可以通过结合isin
和all()
方法创建遮罩来获得更好的结果:
In [349]: nativeMarsupials={'Mammal Subclass':['marsupial'],
'Species Origin':['native']}
nativeMarsupialMask=aussieMammalsDF.isin(nativeMarsupials).all(True)
aussieMammalsDF[nativeMarsupialMask]
Out[349]: Subclass Origin
kangaroo marsupial native
koala marsupial native
wallaby marsupial native
3 rows × 2 columns
因此,我们看到袋鼠,考拉和小袋鼠是我们数据集中的原生有袋动物。 any()
方法返回布尔数据帧中是否有任何元素为True
。 all()
方法过滤器返回布尔数据帧中是否所有元素都是True
。
其来源是这里。
where()
方法用于确保布尔过滤的结果与原始数据具有相同的形状。 首先,我们将随机数生成器种子设置为 100,以便用户可以生成如下所示的相同值:
In [379]: np.random.seed(100)
normvals=pd.Series([np.random.normal() for i in np.arange(10)])
normvals
Out[379]: 0 -1.749765
1 0.342680
2 1.153036
3 -0.252436
4 0.981321
5 0.514219
6 0.221180
7 -1.070043
8 -0.189496
9 0.255001
dtype: float64
In [381]: normvals[normvals>0]
Out[381]: 1 0.342680
2 1.153036
4 0.981321
5 0.514219
6 0.221180
9 0.255001
dtype: float64
In [382]: normvals.where(normvals>0)
Out[382]: 0 NaN
1 0.342680
2 1.153036
3 NaN
4 0.981321
5 0.514219
6 0.221180
7 NaN
8 NaN
9 0.255001
dtype: float64
此方法似乎仅在序列情况下有用,因为在数据帧情况下我们免费获得此行为:
In [393]: np.random.seed(100)
normDF=pd.DataFrame([[round(np.random.normal(),3) for i in np.arange(5)] for j in range(3)],
columns=['0','30','60','90','120'])
normDF
Out[393]: 0 30 60 90 120
0 -1.750 0.343 1.153 -0.252 0.981
1 0.514 0.221 -1.070 -0.189 0.255
2 -0.458 0.435 -0.584 0.817 0.673
3 rows × 5 columns
In [394]: normDF[normDF>0]
Out[394]: 0 30 60 90 120
0 NaN 0.343 1.153 NaN 0.981
1 0.514 0.221 NaN NaN 0.255
2 NaN 0.435 NaN 0.817 0.673
3 rows × 5 columns
In [395]: normDF.where(normDF>0)
Out[395]: 0 30 60 90 120
0 NaN 0.343 1.153 NaN 0.981
1 0.514 0.221 NaN NaN 0.255
2 NaN 0.435 NaN 0.817 0.673
3 rows × 5 columns
where
方法的逆运算为mask
:
In [396]: normDF.mask(normDF>0)
Out[396]: 0 30 60 90 120
0 -1.750 NaN NaN -0.252 NaN
1 NaN NaN -1.070 -0.189 NaN
2 -0.458 NaN -0.584 NaN NaN
3 rows × 5 columns
为了完成本章,我们将讨论索引的操作。 当我们希望重新对齐数据或以其他方式选择数据时,有时需要对索引进行操作。 有多种操作:
set_index
-允许在现有数据帧上创建索引并返回索引的数据帧。
正如我们之前所见:
In [939]: stockIndexDataDF=pd.read_csv('./stock_index_data.csv')
In [940]: stockIndexDataDF
Out[940]: TradingDate Nasdaq S&P 500 Russell 2000
0 2014/01/30 4123.13 1794.19 1139.36
1 2014/01/31 4103.88 1782.59 1130.88
2 2014/02/03 3996.96 1741.89 1094.58
3 2014/02/04 4031.52 1755.20 1102.84
4 2014/02/05 4011.55 1751.64 1093.59
5 2014/02/06 4057.12 1773.43 1103.93
现在,我们可以如下设置索引:
In [941]: stockIndexDF=stockIndexDataDF.set_index('TradingDate')
In [942]: stockIndexDF
Out[942]: Nasdaq S&P 500 Russell 2000
TradingDate
2014/01/30 4123.13 1794.19 1139.36
2014/01/31 4103.88 1782.59 1130.88
2014/02/03 3996.96 1741.89 1094.58
2014/02/04 4031.52 1755.20 1102.84
2014/02/05 4011.55 1751.64 1093.59
2014/02/06 4057.12 1773.43 1103.93
reset_index
反转set_index
:
In [409]: stockIndexDF.reset_index()
Out[409]:
TradingDate Nasdaq S&P 500 Russell 2000
0 2014/01/30 4123.13 1794.19 1139.36
1 2014/01/31 4103.88 1782.59 1130.88
2 2014/02/03 3996.96 1741.89 1094.58
3 2014/02/04 4031.52 1755.20 1102.84
4 2014/02/05 4011.55 1751.64 1093.59
5 2014/02/06 4057.12 1773.43 1103.93
6 rows × 4 columns
总而言之,有多种方法可以从 Pandas 中选择数据:
- 我们可以使用基本索引,这与我们对访问数组中数据的了解最接近。
- 我们可以将基于标签或整数的索引与关联的运算符一起使用。
- 我们可以使用多重索引,它是包含多个字段的复合键的 Pandas 版本。
- 我们可以使用布尔/逻辑索引。
有关在 Pandas 中建立索引的更多参考,请查看官方文档。
在下一章中,我们将研究使用 Pandas 对数据进行分组,重塑和合并的主题。