numpy.rot90()

2025-05-26

numpy.rot90()とは

numpy.rot90(m, k=1, axes=(0, 1)) は、NumPyの配列(行列)を90度ずつ反時計回りに回転させる関数です。主に2次元配列に対して使用されますが、多次元配列の特定の2つの軸に沿って回転させることも可能です。

パラメータ

  • axes:回転させる軸を指定するタプル。デフォルトは(0, 1)で、これは2次元配列の行(axis 0)と列(axis 1)を回転させることを意味します。多次元配列の場合、このタプルで指定された2つの軸に沿って回転が行われます。例えば、(0, 2)と指定すると、0番目の軸と2番目の軸の間で回転が行われます。
  • k:回転回数。デフォルトは1で、1回(90度)反時計回りに回転させます。正の整数を指定すると反時計回りに、負の整数を指定すると時計回りに回転します。例えば、k=2なら180度、k=-1なら90度時計回り(270度反時計回り)に回転します。
  • m:回転させたい配列(NumPy ndarray)。

戻り値

回転された新しい配列が返されます。元の配列は変更されません。

2次元配列の回転

import numpy as np

# 2x3の配列を作成
arr = np.array([[1, 2, 3],
                [4, 5, 6]])

print("元の配列:\n", arr)

# 1回(90度)反時計回りに回転
rotated_arr_90 = np.rot90(arr)
print("\n90度反時計回りに回転:\n", rotated_arr_90)

# 2回(180度)反時計回りに回転
rotated_arr_180 = np.rot90(arr, k=2)
print("\n180度反時計回りに回転:\n", rotated_arr_180)

# 1回(90度)時計回りに回転 (k=-1 または k=3 と同じ)
rotated_arr_minus_90 = np.rot90(arr, k=-1)
print("\n90度時計回りに回転:\n", rotated_arr_minus_90)

出力

元の配列:
 [[1 2 3]
 [4 5 6]]

90度反時計回りに回転:
 [[3 6]
 [2 5]
 [1 4]]

180度反時計回りに回転:
 [[6 5 4]
 [3 2 1]]

90度時計回りに回転:
 [[4 1]
 [5 2]
 [6 3]]

多次元配列の回転

axes引数を使用することで、多次元配列の特定の軸を回転させることができます。

import numpy as np

# 3x3x2の3次元配列を作成
arr_3d = np.arange(1, 19).reshape(3, 3, 2)
print("元の3次元配列:\n", arr_3d)

# デフォルトのaxes=(0, 1)で回転
# 0番目の軸(行)と1番目の軸(列)を回転
rotated_3d_default = np.rot90(arr_3d)
print("\nデフォルトのaxes=(0, 1)で回転:\n", rotated_3d_default)

# axes=(0, 2)で回転
# 0番目の軸(行)と2番目の軸(一番内側の軸)を回転
rotated_3d_axes_0_2 = np.rot90(arr_3d, axes=(0, 2))
print("\naxes=(0, 2)で回転:\n", rotated_3d_axes_0_2)

この例では、axesを変更することで、どの「平面」で回転が行われるかが変わることがわかるでしょう。



numpy.rot90()は比較的シンプルな関数ですが、それでも使い方を誤るとエラーが発生したり、期待通りの結果が得られなかったりすることがあります。

axes引数の指定ミス

エラー/問題点

  • 多次元配列で、意図しない軸の組み合わせを回転させてしまう。
  • axesに指定する軸の番号が、配列の次元数を超えている。
  • axesに指定するタプルの要素数が2ではない。


import numpy as np

arr_3d = np.arange(1, 28).reshape(3, 3, 3)

# エラー: axesの要素数が2ではない
# np.rot90(arr_3d, axes=(0,)) # TypeError: 'axes' must be a tuple of 2 integers

# エラー: 軸の番号が次元数を超える (この配列は0, 1, 2の軸しか持たない)
# np.rot90(arr_3d, axes=(0, 3)) # AxisError: axis 3 is out of bounds for array of dimension 3

トラブルシューティング

  • 多次元配列の場合、どの2つの軸を回転させたいのかを明確にし、それに対応する正しい軸番号を指定してください。例えば、画像を回転させる場合、通常は高さと幅に対応する軸(多くの場合0と1)を指定します。
  • 指定する軸の番号が、対象の配列の次元数(arr.ndim)より小さいことを確認してください。軸は0からndim-1までです。
  • axes引数には必ず2つの整数を指定してください。これは、rot90()が常に「2次元平面」での回転を扱うためです。

k引数の型/値の誤り

