NumPy♪関数maxやsumにおけるkeepdims指定の図解

keepdims指定によって配列の次元数が維持されるのはわかります。でも、各次元の長さが、どのように変化するのかについては、すっきりしない方も多いのではないでしょうか。そこで、計算前後の変化について図を使って説明したいと思います。

なお、この記事は以下の記事の続編となります。図や解説の重複する部分が多いですが、先に以下の記事を読んでから読み進めていただければ、更に理解しやすいのではないかと思います。

NumPy♪axis指定を易しく図解(axis = -1とは)

「次元の長さ」「次元数」といった用語は以下の記事を参照してください。
Python♪用語集:NumPyの配列に関する日本語表現

また、この記事では「import numpy as np」が実行されたものとして、図中や解説で「numpy」を「np」と表現しています。

1.2次元配列のkeepdims

それでは2次元配列のkeepdimsについて説明します。以下、axisの方向です。

2次元配列とaxis

(1) axis = 0のときのkeepdims

下図は、keepdimsの指定がない場合の例です。np.max(x, axis=0)では、それぞれ、axis = 0の方向の中で1番大きい要素を選択し、[4 5 6]が計算結果です。keepdimsの指定が無い場合は、必ず次元数が1つ減ります

2次元配列のaxis0

ではkeepdims = Trueの場合はどうなるのでしょうか。keepdimsの名前の通り、次元数が配列の前後で変わりません。計算結果が2次元配列のままです。[[4] [5] [6]]のようにはなりません。[[4 5 6]]となります。

ただ、下の図で[[4] [5] [6]]となるのか、[[4 5 6]]となるのか判断するのは難しいと思います。

2次元配列のaxis0_keepdims

そこで、配列の形状がどのように変化するかを下図によって確認します。keepdimsの指定がない場合、x[2, 3]は、0番目の次元の長さが2、1番目の次元の長さが3であることを示し、axis = 0を指定して計算すると、0番目の次元が消滅し、全要素数3の1次元配列x = [4 5 6] となります。

つまり、axisで指定した次元の要素を使って計算し、axisで指定した次元は消えてしまいます。

どの次元が減る_axis0

次はkeedims = Trueの場合です。今度は次元が消えず、axisで指定したの部分の次元数が1になります。

どの次元が減る_axis0_keepdims

いかがですか、axis = 0, axis = 1については、このルールで全て説明できますので残りの解説は復習のつもりで読んでください。

(2) axis = 1のときのkeepdims

下図はkeepdimsの指定がない場合です。axis = 0の場合と同様に配列の次元数が1つ減ります。

2次元配列のaxis1

一方、keepdims = Trueの場合です。一番内側の角括弧が残ります。

2次元配列のaxis1_keepdims

keepdimsの指定がない場合の各次元の長さの変化を比較します。axisで指定した次元が消えます。

どの次元が減る_axis1

keepdims = Trueの場合、axisで指定した次元の長さが1になります。

どの次元が減る_axis1_keepdims

(3) axis = -1のときのkeepdims

keepdimsの指定の有無にかかわらず、2次元以上の配列ではaxis = -1はaxis = 1の場合と同じ結果になります。1次元の場合は後述します。

(4) サンプルコード

以下、サンプルコードです。

#コード01
import numpy as np

x = np.array([[1, 2, 3], [4 ,5 ,6]])
print(x.shape)
print('x:')
print(x)
print('axis = 0:')
print(np.max(x, axis=0))
print('axis = 0, keepdims=True:')
print(np.max(x, axis=0, keepdims=True))
print('axis = 1:')
print(np.max(x, axis=1))
print('axis = 1, keepdims=True:')
print(np.max(x, axis=1, keepdims=True))
print('axis = -1:')
print(np.max(x, axis=-1))
print('axis = -1, keepdims=True:')
print(np.max(x, axis=-1, keepdims=True))
#出力01
(2, 3)
x:
[[1 2 3]
 [4 5 6]]
axis = 0:
[4 5 6]
axis = 0, keepdims=True:
[[4 5 6]]
axis = 1:
[3 6]
axis = 1, keepdims=True:
[[3]
 [6]]
axis = -1:
[3 6]
axis = -1, keepdims=True:
[[3]
 [6]]

