Skip to content
トピック
NumPy
NumPy Reshape:Pythonで配列の形状を変更する方法

NumPy Reshape:Pythonで配列の形状を変更する方法

Updated on

10,000個のピクセル値のフラットなリストがあり、ニューラルネットワークは100x100の画像行列を期待しています。あるいは、特徴量配列が1次元であるのに列ベクトルが必要なため、scikit-learnがエラーを投げているかもしれません。これらはエッジケースではありません -- Pythonで数値データを扱う人にとって日常的な障害です。これらを解決する関数がnumpy.reshape()であり、正しく理解すればデバッグの時間を何時間も節約できます。

📚

numpy reshapeは何をするのか?

numpy.reshape()は、データを変更せずに配列の形状を変更します。同じ要素を取り、新しい次元のセットに再配置します。12要素の1D配列は、3x4行列、2x6行列、4x3行列、さらには2x2x3の3次元配列にもなります。唯一のルールは、要素の総数が同じでなければならないことです。

import numpy as np
 
a = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
print(a.shape)
# (12,)
 
b = a.reshape(3, 4)
print(b)
# [[ 1  2  3  4]
#  [ 5  6  7  8]
#  [ 9 10 11 12]]
 
print(b.shape)
# (3, 4)

元の配列aは元の形状のまま存在し続けます。reshape後の配列bは同じデータの新しいビューです(ビューとコピーについては後述)。

基本的な構文とパラメータ

NumPyで配列をreshapeする方法は2つあります:

# 方法1:関数として
np.reshape(array, newshape, order='C')
 
# 方法2:配列のメソッドとして
array.reshape(newshape, order='C')
パラメータ説明
arrayndarrayreshapeする配列(np.reshape()関数形式でのみ使用)
newshapeintまたはintのタプル目標の形状。1つの次元を-1にでき、自動的に計算されます。
order'C''F'、または'A'要素の読み書き順序。'C' = 行優先(デフォルト)、'F' = 列優先、'A' = 配列がFortran連続ならFortran順、そうでなければC順。

どちらの形式も同じ結果を生成します。メソッド形式(array.reshape())の方がコード中で自然に読めるため、より一般的です。

1Dから2D配列へのreshape

これは最も頻繁なreshape操作です。フラットな配列から始めて行列が必要な場合です。

import numpy as np
 
# 8要素の1D配列
data = np.array([10, 20, 30, 40, 50, 60, 70, 80])
 
# 2行4列にreshape
matrix_2x4 = data.reshape(2, 4)
print(matrix_2x4)
# [[10 20 30 40]
#  [50 60 70 80]]
 
# 4行2列にreshape
matrix_4x2 = data.reshape(4, 2)
print(matrix_4x2)
# [[10 20]
#  [30 40]
#  [50 60]
#  [70 80]]
 
# 列ベクトルにreshape(8行1列)
column_vector = data.reshape(8, 1)
print(column_vector)
# [[10]
#  [20]
#  [30]
#  [40]
#  [50]
#  [60]
#  [70]
#  [80]]
 
print(column_vector.shape)
# (8, 1)

列ベクトルへのreshape(n, 1)は特に重要です。多くのscikit-learn関数は、単一の特徴量であっても2D入力を必要とします。1D配列を渡すと、Expected 2D array, got 1D array insteadというエラーが発生します。修正方法は.reshape(-1, 1)です。

2Dから3Dへのreshape(機械学習とディープラーニング向け)

TensorFlowやPyTorchなどのディープラーニングフレームワークは、しばしば3Dまたは4Dの入力テンソルを必要とします。例えば、グレースケール画像のバッチは(batch_size, height, width)の形状が必要で、カラー画像のバッチは(batch_size, height, width, channels)が必要です。

import numpy as np
 
# 2D配列の行として格納された4枚の3x3グレースケール画像をシミュレート
images_2d = np.arange(36).reshape(4, 9)
print(images_2d.shape)
# (4, 9)
 
# (バッチ、高さ、幅)にreshape
images_3d = images_2d.reshape(4, 3, 3)
print(images_3d.shape)
# (4, 3, 3)
 
print(images_3d[0])
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

3チャンネル(RGB)のカラー画像の場合:

import numpy as np
 
# 2枚のカラー画像、各4x4で3チャンネル、フラットに格納
flat_data = np.arange(96)  # 2 * 4 * 4 * 3 = 96
 
images = flat_data.reshape(2, 4, 4, 3)
print(images.shape)
# (2, 4, 4, 3)