エラー/問題点

  • kが非整数型(floatなど)である。
  • kに整数以外の値を指定した。


import numpy as np

arr = np.array([[1, 2], [3, 4]])

# エラー: kが整数ではない
# np.rot90(arr, k=0.5) # TypeError: 'k' must be an integer

トラブルシューティング

  • k引数には必ず整数を指定してください。正の整数は反時計回り、負の整数は時計回りの回転回数を表します。

回転後の配列の形状(shape)の変化の理解不足

エラー/問題点

  • 回転後の配列の形状が期待と異なる。特に、非正方行列を回転させた場合に発生しやすい。


import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6]]) # 形状: (2, 3)

rotated_arr = np.rot90(arr)
print("元の形状:", arr.shape)         # (2, 3)
print("回転後の形状:", rotated_arr.shape) # (3, 2)

この場合、エラーは発生しませんが、もし回転後の配列が元の形状と同じであることを期待していると、論理的な問題につながります。

トラブルシューティング

  • 後続の処理で配列の形状に依存する操作を行う場合は、回転後のshapeを確認し、必要に応じて対応するロジックを調整してください。
  • numpy.rot90()は、回転によって配列の形状が変化する可能性があることを理解してください。特に、元の配列が正方行列でない場合、回転後の行数と列数は入れ替わります。

元の配列が変更されるという誤解

エラー/問題点

  • numpy.rot90()がインプレースで配列を変更すると誤解し、戻り値を無視して元の配列にアクセスしてしまう。


import numpy as np

arr = np.array([[1, 2], [3, 4]])
np.rot90(arr) # 戻り値を変数に代入しない

print("元の配列(変更されていない):\n", arr)

トラブルシューティング

  • 回転結果を利用するには、必ず戻り値を変数に代入してください。
    rotated_arr = np.rot90(arr)
    
  • numpy.rot90()は、新しい配列を返します。元の配列は変更されません。

軸の概念の混同 (特に画像処理など)

エラー/問題点

  • axes=(0, 1)(1, 0)の違いを理解していない。
  • 画像の高さ、幅、チャンネルなどとNumPy配列の軸(axis)の対応関係を誤解している。
  • 多次元配列(例: (高さ, 幅, チャンネル)の画像データ)を扱う場合は、どの軸を回転させたいのかを明確に意識し、それに応じたaxesを指定することが重要です。例えば、画像を平面内で回転させたい場合は、高さと幅の軸を指定します。
  • axes=(0, 1)は「0番目の軸と1番目の軸を交換しながら回転する」ことを意味します。axes=(1, 0)も同じ回転を意味しますが、NumPyの内部処理で若干の違いがある可能性はありますが、結果的には同じになります。重要なのは、どの2つの軸を対象にするかです。
  • NumPyの配列の軸は、0から始まるインデックスで表現されます。2次元配列では、通常axis=0が行(高さ)に、axis=1が列(幅)に対応します。


numpy.rot90()は、配列(主に2次元配列)を90度ずつ反時計回りに回転させるための便利な関数です。ここでは、基本的な使い方から、より複雑な応用例まで、いくつかのコード例を示します。

例1:基本的な2次元配列の回転

最も基本的な使い方です。デフォルトでは90度反時計回りに1回回転します。

import numpy as np

# 3x3の配列を作成
arr_original = np.array([[1, 2, 3],
                         [4, 5, 6],
                         [7, 8, 9]])

print("--- 例1: 基本的な2次元配列の回転 ---")
print("元の配列:\n", arr_original)

# 90度反時計回りに1回回転 (k=1がデフォルト)
rotated_arr_90 = np.rot90(arr_original)
print("\n90度反時計回りに回転:\n", rotated_arr_90)

# 180度反時計回りに回転 (k=2)
rotated_arr_180 = np.rot90(arr_original, k=2)
print("\n180度反時計回りに回転:\n", rotated_arr_180)

# 270度反時計回りに回転 (k=3) または 90度時計回りに回転 (k=-1)
rotated_arr_270 = np.rot90(arr_original, k=3)
print("\n270度反時計回りに回転:\n", rotated_arr_270)

rotated_arr_minus_90 = np.rot90(arr_original, k=-1)
print("\n90度時計回りに回転 (k=-1):\n", rotated_arr_minus_90)

出力のポイント

  • k=-1k=3と同じ結果になり、時計回りに90度回転します。
  • k=2では、元の配列が上下左右反転した状態になります。
  • k=1(デフォルト)では、元の配列の右上の要素が左下に移り、列が反時計回りに回転します。

