NumPy は Python での数値計算の土台になるライブラリです。pandas / Polars / matplotlib / scikit-learn など、本サイトで扱うほとんどのライブラリが NumPy の配列(ndarray)を内部で使っています。

本記事では、NumPy の中心オブジェクトである ndarray を、株価風のサンプルデータで作成・操作・集計しながら、配列で考える発想を身につけます。

目次

  1. インストール
  2. NumPy が解く問題
  3. 配列を作る
  4. 形状を扱う(reshape / axis)
  5. ベクトル化された演算
  6. ブロードキャスト
  7. 真偽値配列でフィルタする
  8. 統計関数のまとめ

インストール

Terminal window
pip install numpy

検証バージョン: Python 3.12.5 / NumPy 2.0.1

NumPy が解く問題

Python の標準リストは柔軟ですが、数千〜数百万件の数値計算には向いていません。値ごとに型情報を持つ汎用オブジェクトのため、要素ごとの計算がループで回り、速度・メモリの両面で重くなります。

NumPy の ndarray は、同一型の値が連続したメモリに並ぶ「型付きの配列」です。要素ごとの演算は内部で C 実装のループに落ちるため、Python の for 文より 1〜2 桁高速になります。

配列を作る

リストから配列を作るのが最も基本的な方法です。

import numpy as np
closes = np.array([2900, 2925, 2880, 2910, 2945])
print(closes)
print(closes.dtype, closes.shape)
[2900 2925 2880 2910 2945]
int64 (5,)

dtype は要素の型、shape は各次元の長さを示すタプルです。1 次元配列なので (5,) となります。

専用の生成関数もよく使います。

zeros = np.zeros(5) # [0. 0. 0. 0. 0.]
ones = np.ones((2, 3)) # 2 行 3 列の 1 で埋めた行列
seq = np.arange(0, 10, 2) # [0 2 4 6 8]
linspace = np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1.]
rng = np.random.default_rng(42)
random_values = rng.normal(loc=0, scale=1, size=1000) # 正規分布から 1000 件

乱数は default_rng 経由で生成します。np.random.rand の旧 API より再現性とスレッド安全性が改善されています。

形状を扱う(reshape / axis)

複数銘柄の価格表は 2 次元配列で表現できます。

# 行: 営業日, 列: 銘柄(7203, 9984, 8306)
prices = np.array([
[2900, 9800, 1450],
[2925, 9750, 1448],
[2880, 9900, 1462],
[2910, 9850, 1470],
[2945, 9820, 1480],
])
print(prices.shape) # (5, 3)

軸(axis)を指定すると「列ごと」「行ごと」の集計を切り替えられます。

print(prices.mean(axis=0)) # 銘柄ごとの平均(列方向)
print(prices.mean(axis=1)) # 日付ごとの平均(行方向)

axis=0 は「行を潰して 1 行にする」、axis=1 は「列を潰して 1 列にする」と覚えると迷いません。

reshape で形を変えられます。要素数が同じであれば、自由に並べ替えできます。

flat = prices.reshape(-1) # (15,)
mat = flat.reshape(3, 5) # (3, 5)

-1 は「残りの次元から自動で計算」の意味です。

NumPy 配列の形状と軸の概念図

ベクトル化された演算

NumPy の真価は、要素ごとの演算をループ無しで書けるところにあります。

# 単純リターン(前日比)を一括で計算
returns = (prices[1:] - prices[:-1]) / prices[:-1]
print(returns.round(4))

prices[1:] は 2 行目以降、prices[:-1] は最終行を除く全行です。差分を取って前日価格で割るだけで、全銘柄・全日付のリターン行列が得られます。

ブロードキャスト

形が異なる配列同士の演算を「自動で合わせる」仕組みがブロードキャストです。

mean_per_stock = prices.mean(axis=0) # shape (3,)
deviation = prices - mean_per_stock # shape (5, 3) と (3,) の演算
print(deviation.round(2))

(5, 3) の行列から (3,) のベクトルを引くと、ベクトルが各行に同じ形でコピーされて差し引かれます。明示的にループや np.tile を書く必要はありません。

ブロードキャストの基本ルールは次の通りです。

  • 末尾から軸を比較する
  • 各軸の長さが「同じ」または「片方が 1」なら合致
  • 合致しない軸があると ValueError

真偽値配列でフィルタする

条件式は要素ごとに True / False の配列を返します。

mask = closes > 2900
print(mask) # [False True False True True]
print(closes[mask]) # [2925 2910 2945]

複数条件は &(かつ)・|(または)で繋ぎ、各条件をカッコで囲むのが必須です。

big_moves = (closes > 2900) & (closes < 2940)
print(closes[big_moves])

統計関数のまとめ

代表的な集計を一覧で示します。

関数役割
arr.sum()合計
arr.mean()平均
arr.std(ddof=0)標準偏差(母集団)
arr.std(ddof=1)標準偏差(標本)
arr.min() / arr.max()最小・最大
arr.argmin() / arr.argmax()最小・最大の位置
np.median(arr)中央値
np.percentile(arr, [25, 50, 75])分位点

リターン列の代表値を求めると、銘柄ごとのボラティリティの違いが見えます。

daily_ret = (prices[1:] - prices[:-1]) / prices[:-1]
print("mean :", daily_ret.mean(axis=0))
print("std :", daily_ret.std(axis=0, ddof=1))
print("max :", daily_ret.max(axis=0))
print("min :", daily_ret.min(axis=0))

生成AI へのプロンプト例

NumPy のコードは仕様が短く、生成AI が安定して書ける領域です。

NumPy で次の処理を行うコードを書いてください。
入力:
- closes: shape (n_days, n_tickers) の終値行列
- weights: shape (n_tickers,) のポートフォリオ比率(合計 1)
要件:
- 日次の単純リターン行列を計算する
- ポートフォリオの日次リターンを計算する(銘柄ごとのリターン × 比率の総和)
- 累積リターン(初期 1 の元本が時系列でいくらになるか)を返す
- NumPy 2 系を想定し、ループは使わない

「入力の形」「処理の手順」「禁止事項(ループ禁止など)」を明示すると、ブロードキャストを使った簡潔なコードが返ってきます。

まとめ

  • ndarray は「同一型・連続メモリ」の数値配列で、要素ごとの演算が高速
  • 形(shape)と軸(axis)を意識すると、銘柄方向・日付方向の集計を書き分けられる
  • 形の異なる配列同士の演算は ブロードキャスト で自動的に合う
  • 真偽値配列でフィルタ・統計関数で集計までを、ループ無しで書ける