Pandas之索引和查看数据
细致的朋友可能会发现一个现象,不论是序列也好,还是数据框也好,对象的最左边总有一个非原始数据对象,这个是什么呢?不错,就是我们接下来要介绍的索引。
在我看来,序列或数据框的索引有两大用处,一个是通过索引值或索引标签获取目标数据,另一个是通过索引,可以使序列或数据框的计算、操作实现自动化对齐。
1、索引对象
Index
pandas的索引对象用来保存坐标轴标签和其它元数据(如坐标轴名或名称)。构建一个Series或DataFrame时通过index属性来访问索性对象。
import pandas as pd
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index
index
>>:
Out[1]:
Index(['a', 'b', 'c'], dtype='object')
如果一个DataFrame的index和columns有它们的name,也会被显示出来:
pop = {'Nevada': {2001: 2.4, 2002: 2.9},'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = pd.DataFrame(pop)
print(frame3)
print('\n',frame3.columns)
print(frame3.index)
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
Index(['Nevada', 'Ohio'], dtype='object')
Int64Index([2000, 2001, 2002], dtype='int64')
索引对象是不可变
索引对象是不可变的,因此不能由用户改变,下列操作将报错:
import pandas as pd
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index
index
index[1] = 'd'
输出:
索引对象的不可变性非常重要,这样它可以在数据结构中安全的共享,单独创建一个index对象赋值给dataframe,如果中途修改了index的值,运行期间会报错"TypeError: Index does not support mutable operations"
import numpy as np
index = pd.Index(np.arange(3))
obj2 = pd.Series([1.5, -2.5, 0], index=index)
obj2.index is index
Out[10]:True
修改index报错:
obj2.index[1]=10
print(obj2)
索引的函数(了解,不重点介绍)
下述表格是库中内建的索引类清单。通过一些开发努力,索引可以被子类化,来实现特定坐标轴索引功能。
Index | 最通用的索引对象,使用Python对象的NumPy数组来表示坐标轴标签。 |
---|---|
Int64Index | 对整形值的特化索引。 |
MultiIndex | “分层”索引对象,表示单个轴的多层次的索引。可以被认为是类似的元组的数组。 |
DatetimeIndex | 存储纳秒时间戳(使用NumPy的datetime64 dtyppe来表示)。 |
PeriodIndex | 对周期数据(时间间隔的)的特化索引。 |
除了类似于阵列,索引也有类似固定大小集合一样的功能:
print(frame3)
#in的这种语法很像遍历数组
print('Ohio' in frame3.columns)
print(2003 in frame3.index)
输出:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
True
False
每个索引都有许多关于集合逻辑的方法和属性,且能够解决它所包含的数据的常见问题,如下述表格:
append | 链接额外的索引对象,产生一个新的索引 |
---|---|
diff | 计算索引的差集 |
intersection | 计算交集 |
union | 计算并集 |
isin | 计算出一个布尔数组表示每一个值是否包含在所传递的集合里 |
delete | 计算删除位置i的元素的索引 |
drop | 计算删除所传递的值后的索引 |
insert | 计算在位置i插入元素后的索引 |
is_monotonic | 返回True,如果每一个元素都比它前面的元素大或相等 |
is_unique | 返回True,如果索引没有重复的值 |
unique | 计算索引的唯一值数组 |
索性的自动对齐
如果有两个序列,需要对这两个序列进行算术运算,这时索引的存在就体现的它的价值了—自动化对齐.
s5 = pd.Series(np.array([10,15,20,30,55,80]),index = ['a','b','c','d','e','f'])
print(s5)
s6 = pd.Series(np.array([12,11,13,15,14,16]), index = ['a','c','g','b','d','f'])
print('\n',s6)
print('\n',s5 + s6)
print('\n',s5/s6)
输出:
a 10
b 15
c 20
d 30
e 55
f 80
dtype: int64
a 12
c 11
g 13
b 15
d 14
f 16
dtype: int64
a 22.0
b 30.0
c 31.0
d 44.0
e NaN
f 96.0
g NaN
dtype: float64
a 0.833333
b 1.000000
c 1.818182
d 2.142857
e NaN
f 5.000000
g NaN
dtype: float64
由于s5中没有对应的g索引,s6中没有对应的e索引,所以数据的运算会产生两个缺失值NaN。注意,这里的算术结果就实现了两个序列索引的自动对齐,而非简单的将两个序列加总或相除。对于数据框的对齐,不仅仅是行索引的自动对齐,同时也会自动对齐列索引(变量名)
数据框中同样有索引,而且数据框是二维数组的推广,所以其不仅有行索引,而且还存在列索引,关于数据框中的索引相比于序列的应用要强大的多,这部分内容将放在数据查询中讲解。
自动对齐本质是索引的或运算。
2、查看数据
1、通过索引值或索引标签获取数据
import pandas as pd
s4 = pd.Series(np.array([1,1,2,3,5,8]))
s4
如果不给序列一个指定的索引值,则序列自动生成一个从0开始的自增索引。可以通过index查看序列的索引
s4.index
现在我们为序列设定一个自定义的索引值:
s4.index = ['a','b','c','d','e','f']
s4
序列有了索引,就可以通过索引值或索引标签(索引切片)进行数据的获取:
import pandas as pd
s4 = pd.Series(np.array([1,1,2,3,5,8]))
s4.index = ['a','b','c','d','e','f']
print('s4:\n',s4)
print('\n',s4[3])
print('\n',s4['e'])
print('\n',s4[[1,3,5]])
print('\n',s4[['a','b','d','f']])
print('\n',s4[:4])
print('\n',s4['c':])
print('\n',s4['b':'e'])
千万注意:如果通过索引标签获取数据的话,末端标签所对应的值是可以返回的!在一维数组中,就无法通过索引标签获取数据,这也是序列不同于一维数组的一个方面。
2、用 head 和 tail 查看顶端和底端的几列
dates = pd.date_range('20130101', periods=6)
dates
df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
df
df.head()
df.tail(3)
3、describe()
显示数据的概要
df.describe()
A B C D
count 6.000000 6.000000 6.000000 6.000000
mean 0.073711 -0.431125 -0.687758 -0.233103
std 0.843157 0.922818 0.779887 0.973118
min -0.861849 -2.104569 -1.509059 -1.135632
25% -0.611510 -0.600794 -1.368714 -1.076610
50% 0.022070 -0.228039 -0.767252 -0.386188
75% 0.658444 0.041933 -0.034326 0.461706
max 1.212112 0.567020 0.276232 1.071804
4、获取行/列
注意,以下这些对交互式环境很友好,但是作为 production code 请用优化过的
.at
,.iat
,.loc
,.iloc
和.ix
用columns选择列,用index选择行。注意:选择列的时候单次只能选择某一列的数据,不能同时选择多列;而使用index的时候一定要使用范围(类似于[1:2]),单独某个index会报错。
从 DataFrame 选择一个列,就得到了 Series
df['A']
2013-01-01 0.469112
2013-01-02 1.212112
2013-01-03 -0.861849
2013-01-04 0.721555
2013-01-05 -0.424972
2013-01-06 -0.673690
Freq: D, Name: A, dtype: float64
如果同时选择多列df['A','B'],报错:
df['A','B']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
~/local/anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc
(self, key, method, tolerance)
2441 try:
-> 2442 return self._engine.get_loc(key)
2443 except KeyError:
和 numpy 类似,这里也能用[],通过索引访问
df[0:3]
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
df['20130102':'20130104']
A B C D
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
5、通过 label 选择
使用loc方法,通过位置标签选择:统一格式为DataFrame.loc[index:index,[‘columns’]],loc方法当中的columns可以选择多列,如果表示只按列选择的话index可以不填但是冒号(:)和逗号(,)一定要写.
另外,如果loc还能这么用:DataFrame.loc[index,[‘columns’]],这时的index为特定能够的label或值,这样用会返回一个Series;DataFrame.loc[index,‘columns’],这里面的index和columns都是唯一的,返回一个值。
由于降维的问题,pandas会对精度进行转换.
刚刚那个 DataFrame 可以通过时间戳的下标(dates[0] = Timestamp('20130101')
)来访问。
df.loc[dates[0]]
A 0.469112
B -0.282863
C -1.509059
D -1.135632
Name: 2013-01-01 00:00:00, dtype: float64
还可以多选
df.loc[:,['A','B']]
A B
2013-01-01 0.469112 -0.282863
2013-01-02 1.212112 -0.173215
2013-01-03 -0.861849 -2.104569
2013-01-04 0.721555 -0.706771
2013-01-05 -0.424972 0.567020
2013-01-06 -0.673690 0.113648
注意那个冒号,用法和NumPy 是一样的!所以也可以这样
df.loc['20130102':'20130104',['A','B']]
A B
2013-01-02 1.212112 -0.173215
2013-01-03 -0.861849 -2.104569
2013-01-04 0.721555 -0.706771
当有一个维度是标量(而不是范围或序列)的时候,选择出的矩阵维度会减少
df.loc['20130102',['A','B']]
A 1.212112
B -0.173215
Name: 2013-01-02 00:00:00, dtype: float64
如果对所有的维度都写了标量,不就是选出一个元素吗?
df.loc[dates[0],'A']
0.46911229990718628
这种情况通常用at
,速度更快
df.at[dates[0],'A']
0.46911229990718628
6、通过整数下标选择
这个就和数组类似啦,直接看例子。选出第3行:
df.iloc[3]
A 0.721555
B -0.706771
C -1.039575
D 0.271860
Name: 2013-01-04 00:00:00, dtype: float64
选出3~4行,0~1列:
df.iloc[3:5,0:2]
A B
2013-01-04 0.721555 -0.706771
2013-01-05 -0.424972 0.567020
也能用 list 选择
df.iloc[[1,2,4],[0,2]]
A C
2013-01-02 1.212112 0.119209
2013-01-03 -0.861849 -0.494929
2013-01-05 -0.424972 0.276232
也能用 slice
df.iloc[1:3,:]
A B C D
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
df.iloc[:,1:3]
B C
2013-01-01 -0.282863 -1.509059
2013-01-02 -0.173215 0.119209
2013-01-03 -2.104569 -0.494929
2013-01-04 -0.706771 -1.039575
2013-01-05 0.567020 0.276232
2013-01-06 0.113648 -1.478427
对应单个元素
df.iloc[1,1]
-0.17321464905330858
df.iat[1,1]
-0.17321464905330858
7、布尔值下标
基本用法
df[df.A >0]
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
没有填充的值等于 NaN
df[df>0]
A B C D
2013-01-01 0.469112 NaN NaN NaN
2013-01-02 1.212112 NaN 0.119209 NaN
2013-01-03 NaN NaN NaN 1.071804
2013-01-04 0.721555 NaN NaN 0.271860
2013-01-05 NaN 0.567020 0.276232 NaN
2013-01-06 NaN 0.113648 NaN 0.524988
isin()
函数:是否在集合中
df2 = df.copy()
df2['E'] = ['one', 'one','two','three','four','three']
df2
A B C D E
2013-01-01 0.612417 0.095828 -0.054642 1.817248 one
2013-01-02 2.076892 0.092285 0.660803 0.894581 one
2013-01-03 -0.733269 -2.057131 -0.343663 -1.196418 two
2013-01-04 -0.650500 1.072853 -0.957207 1.301154 three
2013-01-05 0.118362 0.016416 -0.054961 -0.598852 four
2013-01-06 0.858298 1.206379 0.385798 0.642214 three
df2[df2['E'].isin(['two','four'])]
A B C D E
2013-01-03 -0.733269 -2.057131 -0.343663 -1.196418 two
2013-01-05 0.118362 0.016416 -0.054961 -0.598852 four