note.nkmk.me

NumPy配列ndarrayに新たな次元を追加するnp.newaxis

Date: 2019-02-14 / tags: Python, NumPy

NumPy配列ndarrayに新たな次元を追加したい場合、np.newaxisを使う方法がある。

ここでは以下の内容について説明する。

  • np.newaxisNone
  • np.newaxisの使い方
  • np.newaxisでブロードキャストを制御
  • reshape()で新たな次元を追加する方法
スポンサーリンク

np.newaxisはNone

np.newaxisNoneのエイリアス。

import numpy as np

print(np.newaxis is None)
# True

分かりやすくするように別名が付けられているだけなので、以下のサンプルコードのnp.newaxisNoneに置き換えても同じように動作する。

np.newaxisの使い方

[]によるインデックスの中でnp.newaxisを使うとその位置にサイズが1の新たな次元が追加される。

a = np.arange(6).reshape(2, 3)
print(a)
# [[0 1 2]
#  [3 4 5]]

print(a.shape)
# (2, 3)

print(a[:, :, np.newaxis])
# [[[0]
#   [1]
#   [2]]
# 
#  [[3]
#   [4]
#   [5]]]

print(a[:, :, np.newaxis].shape)
# (2, 3, 1)
print(a[:, np.newaxis, :])
# [[[0 1 2]]
# 
#  [[3 4 5]]]

print(a[:, np.newaxis, :].shape)
# (2, 1, 3)
print(a[np.newaxis, :, :])
# [[[0 1 2]
#   [3 4 5]]]

print(a[np.newaxis, :, :].shape)
# (1, 2, 3)

複数のnp.newaxisを一度に使ってもOK。

print(a[np.newaxis, :, np.newaxis, :, np.newaxis])
# [[[[[0]
#     [1]
#     [2]]]
# 
# 
#   [[[3]
#     [4]
#     [5]]]]]

print(a[np.newaxis, :, np.newaxis, :, np.newaxis].shape)
# (1, 2, 1, 3, 1)

np.newaxisでブロードキャストを制御

NumPy配列ndarray同士の二項演算(四則演算など)ではブロードキャスト(broadcasting)という仕組みによりそれぞれの形状shapeが同じになるように自動的に変換される。

a = np.zeros(27, dtype=np.int).reshape(3, 3, 3)
print(a)
# [[[0 0 0]
#   [0 0 0]
#   [0 0 0]]
# 
#  [[0 0 0]
#   [0 0 0]
#   [0 0 0]]
# 
#  [[0 0 0]
#   [0 0 0]
#   [0 0 0]]]

print(a.shape)
# (3, 3, 3)

b = np.arange(9).reshape(3, 3)
print(b)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

print(b.shape)
# (3, 3)

print(a + b)
# [[[0 1 2]
#   [3 4 5]
#   [6 7 8]]
# 
#  [[0 1 2]
#   [3 4 5]
#   [6 7 8]]
# 
#  [[0 1 2]
#   [3 4 5]
#   [6 7 8]]]

詳細は上の関連記事を参照されたいが、ブロードキャストでは次元数を揃えるために次元数が少ない方の配列の先頭に新たな次元を加えるというルールがある。

np.newaxisで先頭に新たな次元を追加した場合はブロードキャストにより自動的に変換された場合と同じ結果になる。

print(b[np.newaxis, :, :].shape)
# (1, 3, 3)

print(a + b[np.newaxis, :, :])
# [[[0 1 2]
#   [3 4 5]
#   [6 7 8]]
# 
#  [[0 1 2]
#   [3 4 5]
#   [6 7 8]]
# 
#  [[0 1 2]
#   [3 4 5]
#   [6 7 8]]]

追加する位置を変えると異なる結果となる。

print(b[:, np.newaxis, :].shape)
# (3, 1, 3)

print(a + b[:, np.newaxis, :])
# [[[0 1 2]
#   [0 1 2]
#   [0 1 2]]
# 
#  [[3 4 5]
#   [3 4 5]
#   [3 4 5]]
# 
#  [[6 7 8]
#   [6 7 8]
#   [6 7 8]]]
print(b[:, :, np.newaxis].shape)
# (3, 3, 1)

print(a + b[:, :, np.newaxis])
# [[[0 0 0]
#   [1 1 1]
#   [2 2 2]]
# 
#  [[3 3 3]
#   [4 4 4]
#   [5 5 5]]
# 
#  [[6 6 6]
#   [7 7 7]
#   [8 8 8]]]

例えば、カラー画像の配列(形状: (高さ, 幅, 色))と単色画像(形状: (高さ, 幅))の配列を同じ位置同士で足したり引いたりしたい場合、そのままだとブロードキャストできずにエラーとなるが、単色画像の最後に新たな次元を追加するとうまくいく。このあたりの説明も以下の記事を参照。

reshape()で新たな次元を追加する方法

配列ndarrayの形状shapeを変換する方法としてreshape()メソッドがある。

reshape()メソッドに新たな次元を追加した形状を指定すれば当然ながらそのように変換される。np.newaxisを使った場合と同じ結果となる。

a = np.arange(6).reshape(2, 3)
print(a)
# [[0 1 2]
#  [3 4 5]]

print(a.shape)
# (2, 3)

print(a[:, :, np.newaxis])
# [[[0]
#   [1]
#   [2]]
# 
#  [[3]
#   [4]
#   [5]]]

print(a[:, :, np.newaxis].shape)
# (2, 3, 1)

print(a.reshape(2, 3, 1))
# [[[0]
#   [1]
#   [2]]
# 
#  [[3]
#   [4]
#   [5]]]

print(a.reshape(2, 3, 1).shape)
# (2, 3, 1)

上の例からも分かるようにnp.newaxisを使うと追加する次元以外の次元のサイズ(元の次元のサイズ)を明示的に指定する必要がない、というメリットがある。

reshape()でも、最初か最後に次元を追加する場合は、*を使ってタプルを展開して引数に指定することで、サイズを明示的に指定しなくてもよくなる。お好みで。

print(a.reshape(*a.shape, 1))
# [[[0]
#   [1]
#   [2]]
# 
#  [[3]
#   [4]
#   [5]]]

print(a.reshape(*a.shape, 1).shape)
# (2, 3, 1)
スポンサーリンク
シェア
このエントリーをはてなブックマークに追加

関連カテゴリー

関連記事