形状演算
形状演算
1. 学習内容
- ❶ reshape はデータをコピーせず、メタデータのみを変更します
- ❷
-1次元サイズを自動推定 - ❸ flatten 対 ravel:コピー 対 ビュー
- ❹ 転置 / .T 転置
- ❺ 寸法を追加するには「newaxis」、削除するには「squeeze」
2. ある開発者の実話
(1) 問題点
ボブは、3×4 の 12 個のデータポイントを 4×3 に変形させ、「メモリの再配置」をしているつもりだったが、その変形処理のたびにデータがコピーされていたため、メモリと処理速度が著しく低下してしまった。
(2) 解決策
アリスは彼にMermaidの図を見せながら、「reshapeはメタデータのみを変更するもので、データは1バイトたりとも移動しない。それがNumPyの高速化の秘訣よ」と説明した。演算の結果がビューである限り、データはコピーされない。
(3) 成果
ボブが「ビューとコピー」の違いを理解すると、彼の形状操作コードはメモリ使用量を1桁減らし、実行速度も数倍向上した。フラット化のみが真のコピーであり、それ以外(リシェイプ、ラヴェル、トランスポーズ、スワップアクスなど)はすべてビューである。
3. 形状演算の詳細
(1) reshape の仕組み
reshape は配列の形状を変更しますが、データをコピーすることはありません。これは配列のメタデータ(形状とストライド)のみを変更するものであり、メモリ内の生データはそのまま残ります。
import numpy as np
a = np.arange(12)
b = a.reshape(3, 4)
print(a.shape) # (12,)
print(b.shape) # (3, 4)
print(b.base is a) # True — b is a view of a
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
# reshape does NOT copy data
b[0, 0] = 999
print(a[0]) # 999 — modifying b changes a!
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
メモリダイアグラムの再構成
graph LR
A["Original Array<br/>shape=(12,)"] -->|"reshape(3,4)"| B["View<br/>shape=(3,4)"]
A -->|"reshape(4,3)"| C["View<br/>shape=(4,3)"]
A -->|"reshape(2,6)"| D["View<br/>shape=(2,6)"]
style A fill:#4CAF50,color:#fff
style B fill:#2196F3,color:#fff
style C fill:#FF9800,color:#fff
style D fill:#9C27B0,color:#fff
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
要点:A、B、C、Dはすべて同じメモリを共有しており、単にその「解釈」の仕方が異なるだけである。
(2) -1 自動推論
reshape 内で -1 を使用すると、NumPy がその次元のサイズを自動的に計算するようになります。
a = np.arange(12)
# NumPy infers: 12 / 3 = 4
b = a.reshape(3, -1) # shape = (3, 4)
c = a.reshape(2, -1) # shape = (2, 6)
d = a.reshape(-1, 6) # shape = (2, 6)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(3) フラット化とラヴェル
| メソッド | 戻り値 | コピーする? | メモリ |
|---|---|---|---|
ndarray.flatten() |
1次元配列 | 常にコピーされる | 新しいメモリ |
ndarray.ravel() |
1次元配列 | 可能であれば参照 | 通常は共有 |
import numpy as np
a = np.arange(12).reshape(3, 4)
f = a.flatten() # always a copy
r = a.ravel() # usually a view
f[0] = 999
print(a[0, 0]) # 0 — f is a copy, a unchanged
r[0] = 999
print(a[0, 0]) # 999 — r is a view, a changed
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(4) 転置と .T
import numpy as np
a = np.arange(12).reshape(3, 4)
print(a.shape) # (3, 4)
b = a.T # transpose — view, no copy
print(b.shape) # (4, 3)
# For multi-dimensional arrays, transpose accepts an axis order
c = a.transpose(1, 0)
print(c.shape) # (4, 3)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(5) newaxis と squeeze
import numpy as np
a = np.array([1, 2, 3]) # shape (3,)
# newaxis adds a dimension
b = a[np.newaxis, :] # shape (1, 3)
c = a[:, np.newaxis] # shape (3, 1)
d = a[np.newaxis, :, np.newaxis] # shape (1, 3, 1)
# squeeze removes dimensions of size 1
e = np.squeeze(d) # shape (3,) — back to original
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(1) ▶ サンプル
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: -1 による自動推論を用いた再構成 (難易度 ⭐)
import numpy as np
a = np.arange(24)
# Use -1 for auto-inference
b = a.reshape(2, -1) # 2 x 12
c = a.reshape(3, -1) # 3 x 8
d = a.reshape(4, -1) # 4 x 6
e = a.reshape(6, -1) # 6 x 4
print(f"a.shape={a.shape}")
print(f"b.shape={b.shape}") # (2, 12)
print(f"c.shape={c.shape}") # (3, 8)
print(f"d.shape={d.shape}") # (4, 6)
print(f"e.shape={e.shape}") # (6, 4)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(2) ▶ サンプル
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: flatten 対 ravel — copy 対 view (難易度 ⭐⭐)
import numpy as np
a = np.arange(12).reshape(3, 4)
# flatten always copies
flat = a.flatten()
flat[0] = 999
print("After flat[0]=999, a[0,0]=", a[0,0]) # 0 (unchanged)
# ravel usually returns a view
rav = a.ravel()
rav[0] = 999
print("After rav[0]=999, a[0,0]=", a[0,0]) # 999 (changed!)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
(3) ▶ サンプル
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
: transpose と newaxis (難易度 ⭐⭐)
import numpy as np
a = np.arange(12).reshape(3, 4)
# Transpose
print("Original shape:", a.shape) # (3, 4)
print("Transposed shape:", a.T.shape) # (4, 3)
# newaxis: add dimensions
row = np.array([1, 2, 3])
print("row shape:", row.shape) # (3,)
print("row[np.newaxis,:] shape:", row[np.newaxis, :].shape) # (1, 3)
print("row[:,np.newaxis] shape:", row[:, np.newaxis].shape) # (3, 1)
> Output: Run NumPy 2.x in your local Python environment to see the ndarray output. The Piston server does not have NumPy pre-installed — install it locally (`pip install numpy`) and compare. Actual values may vary by NumPy version and random seed.
4. 比較表
(1) 形状演算の概要
| 操作 | 戻り値 | データをコピーするか? | ユースケース |
|---|---|---|---|
reshape |
表示 | なし | 形状を変更 |
flatten |
コピー | はい | 安全な1D変換 |
ravel |
表示(通常) | まれ | 高速 1D |
T / transpose |
表示 | なし | 軸の順序変更 |
newaxis |
表示 | なし | 寸法を追加 |
squeeze |
表示 | なし | サイズが1小さい次元を除外 |
swapaxes |
表示 | なし | 2つの軸を入れ替える |
(2) 表示とコピー
| 機能 | 表示 | コピー |
|---|---|---|
| メモリ | 共有 | 新規割り当て |
| 変更 | オリジナルへの影響 | 独立 |
| 速度 | O(1) | O(n) |
base 属性 |
出典へのリンク | なし |
| 使用すべき場面 | 読み取り専用または意図的な共有 | 安全な変更 |
5. 原理図
graph TB
A["Original (12,)"] -->|reshape/flatten/ravel| B["New shape"]
A --> C["data buffer: [0 1 2 ... 11]"]
B --> C
B -->|flatten| D["copy of data buffer"]
B -->|ravel| C
B -->|transpose| C
B -->|newaxis| C
style A fill:#4CAF50,color:#fff
style C fill:#FF9800,color:#fff
style D fill:#E91E63,color:#fff
❓ よくある質問
np.ascontiguousarray() を使用してください。.T は、すべての軸を反転させるショートカットです(順序を逆にした .transpose() と同等です)。 2次元配列の場合、これらは同じ動作をします。3次元以上の配列の場合、.Tはすべての軸を反転させますが、.transpose(1,0,2)では順序を指定できます。total_size / product_of_other_dims として計算します。📖 まとめ
- reshape は形状およびストライドのメタデータのみを変更します。データのコピーは行われず、O(1) の操作です。
-1要素の総数から次元のサイズを自動で推定するflattenは常にコピーを行います。一方、ravelは通常、ビューを返します(.baseを参照)。- transpose/T/swapaxes は、ストライドを変更したビューを返し、データの移動は行わない
- newaxis はサイズ 1 の次元を追加し、squeeze はサイズ 1 の次元を削除します
- 「view」と「copy」の違いを理解することは、メモリ効率の良いNumPyコードを書くための鍵となります
📝 練習問題
-
初心者(難易度 ⭐):20要素の配列を作成し、
-1を使用して、それを (4,5)、(5,4)、(2,10)、(10,2) の形に再構成してください。 すべての形状が正しいことを確認してください。 -
中級(難易度 ⭐⭐):3×4の配列を作成し、それを転置した後、転置した結果をreshapeで再成形してみてください。reshapeを使用しても、コピーは回避できるでしょうか?
.baseに確認してみてください。 -
上級(難易度 ⭐⭐⭐):形状が (2,3,4) の3次元配列を作成してください。
transpose(2,0,1)を使用して軸の順序を変更してください。結果において各次元が何を表しているかを説明してください。その後、swapaxesを使用して同じ結果を得てください。