-1トリック:1つの次元を自動計算

1つの次元を-1に設定すると、NumPyは要素の総数と指定した他の次元に基づいて自動的に計算します。これはreshapeの最も便利な機能の1つです。

import numpy as np
 
arr = np.arange(12)
 
# 「3行にして、列は計算して」
print(arr.reshape(3, -1))
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]
# 形状: (3, 4) -- NumPyが4列を計算
 
# 「2列にして、行は計算して」
print(arr.reshape(-1, 2))
# [[ 0  1]
#  [ 2  3]
#  [ 4  5]
#  [ 6  7]
#  [ 8  9]
#  [10 11]]
# 形状: (6, 2) -- NumPyが6行を計算
 
# 1Dにフラット化
print(arr.reshape(-1))
# [ 0  1  2  3  4  5  6  7  8  9 10 11]
# 形状: (12,)
 
# 列ベクトル
print(arr.reshape(-1, 1).shape)
# (12, 1)

-1は1つの次元にしか使用できません。2つ以上の次元に使用すると、計算が曖昧になるためエラーが発生します。

import numpy as np
 
arr = np.arange(12)
 
# これは失敗します:
# arr.reshape(-1, -1)
# ValueError: can only specify one unknown dimension

reshape()とresize()の違い:主な相違点

どちらの関数も配列の形状を変更しますが、動作は大きく異なります。

特徴reshape()resize()
戻り値新しい配列(ビューまたはコピー)配列をインプレースで変更(メソッド)または新しい配列を返す(関数)
サイズの一致が必要はい -- 要素の総数が同じでなければならないいいえ -- ゼロで埋めるか切り詰める
元の配列変更なし変更される(ndarray.resize()使用時)
安全性サイズ不一致でエラーを発生黙って埋めるか切り詰める
一般的な用途次元の再配置配列サイズの変更
import numpy as np
 
arr = np.array([1, 2, 3, 4, 5, 6])
 
# reshape:同じ要素総数を維持する必要がある
reshaped = arr.reshape(2, 3)
print(reshaped)
# [[1 2 3]
#  [4 5 6]]
 
# np.resize(関数形式):新しいサイズが大きい場合、繰り返しで埋める
resized = np.resize(arr, (3, 3))
print(resized)
# [[1 2 3]
#  [4 5 6]
#  [1 2 3]]   <-- 最初から繰り返し
 
# np.resize:新しいサイズが小さい場合、切り詰める
resized_small = np.resize(arr, (2, 2))
print(resized_small)
# [[1 2]
#  [3 4]]

データを変更せずに次元を再配置したい場合はreshape()を使用してください。要素数を本当に変更する必要がある場合にのみresize()を使用してください。

orderパラメータ:C vs F vs A

orderパラメータは、元の配列から要素がどのように読み取られ、新しい形状に配置されるかを制御します。異なるプログラミング言語やストレージフォーマットのデータを扱う際に重要です。

import numpy as np
 
arr = np.array([1, 2, 3, 4, 5, 6])
 
# C順序(行優先):行ごとに埋める -- これがデフォルト
c_order = arr.reshape(2, 3, order='C')
print("C順序:")
print(c_order)
# [[1 2 3]
#  [4 5 6]]
 
# F順序(列優先):列ごとに埋める
f_order = arr.reshape(2, 3, order='F')
print("F順序:")
print(f_order)
# [[1 3 5]
#  [2 4 6]]
Order名前埋め方ユースケース
'C'Cスタイル / 行優先行ごと(最後のインデックスが最も速く変化)Python/C/C++のデフォルトメモリレイアウト
'F'Fortranスタイル / 列優先列ごと(最初のインデックスが最も速く変化)MATLAB、Fortran、Rのデータ
'A'自動配列がFortran連続ならF、そうでなければC既存のメモリレイアウトを維持

ほとんどの場合、デフォルトの'C'順序を使用します。MATLABやFortranからエクスポートされたデータを読み込む際に'F'順序が必要です。これらの環境では行列が列ごとに格納されています。

ビューとコピー:reshapeがビューを返す場合

重要な詳細:reshape()は可能な限り元の配列のビューを返します。ビューは同じ基礎メモリを共有します。ビューを変更すると元の配列も変更され、その逆も同様です。

import numpy as np
 
original = np.arange(6)
reshaped = original.reshape(2, 3)
 
# reshape後のビューを変更
reshaped[0, 0] = 999
 
