このページで扱う主要キーワード(クリックで該当セクションへ):
ndarray(多次元配列)と高速演算を提供する標準ライブラリ「NumPy」 (Numerical Python) は、 SSDSE-B-2026 などの公的統計データを使った教材・分析で頻出するキーワードです。 本ページでは、 まず直感、 次に数式、 そして 47 都道府県の実値で確かめる、 という流れで体系的に整理します。 加えて、 ケーススタディ・FAQ・歴史的経緯・参考文献までを 1 ページに集約し、 用語の「地図」として使えるようにしました。
関連用語(前提・並列・発展)と関連グループ教材も末尾にまとめてあるので、 用語の地図として活用してください。
NumPy(Numerical Python)は Python におけるベクトル・行列・テンソルの標準データ構造である ndarray と、 それに対する高速な数値演算を提供するライブラリです。 pandas は内部で NumPy を使い、 scikit-learn の入力も基本は NumPy 配列、 PyTorch/TensorFlow のテンソル設計も NumPy を参考にしています。
たとえば 47 都道府県の総人口を 1 本の ndarray に格納すれば、 「全国平均」「上位 10 県」「対数変換」「人口に占めるシェア」などの計算が すべて 1 行 で書けます。 これを純 Python の for ループでやると、 同じ処理に何倍もの行数と時間がかかります。
NumPy の威力は 「ベクトル化」 という思想にあります。 ループは内部の C 実装に任せ、 ユーザーは「配列同士の演算」として書く。 これが NumPy らしいコードです。
| 観点 | Python list | NumPy ndarray |
|---|---|---|
| 要素の型 | 異種混在 OK | 原則同一 dtype |
| メモリ配置 | 飛び石(参照配列) | 連続したバッファ |
| 演算子の意味 | [1,2]+[3,4] は連結 | np.array([1,2])+np.array([3,4]) は要素和 |
| 速度 | 遅い(Python オブジェクト操作) | 速い(C/BLAS 呼び出し) |
| 多次元 | list of list | 形状 (行, 列, ...) を直接サポート |
NumPy が裏で行っている要素ごとの演算(ufunc)は、 数式で書けば極めて単純です。 2 つの配列 $\mathbf{x}, \mathbf{y}\in\mathbb{R}^n$ に対して:
@ または np.dot)】これらはすべて C 実装と BLAS(線形代数の高速ライブラリ、 OpenBLAS/MKL/Accelerate)に委譲され、 内部で SIMD やマルチコア並列が走ります。
NumPy の主要概念を「言葉」で読み解きます。
| 概念 | 意味 | 例 |
|---|---|---|
ndarray | 同一 dtype・固定形状の連続メモリ配列 | a = np.array([1,2,3]) |
dtype | 要素のデータ型(int64, float64, bool, complex128) | a.dtype で確認 |
shape | 各軸の長さを示すタプル | 2 次元 (3,4) なら 3 行 4 列 |
axis | 軸の指定。 0=行方向、 1=列方向 | a.sum(axis=0) は列ごとの和 |
| ブロードキャスト | 形状の足りない軸を自動補完して演算 | 列ベクトル−行ベクトルで行列差 |
| ufunc | 要素ごとの汎関数(np.sin、np.exp) | 配列全体に一括適用 |
| view と copy | スライスはビュー(共有メモリ)、 fancy index は copy | 意図せぬ書き換えに注意 |
| strides | 軸方向に 1 要素進むときのバイト数 | 転置はストライドの入替で O(1) |
SSDSE-B-2026 の 2023 年 47 都道府県の 総人口(列 A1101)を NumPy で扱ってみます。 47 個の整数が並んだ 1 次元配列を作り、 基本統計量を直接計算します。
SSDSE-B-2026 から 2023 年の A1101 列を抽出すると、 おおむね次の数字が得られます(単位:人)。
北海道 5,092,000 / 青森 1,184,000 / 岩手 1,163,000 / 宮城 2,264,000 / 秋田 914,000 / 山形 1,026,000 / 福島 1,767,000 / ... / 東京 14,086,000 / ... / 沖縄 1,468,000
NumPy で扱うと:
このような統計量の計算が arr.sum() / arr.mean() / arr.std() / arr.max() / arr.min() の 1 行ずつで完了するのが NumPy の最大の魅力です。
data/raw/SSDSE-B-2026.csv (Shift-JIS、 ヘッダ 2 行)。 2023 年度に絞り、 「総人口」列を整数で to_numpy() したベクトル pop(shape=(47,)、 dtype=int64)。import pandas as pd
import numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis')
df.columns = df.iloc[0]
df = df.iloc[1:].reset_index(drop=True)
df = df[df['年度']=='2023'].reset_index(drop=True)
pop = df['総人口'].astype(int).to_numpy() # 47 都道府県の ndarray
print(pop.shape, pop.dtype)
print('合計:', pop.sum())
print('平均:', pop.mean())
print('標準偏差:', pop.std(ddof=1))
print('最大-最小:', pop.max() - pop.min())
print('上位 5 県:', np.sort(pop)[::-1][:5])
.sum() / .mean() / .std() / .max() という 1 メソッドで取れる。 「最大−最小 ≈ 1340 万人」は東京(約 1392 万)と鳥取県(約 55 万)の差を意味する。 上位 5 県だけで全国総人口の約 30 % を占め、 日本の人口集中度の高さがベクトル演算ひとつで露わになる。pop をスカラ(全国合計)で一度に割り、 各都道府県のシェア(%)を 1 行で計算する。 NumPy のブロードキャストが「ループ書かない」を可能にする実例。pop(shape=(47,))。 全国合計 pop.sum() はスカラ → ブロードキャストで自動的に 47 要素全てに割られる。share = pop / pop.sum() * 100 # 47 個の % を一度に算出
print('東京のシェア:', share.max(), '%')
print('小数点 2 桁で:', np.round(share, 2)[:5])
for ループで 47 回の割り算が必要だが、 NumPy では 1 行・C 実装でループしてくれるので可読性も速度も段違い。X(shape=(47, 4))に変換。cols = ['総人口', '15歳未満人口', '15〜64歳人口', '65歳以上人口']
X = df[cols].astype(int).to_numpy()
print(X.shape) # (47, 4)
print('列ごとの平均:', X.mean(axis=0)) # 4 つの平均
print('行ごとの和(人口の内訳合計):', X.sum(axis=1)[:5])
W(shape=(47,)、 和=1)と、 各県年齢別割合行列 A(shape=(47, 3)、 単位 %)。 SSDSE-B-2026 の 2023 年度を出典とする。W = pop / pop.sum() # 47 次元の重み
agecols = ['15歳未満人口', '15〜64歳人口', '65歳以上人口']
A = df[agecols].astype(int).to_numpy() / pop[:,None] * 100 # 各県の年齢別割合 (%)
nationwide = W @ A # 行列積(@)でひと息に
print('全国平均の年齢別割合:', nationwide)
@ は加重平均・ニューラルネット・線形回帰すべての土台。np.linalg.lstsq だけで「65 歳以上人口で総人口を予測する」単回帰を実行。 線形回帰の本質が「正規方程式の解 = 最小二乗解」であることを体感する。y=総人口、 説明変数 x=65 歳以上人口(共に SSDSE-B-2026 の 2023 年度・47 都道府県)。 切片付き計画行列 X は (47, 2)。y = df['総人口'].astype(int).to_numpy()
x = df['65歳以上人口'].astype(int).to_numpy()
X = np.column_stack([np.ones_like(x), x]) # (47,2) 行列
coef, *_ = np.linalg.lstsq(X, y, rcond=None)
print('切片 a, 傾き b:', coef)
y_hat = X @ coef
rmse = np.sqrt(((y - y_hat)**2).mean())
print('RMSE:', rmse)
LinearRegression().fit() と同じ係数が、 NumPy だけで 4 行で求まる。 アルゴリズムを理解する一歩。.corr() を呼ばず、 NumPy の行列演算だけで「標準化 → ZTZ / (n-1)」の手順を踏み、 5 変数の相関行列を構築する。 相関係数の定義式をコードで確認する。X → 標準化して Z(平均 0・分散 1)。import pandas as pd, numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis')
df.columns = df.iloc[0]
df = df.iloc[1:].reset_index(drop=True)
df = df[df['年度']=='2023'].reset_index(drop=True)
cols = ['総人口','15歳未満人口','15〜64歳人口','65歳以上人口','県内総生産']
X = df[cols].astype(float).to_numpy()
# 標準化
Z = (X - X.mean(axis=0)) / X.std(axis=0, ddof=1)
# 相関行列 = (1/(n-1)) Z^T Z
R = (Z.T @ Z) / (len(Z) - 1)
print(pd.DataFrame(R, index=cols, columns=cols).round(2))
if/elif/else ループ不要。pop。 閾値 5,000,000 と 1,500,000。 np.where はネストして 3 値以上の分岐に拡張できる。pop = df['総人口'].astype(int).to_numpy()
cls = np.where(pop > 5_000_000, '大規模',
np.where(pop > 1_500_000, '中規模', '小規模'))
print(pd.Series(cls).value_counts())
np.where は可読性が落ちるため、 4 値以上なら pd.cut や np.select が推奨。 ただし速度は np.where が最速。np.argsort で人口降順インデックスを取得し、 そのインデックス配列で pop と県名 Series を同時にスライス。 「並べ替えて先頭を取る」が 1 行で実現できる NumPy の真骨頂。pop ベクトル。 np.argsort(pop)[::-1][:5] で「人口が多い順の先頭 5 インデックス」を取得。idx = np.argsort(pop)[::-1][:5]
print('上位 5 県の人口:', pop[idx])
print('都道府県:', df['都道府県'].iloc[idx].tolist())
pop[idx] と df['都道府県'].iloc[idx] で「人口と県名」を完全に同期して取り出せるため、 ランキング表生成に重宝する。np.einsum('i,ij->j', ...) で「重みベクトル × 年齢構成行列」を 1 行に圧縮し、 全国の年齢構成比(人口加重)を算出。 添字 i を縮約、 j を残す。w(shape=(47,))、 年齢構成比 age(shape=(47, 3)、 SSDSE-B-2026 由来)。 結果は (3,) ベクトル。w = pop / pop.sum()
age = df[['15歳未満人口','15〜64歳人口','65歳以上人口']].astype(int).to_numpy() / pop[:,None]
result = np.einsum('i,ij->j', w, age)
print('全国の年齢構成(重み付き):', np.round(result*100, 2), '%')
w @ age と同じ結果だが、 einsum なら 3 階以上のテンソル(時間 × 県 × 年齢)にも自然に拡張できる。 DL モデルのアテンション計算でも頻出する記法。.sum()」と「純 Python for ループ」で比較し、 NumPy が桁違いに高速な理由(C 実装+SIMD ベクトル化)を体感する。np.arange(10_000_000, dtype=np.float64) (0〜9,999,999 の 1 次元配列、 80 MB)。import time
a = np.arange(10_000_000, dtype=np.float64)
t0 = time.time(); s = a.sum(); print('NumPy:', time.time()-t0, '秒', s)
t0 = time.time()
s2 = 0.0
for v in a.tolist():
s2 += v
print('Python loop:', time.time()-t0, '秒', s2)
cov = np.cov(Z.T)
eigvals, eigvecs = np.linalg.eigh(cov)
order = np.argsort(eigvals)[::-1]
print('固有値:', eigvals[order])
print('累積寄与率:', np.cumsum(eigvals[order]) / eigvals.sum())
ステップ 1:環境準備
pip install numpy pandas python -c "import numpy as np; print(np.__version__)"
ステップ 2:データを読み込み ndarray に変換
import pandas as pd, numpy as np
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis')
df.columns = df.iloc[0]
df = df.iloc[1:].reset_index(drop=True)
df = df[df['年度']=='2023'].reset_index(drop=True)
cols = ['総人口','15歳未満人口','15〜64歳人口','65歳以上人口','県内総生産']
X = df[cols].astype(float).to_numpy() # (47, 5) ndarray
ステップ 3:標準化と主成分分析の中身
Z = (X - X.mean(axis=0)) / X.std(axis=0, ddof=1)
cov = (Z.T @ Z) / (len(Z) - 1)
eigvals, eigvecs = np.linalg.eigh(cov)
order = np.argsort(eigvals)[::-1]
print('累積寄与率:', np.cumsum(eigvals[order])/eigvals.sum())
ステップ 4:第 1 主成分への射影
pc1 = Z @ eigvecs[:, order[0]]
ranking = np.argsort(pc1)[::-1]
for i in ranking[:10]:
print(df['都道府県'].iloc[i], round(pc1[i], 2))
ステップ 5:結果の解釈
第 1 主成分は「人口・経済規模」を表す総合指標。 上位は東京・神奈川・大阪・愛知、 下位は鳥取・島根・高知。 NumPy だけで PCA の中身まで実装できます。
JPEG/PNG 画像は基本的に (高さ, 幅, チャンネル) の 3 次元 ndarray として表現されます。 ピクセル値の正規化、 グレースケール変換、 畳み込み、 リサイズなど、 ほぼすべての画像操作が NumPy 配列上の数値計算として書けます。 OpenCV や Pillow もバックエンドで NumPy を使い、 ユーザに ndarray を返します。
import numpy as np
# 画像 → グレースケール(行列の重み付き和)
def to_gray(img):
weights = np.array([0.299, 0.587, 0.114])
return img @ weights # (H, W, 3) @ (3,) = (H, W)
1 秒間に 1000 サンプル取れる加速度センサー、 100 Hz の心拍計、 これらは長い 1 次元 ndarray。 移動平均、 FFT、 ピーク検出を NumPy + SciPy で書くと、 1 行ごとに研究級の処理が並びます。
scikit-learn の API は基本的に (n_samples, n_features) の 2 次元 ndarray を受けます。 SSDSE で言えば 47 行 × N 列の行列。 標準化・PCA・k-means・回帰すべて、 ここから始まります。
OHLCV(始値・高値・安値・終値・出来高)データ、 ボラティリティ、 リターン、 シャープ比、 GARCH、 すべて ndarray と pandas で扱う。 ベクトル化により、 数百万行のバックテストが 1 秒以下に。
緯度経度のメッシュデータ(標高、 気温、 衛星画像)は 2 次元 ndarray。 NumPy で「東京を中心とする 100km × 100km の標高ヒートマップ」を切り出す、 のような操作が標準。
BERT・GPT が出力する単語埋め込みは(語数, 768/1024)の 2 次元 ndarray。 コサイン類似度、 t-SNE、 UMAP もすべて NumPy 演算で完結。
波動方程式・拡散方程式・分子動力学のような偏微分方程式の数値解法は、 状態を ndarray で表し、 ufunc とスライスで時間発展を計算する。 結果は HDF5 へ。
ユーザを 2 群に分け、 各群のコンバージョン率を ndarray で表し、 ブートストラップで信頼区間を出す。 ベクトル化により 10 万回のリサンプルが数秒。
np.corrcoef で計算せよ。np.where で分類し、 各群の平均人口を出せ。| ライブラリ | 位置 | 強み | 弱み |
|---|---|---|---|
| NumPy | 基盤 | 標準・安定・高速 | GPU 不可、 ラベル無し |
| pandas | 表データ | ラベル付き、 時系列 | 大規模で遅い |
| SciPy | 科学計算 | 最適化・統計・信号 | NumPy 依存 |
| CuPy | GPU | NumPy 互換 + CUDA | NVIDIA GPU 必須 |
| Dask Array | 分散 | 大規模・遅延評価 | API 微妙に異なる |
| JAX | 自動微分 | 関数型 + XLA | 学習コスト |
| xarray | 多次元ラベル | 気象・衛星データ | 初学者向けでない |
| 用語 | 意味 |
|---|---|
| ndarray | 多次元配列。 NumPy の中心データ構造 |
| dtype | 要素型。 int64, float64, complex128 等 |
| shape | 各軸の長さ。 (47,5) なら 47 行 5 列 |
| axis | 集計の軸。 0=列方向、 1=行方向 |
| ufunc | 要素ごとの普遍関数。 sin, cos, exp 等 |
| ブロードキャスト | 形状の異なる配列同士の自動拡張ルール |
| view | メモリを共有するスライス結果 |
| copy | メモリを別に確保した複製 |
| stride | 次の要素までのバイト数 |
| vectorization | ループを配列演算で書き換えること |
| SIMD | 1 命令で複数データを処理する CPU 機能 |
| BLAS | Basic Linear Algebra Subprograms。 行列演算の標準 |
| レシピ | コード |
|---|---|
| 配列作成 — 0 / 1 / 連番 / 等差 | np.zeros(5); np.ones((3,4)); np.arange(0,10,2); np.linspace(0,1,11) |
| dtype を指定して作る | np.array([1,2,3], dtype=np.float32) |
| shape と reshape | a = np.arange(12); b = a.reshape(3,4); print(b.shape) |
| axis 集計 | X.sum(axis=0) # 列方向 X.mean(axis=1) # 行方向 |
| ブール索引 | a[a > 100] # 条件を満たす要素 a[(a>10) & (a<100)] |
| fancy index | a[[0,3,5]] # 任意位置 a[a.argsort()[::-1][:5]] # 上位5位 |
| ブロードキャスト | X = np.arange(12).reshape(3,4) mean_col = X.mean(axis=0) X_centered = X - mean_col # (3,4) - (4,) = (3,4) |
| ufunc | np.sin(a); np.log(a); np.exp(a); np.sqrt(a) |
| 行列積 (@) と内積 (dot) | A @ B; np.dot(a, b) |
| 逆行列・線形方程式 | np.linalg.inv(A); np.linalg.solve(A, b) |
| 最小二乗 | coef, *_ = np.linalg.lstsq(X, y, rcond=None) |
| 固有値分解 | vals, vecs = np.linalg.eigh(cov) |
| SVD | U, S, Vt = np.linalg.svd(X) |
| FFT | np.fft.fft(signal); np.fft.rfft(real_signal) |
| save/load | np.save('a.npy', a); a = np.load('a.npy') |
astype(int) は小数を切り捨てる。 想定外の型変換は精度バグの温床。a[1:3] はビュー、 a[[1,3]] はコピー。 ビューを書き換えると元配列も変わる。 安全側に倒すなら .copy() を明示。(47,) と (47,1) は別物。 後者は転置×ブロードキャストで (47,47) の行列を作る。 形状が爆発するとメモリを使い切る。np.random はグローバル状態を共有するため、 並列ジョブで結果が変わる。 np.random.default_rng(seed) でジェネレータを明示する(ただし教材ページでは合成乱数を使わず、 実データを推奨)。arr.mean() は NaN を含むと結果も NaN。 欠損対応は np.nanmean、 もしくは pandas 側で dropna してから ndarray 化する。ndarray が中心で「型が同じ・名前なし」、 pandas は DataFrame で「列ごとに型が違ってよい・行/列に名前がある」。 数値だけの行列演算は NumPy、 表形式データの操作は pandas、 と使い分けます。np.array([1,2,3]) と np.asarray([1,2,3]) の違いは?asarray はコピーせずそのまま返す、 array はデフォルトでコピーします。 関数の引数受けでは asarray が無駄なコピーを避けて好まれます。a[[0,2,5]] のような任意位置の取り出しは連続メモリにならないため、 新しいバッファにコピーする必要があるからです。dtype=np.float32 を明示すれば 8 → 4 バイトに半減します。 a.nbytes、 a.itemsize、 a.dtype で確認可能。 巨大配列は np.memmap でディスクマップも検討。歴史と位置づけ:NumPy のルーツは 1995 年の Numeric(Jim Hugunin)、 続いて 2001 年の Numarray、 そして 2005 年に Travis Oliphant がこれらを統合して NumPy 1.0 をリリース。 以後 20 年にわたり、 Python が「データサイエンスのデファクト言語」になる礎となりました。 2020 年には Nature 誌に「Array programming with NumPy」(Harris ら, 2020)が掲載され、 学術界に与えた影響の大きさが正式に承認されています。
主要バージョンの節目:
__array_ufunc__ プロトコル(拡張可能 ufunc)numpy.random.Generator 導入(新しい乱数 API、 再現性向上)NumPy はインターフェース面でも歴史的役割を担っており、 PyTorch・JAX・CuPy・Dask・xarray などのライブラリが 「NumPy 互換 API」を採用することで、 ユーザは同じ書き方で異なるバックエンド(GPU・分散)を切り替えられます。
NumPy を中心に「上下左右」に伸びる依存関係をツリーで表すと:
(機械学習・深層学習層)
scikit-learn PyTorch TensorFlow JAX
\ | | /
\ | | /
─── ndarray インターフェース ───
/ | | \
pandas xarray SciPy matplotlib
\ | | /
\ | | /
───── NumPy core (ndarray, ufunc) ─────
| | |
BLAS LAPACK SIMD
(C / Fortran 数値計算ライブラリ)
このように NumPy は ハードウェア寄りの BLAS と ユーザ寄りの pandas/sklearn の中間でデータを橋渡しする共通言語になっています。