2.1次元配列のkeepdims

次に1次元配列のaxisを説明します。1次元配列では方向が1つしかないのでaxis = 1はエラーになります。また、1次元配列のときだけaxis = -1とaxis = 1の結果が異なります。

1次元配列とaxis

axis = 0のときのkeepdims

まずはkeepdimsの指定がない場合です。次元数が1つ減り、ただの値(スカラー)になります。

1次元配列のaxis0

keepdimsの指定がない場合はaxisで指定した次元が消えます。

1次元配列_次元の減り方

一方、keepdims = 1の場合は次元数が減りません。要素の数は3から1に変化します。

1次元配列のaxis0_keepdims

keepdims = Trueの場合、axisで指定した次元の長さが1になります。

1次元配列_次元の減り方_keepdims

(2) axis = 1のときのkeepdims

そもそもaxis = 1が存在しないので、keepdimsの指定の有無にかかわらずエラーになります。

2次元配列のときのイメージを引きずると1行3列のように考えてしまいますが、ただのベクトルなのでaxis = 0しか存在しません。

(3) axis = -1のときのkeepdims

keepdimsの指定の有無にかかわらず、axis = -1は1次元配列のときはaxis = 0の結果と同じになり、2次元以上の配列ではaxis = 1の結果と同じになります。

つまり、1次元配列のときaxis = 1ではエラーになりますが、axis = -1ではエラーになりません。

(4) サンプルコード

以下、サンプルコードです。

#コード02
import numpy as np

x = np.array([1, 2, 3])
print('x:')
print(x)
print('axis=0:')
print(np.max(x, axis=0))
print('axis=0, keepdims=True:')
print(np.max(x, axis=0, keepdims=True))
#print('axis=1:')
#print(np.max(x, axis=1))   #エラー(AxisError:)
#print('axis=1, keepdims=True:')
#print(np.max(x, axis=1, keepdims=True))   #エラー(AxisError:)
print('axis=-1:')
print(np.max(x, axis=-1))
print('axis=-1, keepdims=True:')
print(np.max(x, axis=-1, keepdims=True))
#出力02
x:
[1 2 3]
axis=0:
3
axis=0, keepdims=True:
[3]
axis=-1:
3
axis=-1, keepdims=True:
[3]

3.3次元配列のkeepdims

3次元配列もaxisが増えただけで2次元配列と同様の考え方です。axis = 0の向きが下図の奥行き方向であることに注意しましょう。

図では次元数によってaxis = 0の示す位置が変化して分かりにくいかもしれません。しかし、x[0, 0:2, 0:3]は図の手前のブロック、x[1, 0:2, 0:3]は図の置くのブロックであることを意識すればaxis = 0がどの方向を示しているのかがわかると思います。

3次元配列とaxis

(1) 計算結果の比較(axis = 0, 1, 2)

axsis =0, 1, 2のそれぞれの場合の計算結果について、keepdimsを指定した場合と指定しなかった場合で比較します。

(a) axis = 0

keepdimsの指定がない場合は配列の次元数が1減っていますが、keepdims = Trueを指定した場合は配列の次元数が変わりません。

下の図を見ると、どの部分の要素を比較して計算するのかがよく分かります。

なお、keepdimsの指定がない場合は、keepdims = Trueの場合の一番外側の角括弧がなくなったような形になっています。

3次元配列のaxis0_比較

(b) axis = 1

axis = 0のときと同様にkeepdimsの指定がない場合は配列の次元数が1減っていますが、keepdims = Trueを指定した場合は配列の次元数が変わりません。

しかし、keepdimsの指定がない場合、今度はkeepdims = Trueの場合の一番内側の角括弧がなくなったような形になり、axis = 0の場合と配列の形状が違います。

このように、やはり、以下の図だけでは配列の形状がどの様に変化するのかを想定するのは難しいと思います。

3次元配列のaxis1_比較

(c) axis = 2

下の図はaxis = 2のときの計算結果です。axis = 2の方向の要素の中で、それぞれ最大の値を計算していることがわかります。

なお、axis =2の時はkeepdimsの指定の有無で配列の形状が大きく異なりますので注意が必要です。

3次元配列のaxis2_比較