print(original)
# [999   1   2   3   4   5]   <-- 元の配列も変更された!

NumPyがビューを作成できない場合(例えば、メモリレイアウトが新しい形状と互換性がない場合)、代わりにコピーを返します。明示的にコピーを強制できます:

import numpy as np
 
original = np.arange(6)
reshaped_copy = original.reshape(2, 3).copy()
 
reshaped_copy[0, 0] = 999
print(original)
# [0 1 2 3 4 5]   <-- 元の配列は影響を受けない

reshapeがビューとコピーのどちらを返したかを確認するには、base属性を調べます:

import numpy as np
 
arr = np.arange(6)
view = arr.reshape(2, 3)
copy = arr.reshape(2, 3).copy()
 
print(view.base is arr)   # True -- ビューです
print(copy.base is arr)   # False -- コピーです

よくあるエラーとその修正方法

エラー:"cannot reshape array of size X into shape Y"

これは最もよくあるreshapeエラーです。要素の総数が目標の形状と一致しない場合に発生します。

import numpy as np
 
arr = np.arange(10)
 
# 10 != 3 * 4 = 12 なので失敗
# arr.reshape(3, 4)
# ValueError: cannot reshape array of size 10 into shape (3,4)

修正方法:

  1. 計算を確認してください。 すべての目標次元の積がarr.sizeと等しいことを確認します。
import numpy as np
 
arr = np.arange(10)
print(arr.size)  # 10
 
# 10要素の有効な形状: (2,5), (5,2), (1,10), (10,1), (10,)
print(arr.reshape(2, 5))
# [[0 1 2 3 4]
#  [5 6 7 8 9]]
  1. 配列をパディングまたはトリミングしてください。 均等に割り切れない形状が本当に必要な場合です。
import numpy as np
 
arr = np.arange(10)
 
# 12要素にパディングしてから(3, 4)にreshape
padded = np.pad(arr, (0, 2), constant_values=0)
print(padded.reshape(3, 4))
# [[0 1 2 3]
#  [4 5 6 7]
#  [8 9 0 0]]
  1. -1を使用して NumPyに1つの次元を計算させ、手動計算のエラーを避けてください。
import numpy as np
 
arr = np.arange(10)
print(arr.reshape(-1, 2))
# [[0 1]
#  [2 3]
#  [4 5]
#  [6 7]
#  [8 9]]

エラー:"Expected 2D array, got 1D array instead"

このscikit-learnエラーは、1Dの特徴量配列を渡した場合に表示されます。修正は簡単です:

import numpy as np
 
features = np.array([1.5, 2.3, 3.1, 4.7, 5.0])
 
# 列ベクトルに変換
features_2d = features.reshape(-1, 1)
print(features_2d.shape)
# (5, 1)

実践的な例

Scikit-Learn用のデータ準備

scikit-learnは(n_samples, n_features)の形状の特徴量行列を期待します。単純な線形回帰のためにデータを正しくreshapeする方法は以下の通りです:

import numpy as np
 
# 単一の特徴量:住宅の面積(平方フィート)
sizes = np.array([850, 1200, 1500, 1800, 2100, 2400, 2800])
 
# sklearn用に(n_samples, 1)にreshape
X = sizes.reshape(-1, 1)
print(X.shape)
# (7, 1)
 
# 目的変数(価格)は既に1Dで、sklearnはこれを受け入れる
y = np.array([150000, 220000, 275000, 320000, 380000, 430000, 510000])
 
# Xとyはsklearnのための準備が整いました
# from sklearn.linear_model import LinearRegression
# model = LinearRegression().fit(X, y)

画像データのreshape

画像データセットの操作では、フラットベクトルと空間次元の間のreshapeが頻繁に必要です:

import numpy as np
 
# 28x28グレースケール画像をフラット配列としてロードするシミュレーション(MNISTのように)
flat_image = np.random.randint(0, 256, size=784)
print(flat_image.shape)
# (784,)
 
# 2D画像にreshape
image_2d = flat_image.reshape(28, 28)
print(image_2d.shape)
# (28, 28)
 
# ニューラルネットワーク入力用に100枚の画像のバッチをreshape
batch_flat = np.random.randint(0, 256, size=(100, 784))
batch_images = batch_flat.reshape(100, 28, 28, 1)  # チャンネル次元を追加
print(batch_images.shape)
# (100, 28, 28, 1)

reshapeによる転置(そしてすべきでない理由)

