NumPyのshape表記の (2, ) って何ですか?
目次
サマリ
- (2, )
- np.shapeでndarray(NumPy配列)の形状取得した結果
- ndarrayが1次元の場合の表記
- 特徴
- 2次元目がブランク(空白)になっている
- (2)のようにすればいいと感じる
- (2, )となっている理由
- 分かりやすいから(理由は後述)
はじめに
前提
- np.shape、np.ndim
- np.arange
- np.reshape
次元の呼び方(大切なので…)
- (3,4)は?
- 「3」と「4」の2つで2次元
- 行と列の行列なので2次元
- 次元が2つあるので2次元
- 一番左数字の呼び方
- 最初の次元
- 1次元目(推奨)
- 「~目」は序数(~番目)になる
- 第1次元(おすすめではない)
- 浸透していない
- 0次元、1次元(おすすめではない)
- 人によってまちまち
- インデックス番号の0と第1次元の「第」省略したものがミックスされた?
- (3,4)の「3」は?
- 1次元目の要素数で、shape[0]で取得できる
- shape[0]なので、0次元と言ってしまうのもわかる気がする
- (3,4)の「4」は?
- 2次元目の要素数で、shape[1]で取得できる
- 補足:0次元とは?
- スカラーのこと
- ベクトル表記すると「()」(中身なし)
- 参考
- 次元を説明した表
- 外部リンク: テンソルの次元について
- 次元を説明した表
詳細説明
言葉で説明
- (2, )は1次元
- 1次元目の要素数は2
- array([1,2])やarray([10,5])のようなデータ
- 2次元目はないのでブランク(空白)になっている
- np.ndimで確認すると1
- 1次元目の要素数は2
- 行列のように2次元データにすることもできる
- 形状は(1,2)のようになる
- 1次元目の要素数は1、2次元目の要素数は2
- array([[1,2]])やarray([[10,5]])のようなデータ
- np.ndimで確認すると2
- 中身のデータは同じなので、状況に応じて使いやすい方を使えばいい
- 以降ではどのような場合にそれぞれのデータが適しているかを説明する
データを見ながら説明
1次元
import numpy as np
a = np.arange(2)
print(a)
print(a.ndim)
print(a.shape)
[0 1] 1 (2,)
- 次元数は1次元
- (2, )
- 1次元目の要素数が2
- 2次元目の要素数は0(なし)
2次元(行列)
import numpy as np
b = np.arange(2).reshape(1,2)
print(b)
print(b.ndim)
print(b.shape)
[[0 1]] 2 (1, 2)
- 次元数は2次元
- (1,2)
- 1次元目の要素数が1
- 2次元目の要素数は2
- ここまでで両者の違いが何となく分かった
- 以降でそれぞれのよいところや気を付けたいところを説明
(2, )表記もありだなと思える
- 1次元、2次元、3次元、…と並べてみる
(2, ) …… 1次元(中の数字は1つ)
(3,2) …… 2次元(中の数字は2つ)
(2,1,3) …… 3次元(中の数字は3つ)
(4,1,2,2) …… 4次元(中の数字は4つ) - ここで1次元の(2, )を(1,2)に入れ替えると、違和感があります
- (2)のような表記にしないことでndarrayだとすぐにわかります
- (2, )の表記もありだなと思えました?
- 考え方
- 上記でも触れたように(2, )でも(1,2)でも中のデータは同じ
- 以降でどのような場合に適しているかを説明
適しているパターンを探る
(1,2)がよい場合
データ作成
import numpy as np
a = np.array([1,2])
b = np.array([[1,2]])
c = np.array([[1,2], [3,4]])
print(f'[a]shape:{a.shape} ndim:{a.ndim}')
print(f'[b]shape:{b.shape} ndim:{b.ndim}')
print(f'[c]shape:{c.shape} ndim:{c.ndim}')
[a]shape:(2,) ndim:1 [b]shape:(1, 2) ndim:2 [c]shape:(2, 2) ndim:2
- a
- 1次元
- データは1,2の2つ
- b
- 2次元
- データは1,2の2つ
- c
- 普通の行列
- 2次元
- データは1,2,3,4の4つ
同じように取り出してみると
print(a[0])
print(a[0].shape)
print(b[0])
print(b[0].shape)
print(c[0])
print(c[0].shape)
1 () [1 2] (2,) [1 2] (2,)
- a[0]
- 1
- スカラー
- b[0]
- 1と2
- 1次元ndarray(NumPy配列)
- c[0]
- 1と2
- 1次元ndarray
- aとbを比較する
- 同じように取り出しているが取得できるデータが異なる
- cの取り出し結果を見ると、行列として処理を作っている場合はaよりbの方が扱いやすそうです
(2, )がよい場合
データ作成
a = np.arange(6).reshape(3,2)
b = np.arange(2)
c = np.arange(2).reshape(1,2)
print(a)
print(a.shape)
print(b)
print(b.shape)
print(c)
print(c.shape)
[[0 1] [2 3] [4 5]] (3, 2) [0 1] (2,) [[0 1]] (1, 2)
- a
- 形状(3,2)のndarray(NumPy配列)を作成
- b
- 形状(2, )のndarrayを作成
- c
- 形状(1,2)のndarrayを作成
- aに対するb,cのdot積を計算する
np.dot(a,b)
rtn = np.dot(a,b)
print(rtn)
print(rtn.shape)
[1 3 5] (3,)
- (3,2)と(2, )のdot積計算
- (3, )
- dot積計算の法則の通り間の2がなくなった形状になっている
- 例えば、(3,2)と(2,4)の計算なら(3,4)になるのと同様に扱える
np.dot(a,c)
rtn = np.dot(a,c)
print(rtn)
print(rtn.shape)
ValueError: shapes (3,2) and (1,2) not aligned: 2 (dim 1) != 1 (dim 0)
- (3,2)と(1,2)のdot積計算
- dot積の計算のルールに沿っていないため計算できない
- 計算しようと思えば、(3,2)と(2,1)にすれば計算できる
- その場合、計算結果は(3,1)でいい?(1,3)に逆転させるべき?
- このことから「(2, )」のような表記も素直に受け入れた方がよいことが分かる
- 実際のコードでは(2, )の方が一般的で、(1,2)の方が珍しいです
- 参考
- np.dotのルール
- NumPy:【機械学習】で使う行列計算
まとめ
- それぞれの特徴を知っておく
- (2, )の方が(1,2)より一般的
- 状況に応じて使い分けれるようにしておく
- もし(1,2)のように変更しているコードがあったなら
- 何か理由があると考える
- ロジック理解やバグを見つけるきっかけになる
- 思わぬところで時間を取られることも減る
補足
- pandasも同じような考え方があります
- pandasではSeriesとDataFrameと言った別の呼び方になります
