这几天在看《python数据科学手册的时候》中pandas中axis产生了一点疑问,因为dataframe相当于numpy二维数组的扩展,按道理来说他们应该是相同的。但是我在用numpy的axis解释去对应pandas的时候总是对不上。
首先,numpy中的axis,按书中的话说叫数据折叠的方向。这话也比较让人不明所以。不如举个例子:
比如
a = np.array([[0,1,2,3],[0,1,2,3],[0,1,2,3]])
a
array([[0, 1, 2, 3],
[0, 1, 2, 3],
[0, 1, 2, 3]])
这个二维数组有两个轴,最外层的括号对应第一个轴axis0,我们不妨把这个二维数组想象成一维数组,只不过其中每个元素是向量[0,1,2,3]。次外层的括号对应axis1,同样的,它也可以看作是由元素[0,0,0],[1,1,1],[2,2,2],[3,3,3]构成的一维数组。
假如我们的维度再高一点
a = a.reshape((3,2,2))
array([[[0, 1],
[2, 3]],
[[0, 1],
[2, 3]],
[[0, 1],
[2, 3]]])
以此类推
axis0:
[[0, 1],[2, 3]],
[[0, 1],[2, 3]],
[[0, 1],[2, 3]]
axis1
[[0,1],[0,1],[0,1]]
[[2,3],[2,3],[2,3]]
axis2
[[0,2],[0,2],[0,2]]
[[1,3],[1,3],[1,3]]
对应的均值:
axis0:
[[0., 1.],[2., 3.]]
axis1
[[1., 2.],[1., 2.],[1., 2.]]
axis2
[[0.5, 2.5],[0.5, 2.5],[0.5, 2.5]]
然而这种理解方式在pandas中却行不通,比如:
b = pd.DataFrame(np.arange(9).reshape(3,3), columns=list('ABC'))
b
A B C
0 0 1 2
1 3 4 5
2 6 7 8
假如我要去做每一行去减去第一行的操作,按照前面的理解,axis=0,即可将二维数组看作由一堆行向量所构成的一维数组,行向量之间的运算恰好就是这种情况。但是假如真这么做了,结果却不对
b.subtract(b.iloc[0], axis=0)
A B C
0 NaN NaN NaN
1 NaN NaN NaN
2 NaN NaN NaN
A NaN NaN NaN
B NaN NaN NaN
C NaN NaN NaN
为什么会这样呢,这里我从索引的角度给出我的理解,感觉还说得通,我们不妨查看一下取出的第一行
b.iloc[0]
A 0
B 1
C 2
Name: 0, dtype: int32
这个时候我就发现不对劲了,数据的index好像不是原来的0,1,2了(显式上的),继续查看
b.iloc[0].index
Index(['A', 'B', 'C'], dtype='object')
果然,单独抽出来的一行,index已经是A B C了。根据索引的并集,两个运算对象的结果,索引相同的放在一起,索引不同的则新增。在上面的计算中,原来并没有’A’, ‘B’, 'C’三个索引。所以出错了,正确的做法,则是去指定相同索引的轴,比如axis1(水平轴),让A B C和A B C一一对应,则会得到正确的结果:
b.subtract(b.iloc[0], axis=1)
A B C
0 0 0 0
1 3 3 3
2 6 6 6
这个结果也是可以解释的,相当于每一个A列的元素减去了b[0]中index A的元素0,以此类推。完成了行向量相减。
同样的,按照这个思路,我们也可以知道列向量相减用axis=1的话就会出错
b.subtract(b.iloc[:,0], axis=1)
A B C 0 1 2
0 NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN
b.iloc[:,0].index
RangeIndex(start=0, stop=3, step=1)
b.subtract(b.iloc[:,0], axis=0)
A B C
0 0 1 2
1 0 1 2
2 0 1 2
无论是指示行的index,还是指示列的column。都可以看作是一个东西。运算中要互相匹配才能得到正确的结果。