例2:非正方行列の回転と形状の変化

元の配列が正方行列でない場合、回転後に形状が変わることに注意が必要です。

import numpy as np

# 2x3の配列を作成
arr_rect = np.array([[10, 11, 12],
                     [13, 14, 15]])

print("\n--- 例2: 非正方行列の回転と形状の変化 ---")
print("元の配列:\n", arr_rect)
print("元の形状:", arr_rect.shape) # (2, 3)

# 回転
rotated_rect = np.rot90(arr_rect)
print("\n回転後の配列:\n", rotated_rect)
print("回転後の形状:", rotated_rect.shape) # (3, 2)

出力のポイント

  • 元の(2, 3)の配列が回転後には(3, 2)になっています。行数と列数が入れ替わっています。

例3:3次元配列の特定の軸での回転

axes引数を使用して、多次元配列の特定の2つの軸に沿って回転させることができます。

import numpy as np

# 3x3x2の3次元配列を作成 (例えば、3x3の画像が2チャンネルあると考える)
# 各スライスは以下のようなイメージ
# arr_3d[:, :, 0] = [[ 1,  2], [ 4,  5], [ 7,  8]]
# arr_3d[:, :, 1] = [[ 3,  6], [ 9, 10], [11, 12]]
arr_3d = np.arange(1, 19).reshape(3, 3, 2)
print("\n--- 例3: 3次元配列の特定の軸での回転 ---")
print("元の3次元配列:\n", arr_3d)

# axes=(0, 1)で回転 (デフォルト): 最初の2つの軸(行と列)を回転
# 各「深度」(2番目の軸)に対して2D回転が行われるイメージ
rotated_3d_01 = np.rot90(arr_3d, axes=(0, 1))
print("\naxes=(0, 1)で回転 (デフォルト):\n", rotated_3d_01)
print("形状:", rotated_3d_01.shape) # (3, 3, 2) -> 最初の2つの軸の次元が入れ替わった結果

# axes=(0, 2)で回転: 0番目の軸(行)と2番目の軸(最も内側の軸)を回転
rotated_3d_02 = np.rot90(arr_3d, axes=(0, 2))
print("\naxes=(0, 2)で回転:\n", rotated_3d_02)
print("形状:", rotated_3d_02.shape) # (2, 3, 3) -> 0番目と2番目の軸の次元が入れ替わった結果

# axes=(1, 2)で回転: 1番目の軸と2番目の軸を回転
rotated_3d_12 = np.rot90(arr_3d, axes=(1, 2))
print("\naxes=(1, 2)で回転:\n", rotated_3d_12)
print("形状:", rotated_3d_12.shape) # (3, 2, 3) -> 1番目と2番目の軸の次元が入れ替わった結果

出力のポイント

  • (0, 2)(1, 2)のように指定すると、元の配列の異なる次元が回転の対象となり、結果として出力の形状もそれに応じて変わります。
  • (0, 1)は、最も一般的な2D回転で、画像処理における「高さと幅」の回転に相当します。
  • axes引数を指定することで、多次元配列内のどの「平面」に対して回転操作を行うかを制御できます。

例4:画像処理における利用(擬似コード)

numpy.rot90()は画像処理でよく使われます。ここでは実際の画像ファイルを読み込む代わりに、配列で画像を模倣した例を示します。

import numpy as np
# import matplotlib.pyplot as plt # 画像表示にはmatplotlibなどが必要

# 簡易的な「画像」配列を作成 (例えば、5x5のグレースケール画像)
# 中央に明るい四角があるイメージ
image = np.zeros((5, 5), dtype=int)
image[1:4, 1:4] = 255 # 中央に255の四角

print("\n--- 例4: 画像処理における利用 ---")
print("元の画像 (擬似):\n", image)

# 90度反時計回りに回転
rotated_image_90 = np.rot90(image)
print("\n90度反時計回りに回転した画像:\n", rotated_image_90)

# 180度回転
rotated_image_180 = np.rot90(image, k=2)
print("\n180度回転した画像:\n", rotated_image_180)

# 90度時計回り(-90度)に回転
rotated_image_minus_90 = np.rot90(image, k=-1)
print("\n90度時計回りに回転した画像:\n", rotated_image_minus_90)

