Pandas数据框和Numpy数组中“axis”定义的歧义

115

我一直很困惑Python中轴的定义,它们是否指的是数据框的行或列。请看下面的代码:

>>> 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中,“axis”是什么意思?

另外一件事,DataFrame.mean可能被定义错误了。文档中说 DataFrame.mean 中的 axis=1应该表示计算列的平均值而不是行的平均值...


有关别名的详细解释,请参见下面的答案,其中包括“columns”、“index”和“rows”。点击此处查看 - Ted Petrou
这很奇怪。坐标轴应该在“mean”和“drop”中保持一致。需要非线性思维才能到达实际的行为。 - WestCoastProjects
7个回答

186

最简单的记忆方式是将其记为0=向下1=横向

这意味着:

  • 使用axis=0沿每列或行标签(索引)向下应用方法。
  • 使用axis=1沿每行或列标签横向应用方法。

以下图片显示了DataFrame的每个轴所指的部分:

还有一点需要记住的是,Pandas遵循NumPy对单词axis的使用。其用法在NumPy的术语表中有解释:

对于具有多个维度的数组,定义了轴。一个二维数组有两个相应的轴:第一个沿着行向下垂直运行(轴0),第二个沿着列水平运行(轴1)。[我强调的]

因此,关于问题中的方法df.mean(axis=1),似乎已经正确定义。它沿着每个单独的行计算条目横跨列的平均值。另一方面,df.mean(axis=0)将是一个在垂直方向上沿着行操作的过程。

同样,df.drop(name, axis=1)涉及到对列标签的操作,因为它们直观地沿着水平轴走。指定axis=0会使该方法作用于行而不是列。


6
我曾经遇到的困难是,df.apply(..., axis=0) 并没有覆盖轴 0(索引),而是覆盖了列,并返回包含所有索引的序列。 关键在于,df.apply(..., axis=0) 返回一个序列,所以您可以应用操作来覆盖整个索引。 - moritzschaefer
5
如果你将df.apply视为类似于df.sum这样的方法,也可以帮助理解。例如,df.sum(axis=0)对DataFrame的每一列进行求和。同样地,您可以编写df.apply(sum, axis=0)来执行完全相同的操作。虽然在DataFrame中的确对每一列应用了操作,但实际函数是沿着轴0运行的。 - Alex Riley
1
很不幸,命名和顺序约定与 R 的 apply 函数 正好相反 -- 在 R 中,较小的 MARGIN 值(类似于 pandas 中的 axis)为 "1" 对应于 "行",这意味着该函数被应用于每一行,而较大的值 "2" 则指 "列",这意味着该函数被应用于每一列。 - Keith Hughitt
1
这是Pandas中的一个破坏性错误。 - AbstProcDo

20

这里已经有正确的答案,但我会给你另一个超过2维度的例子。

参数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的值。


2
这个答案对我来说比我在这个主题上看到的任何可视化更直观。然而,xarray比pandas更适合多维数组。 - alys
1
最佳答案。同时,无需涉及3D,您也可以对2D数据框进行说明。 - user41855

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(轴的意思是方向)

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

"你不觉得,轴1和平行于轴0意思一样吗?" - Nuance

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'。我记得这个方法是通过想象一个额外的列可能会被添加到我的DataFrame中:

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时,就像得到DataFrame的新行一样。我们来添加一行:

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'}

有趣的是,第一轴数字有别名,尽管它们更明确,但并不经常使用。谁能想象在学校被告知“这是一张表格,请尝试沿着轴0计算总和”而不是“尝试按列获取总和”(或在Pandas中沿着'index'进行)呢? - mins

3
当axis='rows'或axis=0时,它意味着沿着行方向访问元素,从上到下。如果沿axis=0应用sum,它将给出每列的总数。
当axis='columns'或axis=1时,它意味着沿着列方向访问元素,从左到右。如果沿axis=1应用sum,我们将得到每行的总数。
仍然困惑!但上面的内容让我感觉更容易理解一些。
"Original Answer" 翻译成 "最初的回答"

0
我认为需要找到一种统一的方式来描述它。 我认为记住它有两个步骤:
1. 记住轴 = 1 表示左右,轴 = 0 表示上下;你需要一些时间来记住它,我们无法帮助你在这方面。 2. 记住在进行计算(如 np.mean,np.max)时,轴表示沿着那个方向进行计算(所以轴 = 1 表示从左到右进行计算),而在进行索引或搜索时,轴表示你沿着那个方向进行搜索(所以 df.drop('name',axis =1) 表示你从左到右搜索找到代表 'name' 的内容,显然它应该是列名而不是行索引)。

0

我记得通过维度的变化,如果axis=0,则行改变,列不变,如果axis=1,则列改变,行不变。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接