NumPy: NumPy 入門
NumPyはPython科学計算の基盤です。機械学習、データ分析、工学シミュレーションのいずれを行うにしても、ほぼすべての数値計算ライブラリはNumPyの上に構築されています。この章では、Pythonリストのパフォーマンス上の問題点から始め、NumPyがどのように160倍の高速化を実現するのかを明らかにします。数値計算への第一歩です。
1. 学習内容
- ❶ Pythonリストが数値計算を苦手とする理由
- ❷ NumPyの主な利点(高速化・メモリ効率・ブロードキャスト)
- ❸ ndarrayとPythonリストの主な違い
- ❹ NumPyエコシステムの概要
- ❺ NumPyのインストールと最初のプログラムの実行
2. 100万件データでの高速化の旅
(1) 問題:アリスのリストループ
アリスはデータアナリストで、100万個の乱数を2乗する必要があります。彼女は最も「自然な」Pythonコード、つまりリストの要素を1つずつ処理するforループを書きました。
▶ サンプル
> **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.
: リストの二乗(難易度 ⭐)
import time
import random
size = 1_000_000 # 1 million numbers
data = [random.random() for _ in range(size)]
start = time.time()
result = [x * x for x in data]
elapsed = time.time() - start
print(f"List comprehension: {elapsed:.4f} seconds")
# Typical output: List comprehension: 0.8000 seconds
> **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.
アリスのコードは約0.8秒で実行されます。それほど悪くはないように思えますが、同じ操作を数億件のデータポイントに対して行う必要が生じると、待ち時間は耐え難いものになります。
(2) 解決策:ボブのNumPyベクトル化
ボブはたった2行を変更しただけです。リストをnp.arrayに置き換え、ループを単一のベクトル化された乗算に置き換えました。
▶ サンプル
> **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.
: NumPyでの二乗(難易度 ⭐⭐)
import time
import numpy as np
size = 1_000_000 # 1 million numbers
data = np.random.random(size)
start = time.time()
result = data * data # vectorized operation
elapsed = time.time() - start
print(f"NumPy vectorized: {elapsed:.4f} seconds")
# Typical output: NumPy vectorized: 0.0050 seconds
> **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.
同じ100万個の数値でも、NumPyではわずか約5ミリ秒で処理されます — およそ160倍高速です。
(3) その成果:160倍高速化の理由
NumPyの高速さは「Pythonが速くなった」からではありません — 重要なのは計算をCに委譲し、連続メモリを使用することです。
- 内部はC言語: NumPyの中核的な演算はC/Fortranで実装されており、Pythonインタプリタの行ごとのオーバーヘッドを回避します
- 連続メモリ: ndarrayの要素はメモリ上に密に配置されるため、CPUキャッシュのヒット率が高くなります
- ベクトル化: 1つの命令で配列全体を一度に処理するため、Pythonレベルのループは不要です
これがNumPyの核となる思想です — Pythonの使いやすさと、C言語の速度。
3. Pythonリストが数値計算を苦手とする理由
(1) ループのオーバーヘッド:インタプリタ実行
Pythonは動的型付け言語です。ループの各反復では、型チェック → メソッド検索 → 演算の実行 → 結果のボックス化という処理を通ります。数値計算において、これらの余分な手順は純粋な無駄です。
# Each iteration: type check + method lookup + boxing
result = []
for x in data: # Python loop overhead per iteration
result.append(x * x) # type check, method dispatch, result boxing
> **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.
コンパイル済みのCのマシンコードと比べると、Pythonのループ1回分のコストは、実際の乗算の数十倍になることがあります。
(2) 型のオーバーヘッド:各要素は完全なオブジェクト
Pythonリストは生の数値を格納するのではなく、PyObjectへのポインタを格納します。整数42はPythonでは28バイトを消費しますが、Cではわずか4バイトです。
4. NumPyの主な利点
(1) ndarrayとは
ndarray(N次元配列)はNumPyの中核となるデータ構造です。その特徴は次のとおりです。
- 均一性: すべての要素が同じ型を共有します(例:すべて
float64) - 固定サイズ: 要素サイズが固定されており、ボックス化は不要です
- 連続性: 要素がメモリ上に密に配置され、ポインタの間接参照はありません
- 多次元: 1次元、2次元、さらにはN次元のデータをサポートします
(2) ndarray と Pythonリストの比較
| 特徴 | Pythonリスト | NumPy ndarray |
|---|---|---|
| 要素の型 | 混在可能 | 均一(単一のdtype) |
| メモリ配置 | ポインタ配列、要素が分散 | 連続したメモリブロック |
| 単一整数のメモリ | 28バイト(PyObject) | 8バイト(int64) |
| 一括演算 | リスト内包表記 / forループ | ベクトル化、単一の式 |
| ブロードキャスト | 非対応 | 自動的な次元拡張 |
| スライス | コピーを返す | デフォルトでビューを返す(ゼロコピー) |
| 多次元 | ネストしたリスト(不規則) | ネイティブなN次元、shape属性 |
| 内部実装 | CPythonインタプリタ | C / Fortranのコンパイル済みコード |
▶ サンプル
> **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.
: メモリ使用量の比較(難易度 ⭐)
import sys
import numpy as np
size = 1_000_000 # 1 million integers
# Python list
py_list = list(range(size))
list_bytes = sys.getsizeof(py_list) + sum(sys.getsizeof(x) for x in py_list[:1000]) * size // 1000
print(f"Python list: ~{list_bytes / 1024 / 1024:.1f} MB")
# NumPy ndarray
np_array = np.arange(size, dtype=np.int64)
array_bytes = np_array.nbytes
print(f"NumPy ndarray: {array_bytes / 1024 / 1024:.1f} MB")
print(f"Ratio: {list_bytes / array_bytes:.1f}x more memory for list")
# Typical output:
# Python list: ~44.7 MB
# NumPy ndarray: 7.6 MB
# Ratio: 5.9x more memory for list
> **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.
単純なint64のndarrayは、同じサイズのPythonリストと比べて約6倍少ないメモリしか使用しません。ndarrayは完全なPythonオブジェクトではなく生の値を格納するためです。
(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.
: 配列演算とリスト内包表記の比較(難易度 ⭐⭐)
import numpy as np
# Task: add 100 to every element and multiply by 2
data = [1, 2, 3, 4, 5]
# --- Python list ---
result_list = [(x + 100) * 2 for x in data]
print(result_list)
# [202, 204, 206, 208, 210]
# --- NumPy ndarray ---
arr = np.array(data)
result_np = (arr + 100) * 2 # broadcasting + vectorization
print(result_np)
# [202 204 206 208 210]
# With a 2D array and 1D array
matrix = np.array([[1, 2, 3],
[4, 5, 6]])
row = np.array([10, 20, 30])
print(matrix + row)
# [[11 22 33]
# [14 25 36]]
> **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.
リスト内包表記ではループのロジックを明示的に書く必要がありますが、NumPyは1つの数式だけで済みます — コードがそのまま数式になります。
5. NumPyエコシステムの概要
(1) NumPyエコシステム
NumPyはPython科学計算エコシステム全体の基盤です。ほぼすべての主要ライブラリがこれに依存しています。
mindmap
root((NumPy Ecosystem))
Data Science
Pandas
Polars
Machine Learning
Scikit-learn
TensorFlow
PyTorch
Scientific Computing
SciPy
Matplotlib
SymPy
Deep Learning Frameworks
Keras
JAX
MXNet
Specialized Domains
scikit-image
NetworkX
AstroPy
> **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) エコシステム参照表
| ライブラリ | 分野 | NumPyに依存する理由 |
|---|---|---|
| Pandas | データ分析 | DataFrameは内部で列データをndarrayとして格納 |
| Scikit-learn | 機械学習 | モデルの入力と出力がndarray |
| SciPy | 科学計算 | ndarray上で線形代数・最適化・積分を提供 |
| Matplotlib | 可視化 | プロットのデータソースがndarray |
| TensorFlow | ディープラーニング | Tensorの概念はndarrayに由来し、相互変換可能 |
| PyTorch | ディープラーニング | Tensorとndarrayは相互運用可能 |
| Polars | データ分析 | 列指向ストレージだが、NumPy互換 |
(3) NumPyと純粋なPythonの使い分け
| シナリオ | 純粋なPython | NumPy | 推奨 |
|---|---|---|---|
| 数十回程度の単純な演算 | 十分 | わずかに速い | 純粋なPython |
| 1万件以上の一括演算 | 遅い | 10〜100倍速い | NumPy |
| 行列の乗算 | ネストループで非常に遅い | BLASベースで非常に速い | NumPy |
| 線形方程式の求解 | 手書きは非現実的 | np.linalg.solve |
NumPy |
| 文字列処理 | 柔軟 | 得意ではない | 純粋なPython |
| 型が混在するデータ | リストが自然に処理 | 構造化配列が必要 | 状況による |
6. インストールと最初のプログラム
(1) インストール方法
| 方法 | コマンド | 用途 |
|---|---|---|
| pip | pip install numpy |
一般的で最も簡単 |
| conda | conda install numpy |
Anaconda / Miniconda環境 |
| システムパッケージ | apt install python3-numpy |
Linuxへのシステム全体インストール |
| ソースから | python setup.py build |
カスタムBLASバックエンドが必要な場合 |
初心者は
pip install numpyを使用してください。Anacondaユーザーはconda install numpyを使用できます。
▶ サンプル
> **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.
: インストールの確認(難易度 ⭐)
# Install
pip install numpy
# Verify
python -c "import numpy as np; print(np.__version__)"
# Output (example): 2.1.0
> **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) 最初のndarrayを作成する
▶ サンプル
> **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.
: np.arrayでの配列作成(難易度 ⭐)
import numpy as np
# From a Python list
a = np.array([1, 2, 3, 4, 5])
print(type(a)) # <class 'numpy.ndarray'>
print(a.dtype) # int64
print(a.shape) # (5,)
# From nested list (2D)
b = np.array([[1, 2, 3],
[4, 5, 6]])
print(b.shape) # (2, 3)
print(b.ndim) # 2
# Specify dtype explicitly
c = np.array([1, 2, 3], dtype=np.float32)
print(c.dtype) # float32
> **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.
: バージョンと設定の確認(難易度 ⭐)
import numpy as np
print(f"NumPy version: {np.__version__}")
# Show build configuration (BLAS, LAPACK, etc.)
np.show_config()
# Output includes: BLAS, LAPACK backend info,
# which determines computation performance
> **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.
np.show_config()で表示されるBLAS/LAPACKバックエンドが、NumPyの行列演算の速度を左右します。OpenBLAS、MKL、その他のバックエンド間では、パフォーマンスに数倍の差が生じることがあります。
7. 実践:1000万件の数値の平均と標準偏差
1000万個の浮動小数点数の平均と標準偏差を計算します。これはデータ分析で最も一般的なタスクの1つです。純粋なPythonとNumPyの両方で実装し、コード量、速度、メモリを比較します。
▶ サンプル
> **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.
: 総合比較 — リスト vs NumPy(難易度 ⭐⭐⭐)
import time
import math
import random
import numpy as np
import sys
size = 10_000_000 # 10 million
# ========== Pure Python ==========
py_data = [random.random() for _ in range(size)]
t0 = time.time()
mean_py = sum(py_data) / len(py_data)
var_py = sum((x - mean_py) ** 2 for x in py_data) / len(py_data)
std_py = math.sqrt(var_py)
elapsed_py = time.time() - t0
# Memory estimate for list
mem_py = sys.getsizeof(py_data) + size * 24 # ~24 bytes per float object
# ========== NumPy ==========
np_data = np.random.random(size)
t0 = time.time()
mean_np = np.mean(np_data)
std_np = np.std(np_data)
elapsed_np = time.time() - t0
# Memory for ndarray
mem_np = np_data.nbytes
# ========== Results ==========
print(f"--- Pure Python ---")
print(f" Mean: {mean_py:.6f} Std: {std_py:.6f}")
print(f" Time: {elapsed_py:.3f}s")
print(f" Memory: ~{mem_py / 1024 / 1024:.0f} MB")
print(f"--- NumPy ---")
print(f" Mean: {mean_np:.6f} Std: {std_np:.6f}")
print(f" Time: {elapsed_np:.3f}s")
print(f" Memory: {mem_np / 1024 / 1024:.1f} MB")
print(f"--- Comparison ---")
print(f" Speedup: {elapsed_py / elapsed_np:.1f}x")
print(f" Memory saving: {mem_py / mem_np:.1f}x")
# Typical output:
# --- Pure Python ---
# Mean: 0.500032 Std: 0.288675
# Time: 3.200s
# Memory: ~305 MB
# --- NumPy ---
# Mean: 0.500032 Std: 0.288675
# Time: 0.030s
# Memory: 76.3 MB
# --- Comparison ---
# Speedup: 106.7x
# Memory saving: 4.0x
> **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.
| 指標 | 純粋なPython | NumPy | 比較 |
|---|---|---|---|
| コード行数(コアロジック) | 4行 | 2行 | NumPyは50%少ない |
| 実行時間 | 約3.2秒 | 約0.03秒 | 約100倍高速 |
| メモリ使用量 | 約305MB | 約76MB | 4分の1 |
結論:データが大きくなるほど、NumPyの真価が発揮されます。1000万件のデータポイントでは、NumPyは速度とメモリの両面で純粋なPythonを圧倒します。
❓ よくある質問
arrayモジュールは1次元の均一配列のみをサポートし、ブロードキャスト、線形代数、FFT機能はありません。NumPyのndarrayは多次元データ、ブロードキャスト、豊富な数学関数をサポートしており、まったく次元の異なる存在です。📖 まとめ
- Pythonリストには数値計算上の2つの問題点があります。ループ解釈のオーバーヘッドが大きいことと、要素あたりのメモリが大きい(完全なオブジェクト)ことです
- NumPyは、Cレベルの実装+連続メモリ+ベクトル化により、100万件のデータポイントで約160倍の高速化を実現します
- ndarrayは均一・固定サイズ・連続の多次元配列であり、Pythonリストと比べて約6分の1のメモリで済みます
- ブロードキャストにより、異なる形状の配列同士を直接演算できます — コードがそのまま数式になります
- NumPyはPython科学計算エコシステムの基盤です。Pandas、Scikit-learn、SciPy、TensorFlowなど、多くのライブラリがこれに依存しています
pip install numpyでインストールし、np.array()で配列を作成し、np.show_config()で設定を確認します
📝 練習問題
-
初心者(難易度 ⭐):NumPyをインストールし、
import numpy as np; print(np.__version__)を実行してください。バージョンが1.24以上であることを確認し、出力を記録してください。 -
中級(難易度 ⭐⭐):それぞれ500万個の乱数を含むリストとndarrayを作成してください。リスト内包表記とNumPyのベクトル化を使い、各数値の2乗に10を加えた値を計算してください。
time.time()で両方の実行時間を計測して比較し、速度比を記録してください。 -
上級(難易度 ⭐⭐⭐):
np.show_config()を実行し、使用しているNumPyのBLASバックエンド(OpenBLAS / MKL / その他)を特定してください。このバックエンドが行列演算のパフォーマンスにどのように影響するかを調査し、簡単な説明を書いてください。