# 実際の画像処理では、以下のように表示して確認します(matplotlibが必要)
# plt.figure(figsize=(10, 5))
# plt.subplot(1, 3, 1)
# plt.imshow(image, cmap='gray')
# plt.title('Original')
# plt.subplot(1, 3, 2)
# plt.imshow(rotated_image_90, cmap='gray')
# plt.title('Rotated 90 deg')
# plt.subplot(1, 3, 3)
# plt.imshow(rotated_image_180, cmap='gray')
# plt.title('Rotated 180 deg')
# plt.show()
  • 画像データ(NumPy配列)を回転させることで、簡単に画像の向きを変更できます。これはデータ拡張などでも利用されます。


numpy.rot90()の代替となるNumPyでの配列回転方法

numpy.rot90()は特定の角度(90度の倍数)で反時計回りに回転させるための便利な関数ですが、より汎用的な回転や特定の要件がある場合には、他のNumPy関数を組み合わせることで同等またはそれ以上の柔軟な操作が可能です。

主な代替方法としては、以下のものが挙げられます。

  1. numpy.transpose() または arr.Tnumpy.flip() / arr.flip() の組み合わせ
  2. numpy.rollaxis() / numpy.moveaxis()numpy.flip() の組み合わせ(多次元の場合)
  3. スライス操作の組み合わせ

それぞれ詳しく見ていきましょう。

numpy.transpose() または arr.T と numpy.flip() / arr.flip() の組み合わせ

これは2次元配列の回転において最も一般的で理解しやすい代替方法です。

  • numpy.flip(arr, axis): 指定した軸に沿って配列の要素の順序を反転させます。
  • arr.T または numpy.transpose(arr): 配列の軸を転置(行と列を入れ替え)します。

90度反時計回り (np.rot90(arr)) の代替

これは、転置してから新しい列の順序を反転させることで実現できます。

import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]])

print("元の配列:\n", arr)

# numpy.rot90(arr) と同じ結果
# まず転置(行と列を入れ替え)
transposed_arr = arr.T
# 次に、新しい列(元の行)を上下反転させる(軸0で反転)
# numpy.flip(transposed_arr, axis=0) でも同じ
rotated_by_alt = np.flip(transposed_arr, axis=0)

print("\n代替方法 (90度反時計回り):\n", rotated_by_alt)
print("rot90の結果:\n", np.rot90(arr))

90度時計回り (np.rot90(arr, k=-1)) の代替

import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]])

print("元の配列:\n", arr)

# numpy.rot90(arr, k=-1) と同じ結果
# まず転置
transposed_arr = arr.T
# 次に、新しい行(元の列)を左右反転させる(軸1で反転)
# numpy.flip(transposed_arr, axis=1) でも同じ
rotated_by_alt_neg90 = np.flip(transposed_arr, axis=1)

print("\n代替方法 (90度時計回り):\n", rotated_by_alt_neg90)
print("rot90(k=-1)の結果:\n", np.rot90(arr, k=-1))

180度回転 (np.rot90(arr, k=2)) の代替

これは、両方の軸を反転させることで実現できます。

import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]])

print("元の配列:\n", arr)

# numpy.rot90(arr, k=2) と同じ結果
# 軸0(行)を反転
flipped_axis0 = np.flip(arr, axis=0)
# さらに軸1(列)を反転
rotated_by_alt_180 = np.flip(flipped_axis0, axis=1)

print("\n代替方法 (180度回転):\n", rotated_by_alt_180)
print("rot90(k=2)の結果:\n", np.rot90(arr, k=2))

利点

  • 2次元配列の回転には非常に直感的。
  • 操作が明確で、結果を推論しやすい。

欠点

  • kの値に応じて、転置とフリップの組み合わせを自分で考える必要がある。
  • 多次元配列で特定の軸を回転させる場合、axes引数を持つrot90()の方が簡潔になることがある。

numpy.rollaxis() / numpy.moveaxis() と numpy.flip() の組み合わせ(多次元の場合)

多次元配列でnumpy.rot90()axes引数のような柔軟性が欲しい場合に有効です。

  • numpy.moveaxis(a, source, destination): ソース軸をデスティネーション軸の位置に移動します。rollaxisよりも直感的で推奨されます。
  • numpy.rollaxis(a, axis, start=0): 指定した軸を特定の位置にロール(移動)します。

例えば、np.rot90(arr_3d, axes=(0, 2)) を実現する場合、0番目の軸と2番目の軸を入れ替え(転置に相当)、その後フリップします。

import numpy as np

arr_3d = np.arange(1, 28).reshape(3, 3, 3)
print("元の3次元配列:\n", arr_3d)

