Pandas Dataframe / Numpy Array “轴”定义中的模糊性

116 投票
7 回答
35085 浏览
提问于 2025-04-20 22:51

我一直对Python中的轴(axis)是怎么定义的感到很困惑,不知道它是指数据框(DataFrame)的行还是列。看看下面的代码:

>>> df = pd.DataFrame([[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]], columns=["col1", "col2", "col3", "col4"])
>>> df
   col1  col2  col3  col4
0     1     1     1     1
1     2     2     2     2
2     3     3     3     3

所以如果我们调用 df.mean(axis=1),我们会得到每一行的平均值:

>>> df.mean(axis=1)
0    1
1    2
2    3

但是,如果我们调用 df.drop(name, axis=1),实际上我们是删除一列,而不是一行:

>>> df.drop("col4", axis=1)
   col1  col2  col3
0     1     1     1
1     2     2     2
2     3     3     3

有没有人能帮我理解在pandas/numpy/scipy中“轴”是什么意思?

顺便提一下,DataFrame.mean 可能定义得不太对。文档中说 DataFrame.meanaxis=1 应该是指对列求平均,而不是对行...

7 个回答

3

当我们说 axis='rows' 或者 axis=0 时,这意味着我们是在从上到下的方向访问元素。如果我们在这个方向上进行求和,就会得到每一列的总和。

而当我们说 axis='columns' 或者 axis=1 时,这意味着我们是在从左到右的方向访问元素。如果在这个方向上进行求和,就会得到每一行的总和。

还是有点困惑!不过上面的解释让我稍微明白了一些。

5

大家应该更广泛地知道,字符串别名'index''columns'可以用来代替数字0和1。这些别名更清晰,也更容易让我记住计算是怎么进行的。'index'的另一个别名是'rows'

当使用axis='index'时,计算是沿着列进行的,这可能会让人感到困惑。不过,我记住这个的方式是,结果的大小和另一行是一样的。

让我们在屏幕上显示一些数据,看看我在说什么:

df = pd.DataFrame(np.random.rand(10, 4), columns=list('abcd'))
          a         b         c         d
0  0.990730  0.567822  0.318174  0.122410
1  0.144962  0.718574  0.580569  0.582278
2  0.477151  0.907692  0.186276  0.342724
3  0.561043  0.122771  0.206819  0.904330
4  0.427413  0.186807  0.870504  0.878632
5  0.795392  0.658958  0.666026  0.262191
6  0.831404  0.011082  0.299811  0.906880
7  0.749729  0.564900  0.181627  0.211961
8  0.528308  0.394107  0.734904  0.961356
9  0.120508  0.656848  0.055749  0.290897

当我们想要计算所有列的平均值时,我们使用axis='index'来得到以下结果:

df.mean(axis='index')
a    0.562664
b    0.478956
c    0.410046
d    0.546366
dtype: float64

同样的结果也可以通过以下方式得到:

df.mean() # default is axis=0
df.mean(axis=0)
df.mean(axis='rows')

如果想要从左到右对行进行操作,就使用axis='columns'。我记住这个是因为我可以想象在我的数据框中添加一个额外的列:

df.mean(axis='columns')
0    0.499784
1    0.506596
2    0.478461
3    0.448741
4    0.590839
5    0.595642
6    0.512294
7    0.427054
8    0.654669
9    0.281000
dtype: float64

同样的结果也可以通过以下方式得到:

df.mean(axis=1)

使用 axis=0/index/rows 添加新行

让我们用这些结果来添加额外的行或列,以完成解释。因此,每当使用axis=0/index/rows时,就像是在获取数据框的新行。我们来添加一行:

df.append(df.mean(axis='rows'), ignore_index=True)

           a         b         c         d
0   0.990730  0.567822  0.318174  0.122410
1   0.144962  0.718574  0.580569  0.582278
2   0.477151  0.907692  0.186276  0.342724
3   0.561043  0.122771  0.206819  0.904330
4   0.427413  0.186807  0.870504  0.878632
5   0.795392  0.658958  0.666026  0.262191
6   0.831404  0.011082  0.299811  0.906880
7   0.749729  0.564900  0.181627  0.211961
8   0.528308  0.394107  0.734904  0.961356
9   0.120508  0.656848  0.055749  0.290897
10  0.562664  0.478956  0.410046  0.546366

