前言
Python及其numpy、pandas几个库里数据类型的索引真的是很多陷阱,初学者往往容易混淆。本篇尽量把理解对的总结一下。
几种数据类型索引
1、python list
多维列表可以看成嵌套的列表
1) 获取单个元素
正确✅打开方式:
>>> a = [[1,2,3], [4,5,6]]
>>> a
[[1, 2, 3], [4, 5, 6]]
>>> a[0][0]
1
>>> a[1][0]
4
>>> a[1][2]
6
错误❌打开方式:
>>> a[1,0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers or slices, not tuple
2) 获取多个元素
正确✅打开方式:
>>> a[:][:]
[[1, 2, 3], [4, 5, 6]]
>>> a[0]
[1, 2, 3]
# 多维列表可以看成嵌套的列表,这里第一层是[1,2,3]和[4,5,6]两个列表,
# 所以a[0]这是获取第一层的第一个列表
>>> a[0][0:1] # 这是获取列表第一层第一个列表[1,2,3]的由0:1组成的列表
[1] #这里需要注意⚠️的是:a[0][0:1] 返回的是一个列表,而a[0][0]返回的是一个值
>>> type(a[0][0:1])
<class 'list'>
>>> type(a[0][0])
<class 'int'>
容易混淆的打开方式:
>>> a[0:1][0] # 你以为返回的是1或者[1],其实正确答案如下:
[1, 2, 3]
# 为什么?因为a[0:1]是返回列表第一层第0:1个列表组成的列表,
# 所以是[[1,2,3]],还是一个二维列表,所以a[0:1][0]返回的是[1, 2, 3]
# 这样就能理解为什么下面的索引会失败了
>>> a[0:1][1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
# 也能理解下面为什么二维列表居然能用三维下标了
>>> a[0:1][0][0]
1
2、 numpy array
>>> import numpy as np
>>> b = np.array(a)
>>> b
array([[1, 2, 3],
[4, 5, 6]])
>>> b[0][0]
1
>>> b[0,0] # 惊奇地发现这种方式合法了,amazing!
1
list和array如此之相像,在写程序过程中有时候会混淆,而如果搞不清楚list和array,例如把list当成了array,则上述索引a[0,0] 就会报错。
array实际上是矩阵的形式,[0, 0]这种方式就是按照矩阵的思路来理解的,所以它可以按列来获取元素
>>> b[:2, :1]。# 获取到了0、1行的第0列
array([[1],
[4]])
同时,array保留了类似[0][0]方式,和list操作及结果一样。我们来对比一下:
>>> b[:2][:1]
array([[1, 2, 3]])
>>> a[:2][:1]
[[1, 2, 3]]
3、pandas DataFrame
>>> import pandas as pd
>>> c = pd.DataFrame(a, columns=['col0','col1', 'col2'])
>>> c
col0 col1 col2
0 1 2 3
1 4 5 6
在DataFrame里,体系是完全不一样的,不能用list和array的方式来做索引。如果非要那么做的话,要采用iloc。iloc基本是把DataFrame看成array,可以用行/列序号来索引。
>>> c[0][0]
Traceback (most recent call last):
File "/Users/xxx/anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py", line 3063, in get_loc
return self._engine.get_loc(key)
File "pandas/_libs/index.pyx", line 140, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/index.pyx", line 162, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 1492, in pandas._libs.hashtable.PyObjectHashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 1500, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 0. # ...未完,相当长一段错误提示
# 正确✅打开方式
>>> c.iloc[0][0]
1
DataFrame非常好的一点是把列都取了名字,可以直接按列名字获取一列或者多列。
>>> c['col0'] #返回的是Series类型
0 1
1 4
>>> c[['col0','col1']] #返回的是DataFrame类型
col0 col1
0 1 2
1 4 5
但是在获取多列的时候要⚠️注意,必须把多列用[]括起来,否则....出错没商量...
>>> c['col0','col1']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/l.... #此处省略错误提示若干
那么,怎么获取行呢?用loc。loc还可以获取指定名字的行、列。
# 获取第0行
>>> c.loc[0]
col0 1
col1 2
col2 3
Name: 0, dtype: int64
>>> c.loc[:2]
col0 col1 col2
0 1 2 3
1 4 5 6
# 获取指定行、列
>>> c.loc[:2, 'col0']
0 1
1 4
Name: col0, dtype: int64
>>> c.loc[:2, ['col0']]
col0
0 1
1 4
# c.loc[:2, 'col0'] 和 c.loc[:2, ['col0']]区别在哪里?返回的类型不一样!!!
>>> type(c.loc[:2, 'col0'])
<class 'pandas.core.series.Series'> #Series类型
>>> type(c.loc[:2, ['col0']])
<class 'pandas.core.frame.DataFrame'> #DataFrame类型
>>> for each in c.loc[:2, 'col0']:
... print(each)
...
1
4
>>> for each in c.loc[:2, 'col0'].iterrows(): # Series不能iterrows
... print(each)
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/U
摘抄一段Series的描述:“Series是Pandas中的一维数据结构,类似于Python中的列表和Numpy中的Ndarray,不同之处在于:Series是一维的,能存储不同类型的数据,有一组索引与元素对应。”
我们再来看一下有无[]的values的差别:
>>> c.loc[:2, 'col0'].values # 这个是行
array([1, 4])
>>> c.loc[:2, ['col0']].values # 这个返回列的形式的numpy array
array([[1],
[4]])
当要获取多个列时,必须用[]括起来:
#错误❌
>>> c.loc[:2, 'col0', 'col1']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/xxx/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py", line 1472, in __getitem__
return self._getitem_tuple(key)
File "/Users/liqiong/anaconda3/lib/python3.6/site-packages/pandas/core/indexing.py", line 875, in _getitem_tuple
self._has_valid_tuple
.... # 此处省去错误提示若干行
# 正确✅示范:
>>> c.loc[:2, ['col0', 'col1']]
col0 col1
0 1 2
1 4 5
关于赋值:
# 单行多累赋值
>>> c.loc[0, ['col0','col1']] = [7,8]
>>> c
col0 col1 col2
0 7 8 3
1 4 5 6
# 多行多列赋值
>>> c.loc[:2, ['col0','col1']] = [[7,8],[0,9]]
>>> c
col0 col1 col2
0 7 8 3
1 0 9 6
# Series单列赋值
>>> c.loc[0, 'col0'] = 3 #这里不能是c.loc[0, 'col0'] = [3],否则出错
>>> c
col0 col1 col2
0 3 8 3
1 0 9 6
>>> c.loc[:2, 'col0'] = [3,4]
>>> c
col0 col1 col2
0 3 8 3
1 4 9 6
# iloc也同样可以赋值,我们用iloc把c赋值回最初的值
>>> c.iloc[:2,:2] = [[1,2],[4,5]]
>>> c
col0 col1 col2
0 1 2 3
1 4 5 6
>>> c.iloc[:1,0] = [3]
>>> c
col0 col1 col2
0 3 2 3
1 4 5 6
>>> c.iloc[:1,0] = 1 #这里1为什么不需要[],还不是很清楚
>>> c
col0 col1 col2
0 1 2 3
1 4 5 6
我们把行索引换成名字吧,再体会一下不同行、列输入的差别。
>>> c = c.rename({0:'row0',1:'row1'}, axis='index')
>>> c
col0 col1 col2
row0 1 2 3
row1 4 5 6
>>> c.loc['row0','col0']
1
>>> c.loc[['row0'],'col0']
row0 1
Name: col0, dtype: int64
>>> c.loc[['row0'],['col0']]
col0
row0 1
>>> c.loc[['row0','row1'],'col0']
row0 1
row1 4
Name: col0, dtype: int64
>>> c.loc[['row0','row1'],['col0']]
col0
row0 1
row1 4
冒号的正确与错误用法
>>> c.loc[[:],['col0']] #错误❌
File "<stdin>", line 1
c.loc[[:],['col0']]
^
SyntaxError: invalid syntax
>>> c.loc[:,['col0']] # 正确✅
col0
row0 1
row1 4
>>> c.loc[:,:]
col0 col1 col2
row0 1 2 3
row1 4 5 6
>>> c.loc[:,:].values
array([[1, 2, 3],
[4, 5, 6]])
>>> c.loc[:'row2',['col0']]
col0
row0 1
row1 4
>>> c.loc['row1':'row2',['col0']]
col0
row1 4
# 神奇的是行是不包含最后一个‘row2’的,而列是包含最后一个'col2'的,
# 这真的确认不是pandas的bug?
>>> c.loc['row1':'row2','col0':'col2']
col0 col1 col2
row1 4 5 6
>>> c.loc['row1':'row2','col0':'col1']
col0 col1
row1 4 5
关于使用冒号进行赋值
>>> c
col0 col1 col2
row0 1 2 3
row1 4 5 6
>>> c.loc['row1':'row2','col0':'col1']=[1,2] #把前两行前两列都赋成[1,2]
>>> c
col0 col1 col2
row0 1 2 3
row1 1 2 6
>>> c.loc[:,'col0':'col1']=[3,4]
>>> c
col0 col1 col2
row0 3 4 3
row1 3 4 6
>>> c.loc[:,'col0']=[1,4]
>>> c
col0 col1 col2
row0 1 4 3
row1 4 4 6
>>> c.loc[:,'col0']=[1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/xxx/anaconda3/lib/python3.6/site-packages/pandas/core
# 此处省略出错信息若干
>>> c.loc[:,'col0']=1
>>> c
col0 col1 col2
row0 1 4 3
row1 1 4 6
估计能坚持到这里不晕的没有几个人,所以多重索引的问题,留到下次吧。