# numpy.rot90(arr_3d, axes=(0, 2)) と同じ結果を目指す
# まず、0番目の軸と2番目の軸を入れ替える(転置に相当)
# (3, 3, 3) -> (3, 3, 3) となるが、軸の順序が (1, 0, 2) から (0, 2, 1) へ
# この場合、元の軸の順番 (0, 1, 2) を (2, 1, 0) に変更し、その後フリップする
# 複雑なので、rot90の内部動作を模倣するより、直接的な転置とフリップを考える方が良い

# 90度反時計回りに回転の一般化されたロジック (axes=(ax1, ax2)の場合):
# 1. ax1とax2を入れ替えるように軸を並び替える
# 2. 新しいax1(元ax2)をフリップする

ax1, ax2 = 0, 2 # rot90(axes=(0, 2)) を模倣

# 軸の順序を入れ替える: 例えば (0, 1, 2) -> (2, 1, 0) とし、その後フリップ
# これは実際には rot90 の動作を完全に再現するわけではないので注意が必要
# rot90 は軸の順序を変更しつつ、フリップを適用する

# ここでは、より直接的に「見た目の回転」を実現する方法を考える
# `moveaxis`で軸を入れ替えるのは転置の汎用版。フリップと組み合わせる。
# rot90(m, axes=(a, b)) の場合、内部的には moveaxis(m, b, a) してから a軸を反転させる
# 参考: https://github.com/numpy/numpy/blob/v1.26.0/numpy/lib/function_base.py#L900-L968

# rot90(arr_3d, axes=(0, 2)) の代替
# まず、軸0と軸2を入れ替える
transposed_02 = np.moveaxis(arr_3d, 0, 2) # 元の軸0が新しい軸2に、元の軸2が新しい軸0に
# 次に、新しい軸0(元の軸2)を反転させる
rotated_alt_3d_02 = np.flip(transposed_02, axis=0)

print("\n代替方法 (axes=(0, 2)で回転):\n", rotated_alt_3d_02)
print("rot90(axes=(0, 2))の結果:\n", np.rot90(arr_3d, axes=(0, 2)))

利点

  • moveaxisは軸の移動が直感的。
  • 多次元配列において、rot90と同じように任意の2つの軸間で回転をシミュレートできる。

欠点

  • rot90の内部ロジックを理解していないと、どの軸をmoveaxisで移動させ、どの軸をflipさせるべきか判断が難しい場合がある。

スライス操作の組み合わせ

NumPyのスライス機能を使って、配列の要素を直接並び替えることで回転を実現することも可能です。これは非常に柔軟ですが、複雑な回転になるほどスライス指定が難しくなります。

180度回転 (np.rot90(arr, k=2)) の代替

import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]])

print("元の配列:\n", arr)

# 180度回転 (上下反転してから左右反転)
rotated_by_slice = arr[::-1, ::-1] # 行を逆順に、列を逆順に

print("\n代替方法 (スライスで180度回転):\n", rotated_by_slice)
print("rot90(k=2)の結果:\n", np.rot90(arr, k=2))

90度反時計回り (np.rot90(arr)) の代替 (2次元の場合)

import numpy as np

arr = np.array([[1, 2, 3],
                [4, 5, 6],
                [7, 8, 9]])

print("元の配列:\n", arr)

# 90度反時計回り回転 (転置してから行を逆順に)
# arr.T[::-1, :] でも同じですが、今回は結合で説明
# 各列を逆順に取り出し、それを新しい行として結合
rotated_by_slice_90 = np.array([arr[:, i][::-1] for i in range(arr.shape[1])])

# もっと簡潔な方法:
rotated_by_slice_90_v2 = arr.T[::-1, :]

print("\n代替方法 (スライスで90度反時計回り):\n", rotated_by_slice_90_v2)
print("rot90の結果:\n", np.rot90(arr))

利点

  • NumPyの強力なスライス機能を理解するのに役立つ。
  • 非常に柔軟で、任意の複雑な並び替えが可能。
  • パフォーマンスがrot90flip/transposeの組み合わせより劣る可能性がある。
  • 汎用性が低く、回転の種類ごとにスライスロジックを設計する必要がある。
  • コードが読みにくくなりがち。
  • 非常に特殊な並び替えや、パフォーマンスを重視する場面では、スライス操作も検討できますが、可読性とのトレードオフになります。
  • numpy.rot90()で対応できないような特定の軸の入れ替えや反転が必要な場合は、numpy.transpose()arr.Tnumpy.flip()numpy.moveaxis()を組み合わせて使うのが良いでしょう。
  • シンプルに90度の倍数で回転させるだけなら、numpy.rot90()が最も簡潔で可読性が高いです。
  • 最も推奨されるのは、状況に応じて適切な方法を選ぶことです。