使用 axis=1/columns 添加新列

同样,当axis=1/columns时,它会创建可以轻松变成自己列的数据:

df.assign(e=df.mean(axis='columns'))

          a         b         c         d         e
0  0.990730  0.567822  0.318174  0.122410  0.499784
1  0.144962  0.718574  0.580569  0.582278  0.506596
2  0.477151  0.907692  0.186276  0.342724  0.478461
3  0.561043  0.122771  0.206819  0.904330  0.448741
4  0.427413  0.186807  0.870504  0.878632  0.590839
5  0.795392  0.658958  0.666026  0.262191  0.595642
6  0.831404  0.011082  0.299811  0.906880  0.512294
7  0.749729  0.564900  0.181627  0.211961  0.427054
8  0.528308  0.394107  0.734904  0.961356  0.654669
9  0.120508  0.656848  0.055749  0.290897  0.281000

似乎你可以通过以下私有变量查看所有别名:

df._AXIS_ALIASES
{'rows': 0}

df._AXIS_NUMBERS
{'columns': 1, 'index': 0}

df._AXIS_NAMES
{0: 'index', 1: 'columns'}
11

另一种解释方式:

// Not realistic but ideal for understanding the axis parameter 
df = pd.DataFrame([[1, 1, 1, 1], [2, 2, 2, 2], [3, 3, 3, 3]],
                  columns=["idx1", "idx2", "idx3", "idx4"],
                  index=["idx1", "idx2", "idx3"]
                 )

---------------------------------------1
|          idx1  idx2  idx3  idx4
|    idx1     1     1     1     1
|    idx2     2     2     2     2
|    idx3     3     3     3     3
0

关于 df.drop (axis指的是位置)

A: I wanna remove idx3.
B: **Which one**? // typing while waiting response: df.drop("idx3",
A: The one which is on axis 1
B: OK then it is >> df.drop("idx3", axis=1)

// Result
---------------------------------------1
|          idx1  idx2     idx4
|    idx1     1     1     1
|    idx2     2     2     2
|    idx3     3     3     3
0

关于 df.apply (axis指的是方向)

A: I wanna apply sum.
B: Which direction? // typing while waiting response: df.apply(lambda x: x.sum(),
A: The one which is on *parallel to axis 0*
B: OK then it is >> df.apply(lambda x: x.sum(), axis=0)

// Result
idx1    6
idx2    6
idx3    6
idx4    6
21

已经有很好的答案了,但我再给你一个超过两维的例子。

参数 axis 的意思是 要改变的轴
比如,假设有一个数据框,它的维度是 a x b x c

  • df.mean(axis=1) 会返回一个维度为 a x 1 x c 的数据框。
  • df.drop("col4", axis=1) 会返回一个维度为 a x (b-1) x c 的数据框。

在这里,axis=1 表示第二个轴,也就是 b,所以在这些例子中,b 的值会被改变。

186

记住这个概念其实很简单,可以把它理解为 0=向下1=横向

这意味着:

  • 使用 axis=0 时,是对每一列进行操作,或者说是对行标签(也就是索引)进行操作。
  • 使用 axis=1 时,是对每一行进行操作,或者说是对列标签进行操作。

下面有一张图,展示了数据框(DataFrame)中每个轴所指的部分:

另外,记住Pandas是跟NumPy的用法一致的,都是用 axis 这个词。NumPy的用法可以在它的术语表中找到:

轴是为多维数组定义的。一个二维数组有两个对应的轴:第一个是垂直的,向下穿过行(axis 0),第二个是水平的,穿过列(axis 1)。[我强调的部分]

所以,关于问题中的方法 df.mean(axis=1),它的定义是正确的。这个方法是计算每一行的平均值,也就是 横向穿过列 的平均值。另一方面, df.mean(axis=0) 则是对 向下穿过行 进行操作。

同样, df.drop(name, axis=1) 是对列标签进行操作,因为列标签是横向的。指定 axis=0 则会让这个方法对行进行操作。

撰写回答