(2) 配列の形状変化(axis = 0, 1, 2)

配列の形状変化は、3次元配列の場合も配列xの形状x[2, 2, 3]から考えれば明快です。

(a) axis = 0

計算前の配列xは、0番目の次元の長さは2、1番目の次元の長さは2、2番目の次元の長さは3の配列です。

keepdims指定を行わなかった計算結果は、指定したaxis = 0の次元が消え、0番目の次元の長さが2、1番目の次元の長さが3となります。

一方、keepdims = Trueの場合は、指定したaxis = 0の次元の長さが1になります

どの次元が減るのかaxis0_3次元配列

(b) axis = 1

axis = 1の時も、axis = 0の場合と同様です。keepdims指定がない場合はaxisで指定した次元が消え、keepdims = Trueの場合はaxisで指定した次元の長さが1になります

どの次元が減るのかaxis1_3次元配列

(c) axis = 2

axis = 2の時も同様です。keepdims指定がない場合はaxisで指定した次元が消え、keepdims = Trueの場合はaxisで指定した次元の長さが1になります

どの次元が減るのかaxis2_3次元配列

(3) axis = -1

keepdims指定の有無にかかわらず、axis = -1の場合は、1次元配列のときはaxis = 0と同じ、2次元以上の配列ではaxis = 1と同じ計算結果になりますから、3次元配列ではaxis = 1と同じ結果になります。

(4) サンプルコード

以下、サンプルコードです。

#コード03
import numpy as np

x = np.array([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]])
print('x:')
print(x)
print('axis=0:')
print(np.max(x, axis=0))
print('axis=0, keepdims=True:')
print(np.max(x, axis=0, keepdims=True))
print('axis=1:')
print(np.max(x, axis=1))
print('axis=1, keepdims=True:')
print(np.max(x, axis=1, keepdims=True))
print('axis=2:')
print(np.max(x, axis=2))
print('axis=2, keepdims=True:')
print(np.max(x, axis=2, keepdims=True))
#出力03
x:
[[[ 1  2  3]
  [ 4  5  6]]

 [[ 7  8  9]
  [10 11 12]]]
axis=0:
[[ 7  8  9]
 [10 11 12]]
axis=0, keepdims=True:
[[[ 7  8  9]
  [10 11 12]]]
axis=1:
[[ 4  5  6]
 [10 11 12]]
axis=1, keepdims=True:
[[[ 4  5  6]]

 [[10 11 12]]]
axis=2:
[[ 3  6]
 [ 9 12]]
axis=2, keepdims=True:
[[[ 3]
  [ 6]]

 [[ 9]
  [12]]]

4.まとめ

keepdimsの指定の有無による計算結果の違いは以下の通りです。長々と図を使って説明しましたが、分かれば簡単です。

  • keepdims指定がない場合はaxisで指定した次元が消えます。
  • keepdims = Trueの場合はaxisで指定した次元の長さが1になります。
  • keepdimsの指定の有無にかかわらず、1次元配列では配列の次元数が1なのでaxis = 1はエラーになります。
  • keepdimsの指定の有無にかかわらず、axis = -1は1次元配列のときはaxis = 0の結果と同じになり、2次元以上の配列ではaxis = 1の結果と同じになります。

私が実際に購入した教材のご紹介

以下、私が実際に購入したPythonの教材をまとめてみました。 Pythonを学習する上で、少しでもお役に立つことができればうれしいです。

Python♪私が購入したPythonの書籍のレビュー
UdemyのPythonの動画講座を書籍を買う感覚で購入してみた

その他

Twitterへのリンクです。SNSもはじめました♪

液晶ペンタブレットを買いました
 (1) モバイルディスプレイを買うつもりだったのに激安ペンタブレット購入

以下、私が光回線を導入した時の記事一覧です。
 (1) 2020年「光回線は値段で選ぶ」では後悔する ←宅内工事の状況も説明しています。
 (2) NURO光の開通までWiFiルーターを格安レンタルできる
 (3) NURO光の屋外工事の状況をご紹介。その日に開通!
 (4) 光回線開通!実測するとNURO光はやっぱり速かった
 (5) ネット上のNURO光紹介特典は個人情報がもれないの?