よくある間違いは、reshapeを使って行列を「転置」しようとすることです。reshapeはメモリ順序で要素を並べ替えますが、これは転置とは異なります。

import numpy as np
 
matrix = np.array([[1, 2, 3],
                   [4, 5, 6]])
 
# 間違い:reshape(3, 2)は転置しない
wrong = matrix.reshape(3, 2)
print("reshape(3,2):")
print(wrong)
# [[1 2]
#  [3 4]
#  [5 6]]
 
# 正しい:.Tまたはnp.transpose()を使用
correct = matrix.T
print("転置:")
print(correct)
# [[1 4]
#  [2 5]
#  [3 6]]

RunCellでNumPy Reshapeを使う

配列を頻繁にreshapeし、より速いワークフローが必要な場合、RunCell (opens in a new tab)はJupyterノートブック内で動作するAIエージェントです。NumPy、pandas、scikit-learnのコードを毎日書くデータサイエンティスト向けに構築されています。

RunCellはreshape操作をいくつかの方法で支援できます:

  • 形状デバッグの即時化。 形状の不一致エラーが発生した場合、RunCellはトレースバックを読み取り、正しい次元とorderパラメータを含む正しいreshape呼び出しを提案します。
  • ボイラープレートの自動生成。 必要なものを自然言語で説明してください -- 「このフラット配列を3チャンネルの32x32画像のバッチにreshapeして」 -- するとRunCellがコードを書きます。
  • 代替手段の探索。 RunCellは、あなたの具体的な状況に応じて、reshapenp.expand_dimsnp.newaxisnp.squeezeのどれを使うべきかを示してくれます。

RunCellはJupyter環境内で直接動作するため、データとコードの完全なコントロールを維持しながら、必要な場所でAIアシストの提案を受けることができます。

FAQ

numpy reshape(-1)は何をしますか?

array.reshape(-1)を呼び出すと、配列を1次元にフラット化します。-1はNumPyに要素の総数を自動的に計算するよう指示します。array.ravel()array.flatten()と同等ですが、reshape(-1)は可能な場合にビューを返し、flatten()は常にコピーを返します。

numpy reshapeは元の配列を変更しますか?

いいえ。reshape()は新しい配列オブジェクトを返します。ただし、返された配列がビュー(通常そうです)の場合、reshape後の配列を変更すると、同じメモリを共有しているため元の配列も変更されます。これを避けるには、結果に.copy()を呼び出してください。

reshapeとflattenの違いは何ですか?

reshape()は配列を任意の互換性のある形状に変換できます。flatten()は常に1D配列に変換し、常にコピーを返します。ravel()も1Dに変換しますが、reshape(-1)と同様に可能な場合はビューを返します。

pandas DataFrameをnumpy reshapeでreshapeできますか?

直接はできません。まずdf.valuesまたはdf.to_numpy()を使って基礎となるNumPy配列を抽出し、その後reshapeする必要があります。DataFrame固有のreshape操作には、pivot()melt()stack()unstack()などのpandasメソッドを使用してください。

新しい軸を追加するために配列をreshapeするには?

reshape()を使用できますが、単一の次元を追加する場合はnp.expand_dims()np.newaxisの方がきれいです:

import numpy as np
 
arr = np.array([1, 2, 3])
 
# reshapeを使用
print(arr.reshape(1, 3).shape)   # (1, 3)
print(arr.reshape(3, 1).shape)   # (3, 1)
 
# np.newaxisを使用(より読みやすい)
print(arr[np.newaxis, :].shape)  # (1, 3)
print(arr[:, np.newaxis].shape)  # (3, 1)

まとめ

numpy.reshape()はNumPyライブラリで最も頻繁に使用される関数の1つです。データをコピーせずに(ほとんどの場合)1D、2D、3D、およびそれ以上の次元の配列間で変換できます。覚えておくべきポイント:

  • 要素の総数はreshape前後で同じでなければならない。
  • 1つの次元に-1を使用してNumPyに自動計算させる。
  • reshape()は通常コピーではなくビューを返す。ビューへの変更は元の配列に影響する。
  • デフォルトのorder='C'は行ごとに要素を埋める。MATLABやFortranの列優先データにはorder='F'を使用する。
  • scikit-learnが1D配列について警告した場合、.reshape(-1, 1)が解決策。

これらのパターンをマスターすれば、機械学習用の表形式データの準備やディープラーニング用の画像テンソルのreshapeなど、形状の不一致を自信を持って処理できるようになります。

📚