区分 pandas 库中的 loc / iloc 以及 numpy 中的 []

numpy 和 pandas(处理数据的 python 库)中,有很多表示二维数组的类。为了定位到二维数组中的数组,这些类也提供了很多实例方法,比如脚标方法(e.g. data[0])。但除了脚标方法这个概念,numpy 和 pandas 在定位方法的设计上有很多不同。我们在这里简单梳理一下。

pandas 中的 loc 和 iloc

在 pandas 中,定位n个竖列(column)用脚标方法[]

  1. df['column'] 返回一个包含当前竖列数据的 pd.Series 对象
  2. df[['column1', 'column2']] 返回一个包含两个竖列数据的 pd.DataFrame 对象

但因为脚标方法被用来定位 columns,定位横排( rows )的时候用什么呢?pandas 想出的方法是创造两个新的对象属性 lociloc,并用他们来访问 rows。

比如,对于这样一个 DataFrame:

1
df = pd.DataFrame(data=[[1,2,3], [4,5,6], [7,8,9]], columns=['A','B','C'], index=['x','y','z'])
A B C
x 1 2 3
y 4 5 6
z 7 8 9

其中,x, y, z 就是 df 的 index(在这个 df 中,index 都是 string):

1
2
print(df.index)
# => Index(['x', 'y', 'z'], dtype='object')

要想通过这些 df 的 index 来访问一整个 row 的数据,我们需要先访问 df 的 loc 属性,然后才能使用 index 作为脚标访问一整行的数据:

1
2
3
4
5
print(df.loc)
# => <pandas.core.indexing._LocIndexer object at 0x7fa060820f28>
df.loc['x'] # return an pd.Series object
# or
df.loc[['x']] # return an pd.DataFrame object, like a 2-d array

同时,pandas 还允许我们按照 rows 的顺序来访问数据,也就是像普通的 list 一样,用数字 index 来访问 df 中的数据。但就像上面的 loc 一样,我们这里也需要先访问 df 的一个属性,叫 iloc 属性:

1
2
3
4
5
print(df.iloc)
# => <pandas.core.indexing._iLocIndexer object at 0x7fa0605d0fd0>
df.iloc[0]
# or
df.iloc[[0]] # return an pd.DataFrame object, like a 2-d array

总之,因为 pandas 的设计,我们在按 rows 来访问 DataFrame 数据之前,都需要先访问 loc 或者 iloc 属性,两者的区别是传入的脚标是否被视为顺序的 index。

其他 pandas 的类也有类似设计,比如 pd.Series,只不过后者通常是一维的,很多时候直接脚标访问数据就可以了。

numpy 的设计

我们在上面讨论了 pandas 中的方法设计,而 numpy 中是很不一样的设计风格。

比如,如果把上面的 df 转化为 numpy.ndarray

1
2
3
4
5
6
array = np.asarray(df)
'''
array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])
'''

然后访问一个 row 的数据:

1
2
3
4
5
array[0] # => [1,2,3]
array[0,:] # => [1,2,3]
# or
array[[0]] # => [[1,2,3]], NOTE: a 2-d array
array[[0],:] # => [[1,2,3]], NOTE: a 2-d array

或者访问一个 column 下的数据:

1
2
array[:,0] # => [1,4,7]
array[:,[0]] # => [[1], [4], [7]], NOTE: a 2-d array

显然,numpy 采用的方法跟 pandas 不一样,在按‘行序号“访问 ndarray 时,不需要先访问 iloc 一类的属性。而且,ndarray 的 [] 方法允许传入两个或两组脚标,第一个代表 rows 的定位,第二个代表 columns 的定位。(备注,pandas 的 DataFrame 的 iloc 属性也允许传入两个或两组角标,用法和 numpy 的 ndarray 基本一样)

What’s more

最后,总结一下,从上面的例子中看出 numpy 和 pands 对于二维数据(或者n维):

  1. 如果传入的脚标是一个单独的 index 的值,那么返回的数据就会降低到一维(n-1 维)
  2. 如果传入的脚标是个包含 index 的数组(: 也算是数组),那么返回的数据不会降维

不论是通过 iloc,loc 的属性间接访问数据,还是直接传入脚标,上面这条设计是个共同点。

(这是归纳法得出的一个结论,吧)