Python「numpy.sum(...)」のaxisオプション指定まとめ

# はじめに

`numpy.ndarray.sum(...)` や `numpy.ndarray.mean(...)` などで指定可能な **axis オプション** についてのまとめです。2次元および3次元のnumpy配列を対象に、図を使って解説します。

# 2次元のnumpy配列の場合

まずは、2次元（ndim=2）のnumpy配列を対象にします。例として、次のような<font color='red'>**3行4列**の**2次元のnumpy配列**</font> `x` を考えます。

![m1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/2a1376f5-252d-72b5-d0dc-196bc055ec0d.png)

この配列 `x` は次のコードで生成できます。

```py:shape=(3,4)のnumpy配列を生成
import numpy as np
x = np.arange(1,12+1).reshape(3,4)
print(x)
#print(x.ndim)  # -> 2
#print(x.shape) # -> (3, 4)
```

```:実行結果
[ [ 1  2  3  4]
  [ 5  6  7  8]
  [ 9 10 11 12] ]
```

## `axis=None` を指定

`axis=None` がデフォルト引数になっているので、`x.sum()` でも `x.sum(axis=None)` でも同じ動作をします。[numpy.sum(...) / numpy.ndarray.sum(...) のリファレンス](https://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html)

```py:sum(axis=None)
s = x.sum(axis=None)
# print(type(s)) # -> <class 'numpy.int64'>
# print(s.ndim)  # -> 0
# print(s.shape) # -> ()
print(s)
```

`axis=None` を指定すると配列を構成する**全ての要素についての合計**が求められます。具体的には $1+2+3+\cdots+11+12=$<font color='red'>$\bf{78}$</font> が計算されます。

```:実行結果
78
```

## `axis=0` を指定


```py:sum(axis=0)
s = x.sum(axis=0)
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 1
# print(s.shape) # -> (4,)
print(s)
```

```:実行結果
[15 18 21 24]
```

`axis=0` を指定すると、**行方向に要素が合計されます**。具体的には、$1+5+9=$<font color='red'>$\bf{15}$</font>、$2+6+10=$<font color='red'>$\bf{18}$</font>、$3+7+11=$<font color='red'>$\bf{21}$</font>、$4+8+12=$<font color='red'>$\bf{24}$</font> が計算され `[15 18 21 24]` となっています。

![ma0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/083c35bd-3592-ccc9-bdc0-9cad99eafe73.png)
![ma0s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/61b532b3-8a90-c2da-4b0f-4768716913ef.png)

<font color='red'>**【注意】：各行の要素合計ではなくて、行方向（行が 0, 1, 2, 3, ... と大きくなる方向）に要素を合計していること**</font>に注意してください（ここを勘違いすると一気に？？？に陥ります（経験談））。

## `axis=1` を指定

```py:sum(axis=1)
s = x.sum(axis=1)
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 1
# print(s.shape) # -> (3,)
print(s)
```

```:実行結果
[10 26 42]
```

`axis=1` を指定すると、**列方向（列が 0, 1, 2, 3, ... と大きくなる方向）に要素が合計されています**。具体的には、$1+2+3+4=$<font color='red'>$\bf{10}$</font>、$5+6+7+8=$<font color='red'>$\bf{26}$</font>、$9+10+11+12=$<font color='red'>$\bf{42}$</font> が計算されて `[10 26 42]` となっています。

![ma1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/60f4f82b-431d-9a57-19a0-ae5a8628efc2.png)
![ma1s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/01281e45-9772-634f-75df-b3d1e0b940aa.png)



# 3次元のnumpy配列の場合

つづいて、3次元のnumpy配列を対象にします。例として、shape が (3,4,2) のnumpy配列 `x` を扱います。

```py:shape=(3,4,2)のnumpy配列を生成
import numpy as np
x = np.arange(1,24+1).reshape(3,4,2)
print(x)
# print(x.ndim)  # -> 3
# print(x.shape) # -> (3, 4, 2)
```

```:実行結果（読みやすくするために改行位置などの整形をしています）
[  [  [ 1  2]  [ 3  4]　[ 5  6]  [ 7  8]  ]
   [  [ 9 10]  [11 12]  [13 14]  [15 16]  ] 
   [  [17 18]  [19 20]  [21 22]  [23 24]  ]  ]
```

`axis=2` で分離して描くと次のようになります。

![m2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/2eb6791f-21f1-3798-a958-0353795c65bc.png)

## `axis=None` を指定

```py:sum(axis=None)
s = x.sum(axis=None)
# print(type(s)) # -> <class 'numpy.int64'>
# print(s.ndim)  # -> 0
# print(s.shape) # -> ()
print(s)
```

**すべての要素についての合計**です。具体的には $1+2+3+4+\cdots+22+23+24=300$ が計算されます。

```:実行結果
300
```

## `axis=0` を指定

```py:sum(axis=0)
s = x.sum(axis=0)
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 2
# print(s.shape) # -> (4, 2)
print(s)
```

```:実行結果
[ [27 30]
  [33 36]
  [39 42]
  [45 48] ]
```

`axis=0` の指定により、**行方向**に要素が合計されます。

上記の実行結果の最初の要素 `[27 30]` は、$1+9+17=$<font color='red'>$\bf{27}$</font>、$2+10+18=$<font color='red'>$\bf{30}$</font> のように計算された結果です。

![m2a0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/22697f09-6618-f7b4-fbdf-352f56ff53ef.png)
![m2as.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/9a08009e-550a-985c-cd01-50d9213bfc8f.png)

shape=(3,4,2) であった `x` が、`x.sum(axis=0)` で shape=(4,2) になります。 

## `axis=1` を指定

```py:sum(axis=1)
s = x.sum(axis=1)
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 2
# print(s.shape) # -> (3, 2)
print(s)
```

```:実行結果
[ [16 20]
  [48 52]
  [80 84] ]
```

`axis=1` の指定により、**列方向**に要素が合計されます。上記実行結果の最初の要素 `[16 20]` は、$1+3+5+7=$<font color='red'>$\bf{16}$</font>、$2+4+6+8=$<font color='red'>$\bf{20}$</font> のように計算された結果です。

![m2a1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/0ba0024b-fe06-b46d-ba80-0a42beb142a0.png)
![m2a1s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/4173e372-04cb-7a3e-0df8-2350d620dd95.png)

shape=(3,4,2) であった `x` が、`x.sum(axis=1)` で shape=(3,2) になります。 

## `axis=2` を指定

```py:sum(axis=2)
s = x.sum(axis=2)
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 2
# print(s.shape) # -> (3, 4)
print(s)
```

```:実行結果
[ [ 3  7 11 15]
  [19 23 27 31]
  [35 39 43 47] ]
```

`axis=2` の指定により、画像であれば**チャンネル方向**に要素が合計されます。上記実行結果の最初の左上の要素 `3` は $1+2=$<font color='red'>$\bf{3}$</font>、右下の要素 `47` は$23+24=$<font color='red'>$\bf{47}$</font> と計算された結果です。

![m2a2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/cc0249d8-fdd7-e7c3-cfa9-bad348d91348.png)
![m2a2s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/103757fb-19d0-1c9c-007c-8d2d0f77857a.png)

shape=(3,4,2) であった `x` が、`x.sum(axis=2)` で shape=(3,4) になります。 

## `axis=(0,1)` を指定

NumPy 1.7 以降では、axis をタプルで指定することができます。

`axis=(0,1)` の指定により、**行と列についての合計**が求められます。なお、`axis=(1,0)` としても結果は同じです（順番は関係ありません）。

```py:sum(axis=(0,1))
s = x.sum(axis=(0,1))
#print(type(s)) # -> <class 'numpy.ndarray'>
#print(s.ndim)  # -> 1
#print(s.shape) # -> (2,)
print(s)
```

```:実行結果
[144 156]
```

実行結果の最初の要素 `144` は、`x[:,:,0]` の全要素の合計、つまり $1+3+5+7+\cdots + 19+21+23$ を計算した結果になります。

![m2a01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/63e64317-0a7c-50cc-9f0e-c2843e5eeb15.png)
![m2a01s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/73293e8a-d6eb-e7b1-0b2f-3de32adc71ac.png)

shape=(3,4,2) であった `x` が、`x.sum(axis=(0,1))` で shape=(2,) になります。 

## `axis=(1,2)` を指定

`axis=(2,1)` としても同じです。

```py:sum(axis=(1,2))
s = x.sum(axis=(1,2))
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 1
# print(s.shape) # -> (3,)
print(s)
```

```:実行結果
[ 36 100 164]
```

実行結果の最初の要素 `36` は、下図の**青枠の要素の合計**、つまり、$(1+3+5+7)+(2+4+6+8)$ を計算した結果になります。

![m2a12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/a808a9cf-97ed-4428-8e8b-bb0f91886c3d.png)
![m2a12s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/d1fee704-d4ce-fee1-9947-e3bee330263b.png)

shape=(3,4,2) であった `x` が、`x.sum(axis=(1,2))` で shape=(3,) になります。 

## `axis=(0,2)` を指定

`axis=(2,0)` でも同じです。

```py:sum(axis=(0,2))
s = x.sum(axis=(0,2))
# print(type(s)) # -> <class 'numpy.ndarray'>
# print(s.ndim)  # -> 1
# print(s.shape) # -> (4,)
print(s)
```

```:実行結果
[57 69 81 93]
```
実行結果の最初の要素 `57` は、下図の**青枠の要素の合計**、つまり、$(1+9+17)+(2+10+18)$ を計算した結果になります。

![m2a02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/d55ab216-b7ed-2b65-12fc-572455ae6e97.png)
![m2a02s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/b19ddbe9-9d53-eb50-88c5-5872320520d2.png)

shape=(3,4,2) であった `x` が、`x.sum(axis=(0,2))` で shape=(4,) になります。 

## `axis=(0,1,2)` を指定

`sum(axis=(0,1,2))` は、`sum(axis=None)` または `sum()` と同じで全要素の合計が計算されます。 

```py:sum(axis=(0,1,2))
s = x.sum(axis=(0,1,2))
#print(type(s)) # -> <class 'numpy.int64'>
#print(s.ndim)  # -> 0
#print(s.shape) # -> ()
print(s)
```

```:実行結果
300
```

shape=(3,4,2) であった `x` が、`x.sum(axis=(0,1,2))` で shape=(0) になります。