- 技術 💻
NumPyの使い方 基本一覧|配列の作成・演算・形状変更
NumPy(ナンパイ)とは、同じ型の数値をまとめて持つ「配列(ndarray)」と、その配列をループなしで一括計算する関数を提供するPythonのライブラリです。 pandas、matplotlib、scikit-learn など多くのライブラリの土台になっていて、データ分析や数値計算の最初の一歩として学ぶ人が多い道具です。
この記事では、配列の作成から乱数まで、基本操作を一覧表にまとめ、すべての出力を載せました。実行環境は Python 3.11.15 と NumPy 2.4.4 です(2026年10月時点の PyPI の最新は 2.4.6)。NumPy 2.x で変わった型昇格(NEP 50)や削除された np.float_ などの名前を実際に動かして確認し、shape が合わないエラーの直し方、リストとの速度比較、ビューとコピーの違いまで扱います。
pip install numpy
import numpy as np # np という別名で読み込むのが慣例
print(np.__version__)
2.4.4
NumPyの早見表
| やりたいこと | コード | 結果 |
|---|---|---|
| リストから配列を作る | np.array([1, 2, 3]) |
array([1, 2, 3]) |
| 連番を作る | np.arange(2, 10, 2) |
array([2, 4, 6, 8]) |
| 等間隔に区切る | np.linspace(0, 1, 5) |
array([0., 0.25, 0.5, 0.75, 1.]) |
| 0で埋めた配列 | np.zeros((2, 3)) |
2行3列の 0. |
| 形・次元・要素数・型を見る | a.shape, a.ndim, a.size, a.dtype |
(3, 4), 2, 12, int64 |
| 型を変える | a.astype(float) |
新しい配列が返る |
| 1要素を取る | a[1, 2] |
np.int64(6) |
| 列を取る | a[:, 0] |
array([0, 4, 8]) |
| 条件で取り出す | a[a > 6] |
array([ 7, 8, 9, 10, 11]) |
| 条件で置き換える | np.where(a > 6, a, -1) |
条件外が -1 |
| 形を変える | r.reshape(2, 3) |
2行3列 |
| 1次元に戻す | a.ravel() |
1次元配列 |
| 縦に結合/横に結合 | np.concatenate([x, y])/axis=1 |
行が増える/列が増える |
| 列ごとの合計 | a.sum(axis=0) |
array([12, 15, 18, 21]) |
| 行ごとの合計 | a.sum(axis=1) |
array([ 6, 22, 38]) |
| 要素ごとの計算 | v + w、v * 10 |
ループなしで一括 |
| 行列の積 | x @ y |
* は要素ごとの積 |
| 乱数 | rng = np.random.default_rng(42) |
rng.integers(1, 7, size=5) |
| コピーを作る | a.copy() |
元と独立した配列 |
表の a は np.arange(12).reshape(3, 4)、r は np.arange(6) です。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「NumPyの使い方 基本一覧|配列の作成・演算・形状変更」
配列を作る(作成関数の一覧)
| 関数 | 用途 | 例 |
|---|---|---|
np.array(リスト) |
リストやタプルから作る | np.array([[1, 2, 3], [4, 5, 6]]) |
np.arange(開始, 終了, 刻み) |
連番(終了は含まない) | np.arange(5) |
np.linspace(開始, 終了, 個数) |
等間隔(終了を含む) | np.linspace(0, 1, 5) |
np.zeros(形) |
0 で埋める | np.zeros((2, 3)) |
np.ones(形) |
1 で埋める | np.ones((2, 2), dtype=int) |
np.full(形, 値) |
指定の値で埋める | np.full((2, 3), 7) |
np.eye(n) |
単位行列 | np.eye(3) |
np.zeros_like(a) |
a と同じ形・型で 0 埋め |
np.zeros_like(a) |
np.empty(形) |
初期化せず確保(中身は不定) | np.empty((2, 2)) |
import numpy as np
print(repr(np.array([1, 2, 3])))
print(repr(np.array([[1, 2, 3], [4, 5, 6]])))
print(repr(np.arange(5)))
print(repr(np.arange(2, 10, 2)))
print(repr(np.arange(0, 1, 0.25)))
print(repr(np.linspace(0, 1, 5)))
print(repr(np.zeros(3)))
print(repr(np.ones((2, 2), dtype=int)))
print(repr(np.full((2, 3), 7)))
print(repr(np.eye(3)))
array([1, 2, 3])
array([[1, 2, 3],
[4, 5, 6]])
array([0, 1, 2, 3, 4])
array([2, 4, 6, 8])
array([0. , 0.25, 0.5 , 0.75])
array([0. , 0.25, 0.5 , 0.75, 1. ])
array([0., 0., 0.])
array([[1, 1],
[1, 1]])
array([[7, 7, 7],
[7, 7, 7]])
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
arange は終了の値を含まず、linspace は含みます。 小数の刻みで個数をきっちり決めたいときは linspace が向いています。また、zeros/ones/eye は既定で小数(float64)になるので、整数がほしいときは dtype=int を付けます。
形(shape)は、2次元以上のときタプルで渡します。np.zeros(2, 3) ではなく np.zeros((2, 3)) です。
行の長さがそろっていないリストを渡すとどうなるか
np.array([[1, 2], [3]])
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (2,) + inhomogeneous part.
NumPy の配列は「どの行も同じ長さ」が前提です。行の長さをそろえてから渡すと解決します。
dtype(データ型)を確認・変換する
NumPy の配列は、全要素が同じ型(dtype)を持ちます。 これが、リストより速くメモリも少なく済む理由です。
print(np.array([1, 2]).dtype) # 整数だけ
print(np.array([1, 2.5]).dtype) # 整数と小数が混ざる
print(np.array(["a", "bcd"]).dtype) # 文字列
print(np.array([True, False]).dtype) # 真偽値
print(repr(np.array([1, 2, 3], dtype=np.float32))) # 型を指定して作る
print(repr(np.array([1.9, -1.9]).astype(int))) # 小数→整数
print(repr(np.array(["1", "2"]).astype(int))) # 文字列→整数
int64
float64
<U3
bool
array([1., 2., 3.], dtype=float32)
array([ 1, -1])
array([1, 2])
| ポイント | 内容 |
|---|---|
| 混ざったときの型 | 整数と小数が混ざると、全体が float64 になる |
<U3 |
最大3文字の Unicode 文字列という意味 |
astype(int) |
小数点以下を切り捨てる(1.9 → 1、-1.9 → -1)。四捨五入したいときは先に np.round |
astype の戻り値 |
新しい配列。元の配列は変わらない |
| 既定の整数型 | NumPy 2.0 以降は 64bit の Windows でも int64(公式リリースノートで確認) |
よく使う型は次のとおりです。
| dtype | 中身 | 1要素のサイズ |
|---|---|---|
int64 |
整数(既定) | 8 バイト |
int32/uint8 |
整数(範囲が狭い)/0〜255 | 4 バイト/1 バイト |
float64 |
小数(既定) | 8 バイト |
float32 |
小数(桁数は約7桁) | 4 バイト |
bool |
True/False | 1 バイト |
str_(<U3 など) |
文字列 | 文字数×4 バイト |
整数型には範囲があります。 範囲を超えると、例外にならず値が一周します。
print(np.array([2**62]) * 2)
[-9223372036854775808]
大きな値を扱うときは、小数型に変えるか、Python の int で計算すると安心です。
形・次元・要素数を調べる
a = np.arange(12).reshape(3, 4)
print(repr(a))
print(a.shape, a.ndim, a.size, a.dtype, a.itemsize, a.nbytes)
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
(3, 4) 2 12 int64 8 96
| 属性 | 意味 | 上の例 |
|---|---|---|
shape |
各次元の長さ(行数, 列数) | (3, 4) |
ndim |
次元の数 | 2 |
size |
全要素数 | 12 |
dtype |
データ型 | int64 |
itemsize/nbytes |
1要素/全体のバイト数 | 8/96 |
shape の見分け方は、つまずきやすいところです。
| 作り方 | shape | 呼び方 |
|---|---|---|
np.array([1, 2, 3]) |
(3,) |
1次元(行でも列でもない) |
np.array([[1, 2, 3]]) |
(1, 3) |
1行3列 |
np.array([[1], [2], [3]]) |
(3, 1) |
3行1列 |
(3,) と (3, 1) は別物です。この違いが、後で出てくるブロードキャストのエラーに関わります。
要素を参照する・スライスする
a = np.arange(12).reshape(3, 4)
print(repr(a[0])) # 0行目
print(repr(a[1, 2])) # 1行目・2列目
print(repr(a[-1, -1])) # 最後の行・最後の列
print(repr(a[:, 0])) # 0列目すべて
print(repr(a[:2, 1:3])) # 0〜1行目の、1〜2列目
print(repr(a[::2])) # 1行おき
print(repr(a[[0, 2]])) # 0行目と2行目(リストで指定)
print(repr(a[[0, 2], [1, 3]])) # (0,1) と (2,3) の2要素
array([0, 1, 2, 3])
np.int64(6)
np.int64(11)
array([0, 4, 8])
array([[1, 2],
[5, 6]])
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])
array([[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])
array([ 1, 11])
2次元は a[行, 列] とカンマで区切ります。 a[1][2] でも同じ値が取れますが、a[1, 2] のほうが途中の配列を作らずに済みます。
1要素を取り出すと np.int64(6) のように表示されます。これは NumPy 2.0 から、スカラー(単一の値)の表示に型が付くようになったためです。Python の int がほしいときは .item()、リストに戻すときは .tolist() を使います。
print(type(a[1, 2]).__name__, a[1, 2].item(), a.tolist()[0])
int64 6 [0, 1, 2, 3]
条件で取り出す(ブールインデックス)
配列に比較演算子を当てると、True/False の配列(マスク)が返ります。 それを [] に入れると、True の位置の要素だけが取り出せます。
a = np.arange(12).reshape(3, 4)
print(repr(a > 6))
print(repr(a[a > 6]))
print(repr(a[(a > 2) & (a % 2 == 0)])) # かつ
print(repr(a[(a < 2) | ~(a < 10)])) # または、否定
array([[False, False, False, False],
[False, False, False, True],
[ True, True, True, True]])
array([ 7, 8, 9, 10, 11])
array([ 4, 6, 8, 10])
array([ 0, 1, 10, 11])
| 論理 | NumPy での書き方 | 注意 |
|---|---|---|
| かつ | (条件1) & (条件2) |
各条件を丸かっこで囲む |
| または | (条件1) | (条件2) |
同上 |
| 否定 | ~(条件) |
not ではなく ~ |
Python の and/or を使うと、次のエラーになります。
a[a > 2 and a < 5]
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
and を & に替え、条件を丸かっこで囲むと解決します。
条件に合う要素の置き換え、件数、存在確認も同じ考え方です。
b = a.copy()
b[b > 6] = 0 # 条件に合う要素を書き換える
print(repr(b))
print(repr(np.where(a > 6, a, -1))) # 条件に合えば a、合わなければ -1
print(np.count_nonzero(a > 6)) # 条件に合う個数
print((a > 10).any(), (a >= 0).all()) # 1つでも/すべて
array([[0, 1, 2, 3],
[4, 5, 6, 0],
[0, 0, 0, 0]])
array([[-1, -1, -1, -1],
[-1, -1, -1, 7],
[ 8, 9, 10, 11]])
5
True True
形を変える(reshape・転置・次元の追加)
| 操作 | コード | 結果の shape |
|---|---|---|
| 形を変える | r.reshape(2, 3) |
(2, 3) |
| 片方を自動計算 | r.reshape(3, -1) |
(3, 2) |
| 縦1列にする | r.reshape(-1, 1) |
(6, 1) |
| 転置(行と列の入れ替え) | m.T |
(3, 2) |
| 1次元にする | m.ravel()/m.flatten() |
(6,) |
| 次元を足す | r[np.newaxis, :]/r[:, None] |
(1, 6)/(6, 1) |
| 長さ1の次元を消す | np.squeeze(x) |
(1, 6) → (6,) |
| 軸を並べ替える | x.transpose(2, 0, 1) |
(2, 3, 4) → (4, 2, 3) |
r = np.arange(6)
print(repr(r.reshape(2, 3)))
print(repr(r.reshape(3, -1)))
print(repr(r.reshape(2, 3).T))
print(repr(r.reshape(2, 3).ravel()))
print(r[np.newaxis, :].shape, r[:, None].shape)
array([[0, 1, 2],
[3, 4, 5]])
array([[0, 1],
[2, 3],
[4, 5]])
array([[0, 3],
[1, 4],
[2, 5]])
array([0, 1, 2, 3, 4, 5])
(1, 6) (6, 1)
-1 は「残りは自動で計算して」という意味で、1か所だけ使えます。要素数が合わない形を指定すると、次のエラーになります。
r.reshape(4, 2)
ValueError: cannot reshape array of size 6 into shape (4,2)
6個の要素は 4×2=8個の枠に収まりません。「変更前の要素数=変更後の各次元の積」になる形を指定するか、-1 を使うと解決します。
配列を結合・分割する
x = np.array([[1, 2], [3, 4]])
y = np.array([[5, 6], [7, 8]])
print(repr(np.concatenate([x, y]))) # 縦に(axis=0)
print(repr(np.concatenate([x, y], axis=1))) # 横に
print(np.vstack([x, y]).shape, np.hstack([x, y]).shape, np.stack([x, y]).shape)
print(repr(np.stack([np.array([1, 2]), np.array([3, 4])])))
print(repr(np.column_stack([np.array([1, 2]), np.array([3, 4])])))
print(np.split(np.arange(6), 3))
array([[1, 2],
[3, 4],
[5, 6],
[7, 8]])
array([[1, 2, 5, 6],
[3, 4, 7, 8]])
(4, 2) (2, 4) (2, 2, 2)
array([[1, 2],
[3, 4]])
array([[1, 3],
[2, 4]])
[array([0, 1]), array([2, 3]), array([4, 5])]
| 関数 | 動き | 次元 |
|---|---|---|
np.concatenate([x, y], axis=0) |
既にある軸に沿ってつなぐ | 変わらない |
np.vstack/np.hstack |
縦/横につなぐ | 変わらない(1次元どうしの vstack は2次元に) |
np.stack([x, y]) |
新しい軸を作って重ねる | 1つ増える |
np.column_stack |
1次元配列を「列」として並べる | 2次元になる |
np.split(a, n) |
n 等分する | リストで返る |
渡すのは「配列のリスト」です。 np.concatenate(x, y) ではなく np.concatenate([x, y]) と角かっこで囲みます。
np.append は毎回新しい配列を作るので、ループの中で1件ずつ足すと時間がかかります。Python のリストにためてから、最後に1回 np.array か np.concatenate すると速く済みます。
集計する(axisの考え方)
a = np.arange(12).reshape(3, 4)
print(a.sum(), repr(a.sum(axis=0)), repr(a.sum(axis=1)))
print(a.mean(), repr(a.mean(axis=0)), repr(a.mean(axis=1)))
print(a.max(), repr(a.max(axis=1)), a.argmax(), repr(a.argmax(axis=1)))
print(repr(a.sum(axis=1, keepdims=True)))
66 array([12, 15, 18, 21]) array([ 6, 22, 38])
5.5 array([4., 5., 6., 7.]) array([1.5, 5.5, 9.5])
11 array([ 3, 7, 11]) 11 array([3, 3, 3])
array([[ 6],
[22],
[38]])
axis は「どの軸をつぶして1つにまとめるか」です。
| 指定 | つぶす軸 | 結果 | 覚え方 |
|---|---|---|---|
| なし | 全部 | 1つの値 | 全体の合計 |
axis=0 |
行方向(縦) | 列ごとの値。shape は (4,) |
「列ごと」の集計 |
axis=1 |
列方向(横) | 行ごとの値。shape は (3,) |
「行ごと」の集計 |
shape (3, 4) に対して axis=0 を指定すると、0番目の 3 が消えて (4,) になる、と考えると迷いません。keepdims=True を付けると、つぶした軸が長さ1で残り((3, 1))、後で元の配列と計算するときに形が合いやすくなります。
| 関数 | 内容 | 関数 | 内容 |
|---|---|---|---|
sum/mean |
合計/平均 | min/max |
最小/最大 |
std/var |
標準偏差/分散 | argmin/argmax |
最小/最大の位置 |
np.median |
中央値 | cumsum |
累積和 |
np.unique |
重複のない値 | np.sort/np.argsort |
並べ替え/並べ替えたときの位置 |
u = np.array([3, 1, 2, 3, 1])
print(np.unique(u, return_counts=True))
print(np.sort(u), np.argsort(u), np.sort(u)[::-1])
print(np.cumsum(np.array([1, 2, 3, 4])))
n = np.array([1.0, np.nan, 3.0])
print(n.mean(), np.nanmean(n))
(array([1, 2, 3]), array([2, 1, 2]))
[1 1 2 3 3] [1 4 2 0 3] [3 3 2 1 1]
[ 1 3 6 10]
nan 2.0
欠損値(np.nan)が1つでもあると、mean や sum の結果は nan になります。 欠損を除いて計算したいときは np.nanmean、np.nansum など nan で始まる関数を使います。
配列どうしを計算する
四則演算は「同じ位置の要素どうし」で行われます。 ループを書く必要はありません。
v = np.array([1, 2, 3])
w = np.array([10, 20, 30])
print(v + w, v * w, w / v, v ** 2, w // 7, w % 7)
print(v == np.array([1, 0, 3]), np.array_equal(v, v))
print(np.sqrt(np.array([1, 4, 9])), np.clip(np.array([-5, 5, 50]), 0, 10))
[11 22 33] [10 40 90] [10. 10. 10.] [1 4 9] [1 2 4] [3 6 2]
[ True False True] True
[1. 2. 3.] [ 0 5 10]
リストとは * の意味が異なります。
print(np.array([1, 2, 3]) * 2)
print([1, 2, 3] * 2)
[2 4 6]
[1, 2, 3, 1, 2, 3]
行列の積は @ を使う
x = np.array([[1, 2], [3, 4]])
y = np.array([[5, 6], [7, 8]])
print(repr(x @ y)) # 行列の積
print(repr(x * y)) # 要素ごとの積
print(v @ w) # 1次元どうしは内積
array([[19, 22],
[43, 50]])
array([[ 5, 12],
[21, 32]])
140
* は要素ごとの積、@ が行列の積です。数学の行列の掛け算をしたいときは @ を選びます。
小数の比較は np.isclose
s = np.array([0.1, 0.2, 0.3]).sum()
print(s == 0.6, np.isclose(s, 0.6))
False True
小数の計算にはごく小さな誤差が乗るので、等しいかどうかは np.isclose/np.allclose で確かめます。
ブロードキャスト(形が違う配列の計算)
ブロードキャストとは、形の違う配列どうしを計算するときに、小さいほうを自動で引き伸ばして形を合わせる仕組みです。
v = np.array([1, 2, 3])
print(v * 10) # スカラーとの計算
print(repr(np.zeros((2, 3)) + v)) # (2,3) と (3,)
print(repr(np.array([[0], [10], [20]]) + v)) # (3,1) と (3,)
[10 20 30]
array([[1., 2., 3.],
[1., 2., 3.]])
array([[ 1, 2, 3],
[11, 12, 13],
[21, 22, 23]])
ルールは2つです。
- shape を右端からそろえて比べる(次元が足りない側は、左に長さ1を補う)
- 各次元で「同じ長さ」か「どちらかが1」なら計算できる(1 のほうが引き伸ばされる)
| 左の shape | 右の shape | 右端から比較 | 結果 |
|---|---|---|---|
(2, 3) |
(3,) |
3 と 3 が一致、2 と(補った)1 | (2, 3) |
(3, 1) |
(3,) |
1 と 3 → 1を伸ばす、3 と 1 → 1を伸ばす | (3, 3) |
(2, 3) |
(2,) |
3 と 2 が不一致 | エラー |
(2, 3) |
(2, 1) |
3 と 1 → 伸ばす、2 と 2 が一致 | (2, 3) |
np.broadcast_shapes((2, 3), (3,)) で、計算後の形を事前に確かめられます(結果は (2, 3))。
shapeが合わないエラーの再現と直し方
operands could not be broadcast together
行ごとに別々の値を足そうとして、よく出るエラーです。
m = np.arange(6).reshape(2, 3) # shape (2, 3)
m + np.array([10, 20]) # shape (2,)
ValueError: operands could not be broadcast together with shapes (2,3) (2,)
右端どうしが 3 と 2 で合いません。[:, np.newaxis] で (2, 1) の縦の形にすると、行ごとに足せます。
print(repr(m + np.array([10, 20])[:, np.newaxis]))
array([[10, 11, 12],
[23, 24, 25]])
同じ原因で、「行の合計で割って割合を出す」ときもエラーになります。keepdims=True を付けると形が (2, 1) で残り、そのまま割れます。
# m / m.sum(axis=1) は (2,3) と (2,) でエラー
print(repr(np.round(m / m.sum(axis=1, keepdims=True), 2)))
array([[0. , 0.33, 0.67],
[0.25, 0.33, 0.42]])
matmulの次元不一致
np.ones((2, 3)) @ np.ones((2, 3))
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)
行列の積は「左の列数=右の行数」が条件です。(2, 3) @ (2, 3) は 3 と 2 が合いません。右側を転置して (3, 2) にすると、(2, 3) @ (3, 2) で (2, 2) になります。
concatenateの次元数の違い
x = np.array([[1, 2], [3, 4]])
np.concatenate([x, np.array([9, 9])])
ValueError: all the input arrays must have same number of dimensions, but the array at index 0 has 2 dimension(s) and the array at index 1 has 1 dimension(s)
2次元と1次元は直接つなげません。1次元のほうに [np.newaxis, :] を付けて (1, 2) にします。
print(repr(np.concatenate([x, np.array([9, 9])[np.newaxis, :]])))
array([[1, 2],
[3, 4],
[9, 9]])
エラー別の直し方一覧
| エラー文の一部 | 原因 | 直し方 |
|---|---|---|
operands could not be broadcast together with shapes (2,3) (2,) |
右端の長さが合わない | [:, np.newaxis] や keepdims=True で (2, 1) に |
matmul: Input operand 1 has a mismatch |
左の列数と右の行数が違う | .T で転置、または形を確認 |
must have same number of dimensions |
次元数の違う配列を結合 | np.newaxis や reshape で次元をそろえる |
cannot reshape array of size 6 into shape (4,2) |
要素数が合わない | -1 を使う、形を見直す |
inhomogeneous shape |
行ごとに長さが違うリスト | 長さをそろえてから np.array |
truth value of an array ... is ambiguous |
and/or/if 配列: を使った |
&/|、.any()/.all() |
迷ったら、計算の直前に print(x.shape, y.shape) を入れるのがいちばんの近道です。
乱数を作る(default_rng)
現在の NumPy では、np.random.default_rng() で乱数生成器(Generator)を作り、そのメソッドを呼ぶ書き方が推奨されています。 np.random.seed()/np.random.rand() といった以前からの関数も動きますが、公式ドキュメントは新しいコードに Generator を使うよう案内しています。
rng = np.random.default_rng(42) # 42 はシード。同じ値なら同じ乱数列になる
print(rng.random(3)) # 0以上1未満の小数
print(rng.integers(1, 7, size=5)) # 1〜6の整数(7は含まない)
print(rng.normal(50, 10, size=3)) # 平均50・標準偏差10の正規分布
print(rng.uniform(0, 10, 2)) # 0以上10未満の一様分布
print(rng.choice(["A", "B", "C"], size=4)) # 重複ありで選ぶ
print(rng.choice(10, size=3, replace=False)) # 重複なしで選ぶ
print(rng.permutation(5)) # 並べ替えた新しい配列
arr = np.arange(5)
rng.shuffle(arr) # その場で並べ替える
print(arr)
print(repr(rng.integers(0, 10, size=(2, 3)))) # 2行3列
[0.77395605 0.43887844 0.85859792]
[1 5 2 1 4]
[51.27840403 46.83757408 49.83198842]
[4.50385938 3.70798024]
['C' 'A' 'C' 'C']
[8 5 3]
[0 1 3 4 2]
[1 4 0 2 3]
array([[6, 1, 7],
[7, 3, 0]])
| 以前からの書き方 | Generator での書き方 |
|---|---|
np.random.seed(42) |
rng = np.random.default_rng(42) |
np.random.rand(3) |
rng.random(3) |
np.random.randint(1, 7, 5) |
rng.integers(1, 7, size=5) |
np.random.randn(3) |
rng.standard_normal(3) |
np.random.choice(...) |
rng.choice(...) |
np.random.shuffle(arr) |
rng.shuffle(arr) |
同じシードなら、何度実行しても同じ結果になります。 検証を再現したいときに役立ちます。
print(np.random.default_rng(42).integers(0, 100, 3))
print(np.random.default_rng(42).integers(0, 100, 3))
[ 8 77 65]
[ 8 77 65]
NumPy 2.xで変わったこと(実際に動かして確認)
NumPy 2.0 は2024年6月に公開された大きな更新で、1.x 向けの解説コードがそのままでは動かない箇所があります。NumPy 2.4.4 で実際に確かめた結果を載せます。
型昇格のルールが変わった(NEP 50)
NumPy 2.0 から、Python の int/float と NumPy の値を計算したとき、NumPy 側の型が保たれるようになりました。
print(repr(np.float32(3) + 3.0)) # NumPyスカラー + Pythonのfloat
print((np.array([1, 2], dtype=np.float32) * 2.0).dtype) # float32の配列 × Pythonのfloat
print((np.array([1, 2], dtype=np.float32) * np.float64(2.0)).dtype) # 相手がnp.float64
print((np.array([1, 2], dtype=np.uint8) + 1).dtype) # uint8の配列 + Pythonのint
np.float32(6.0)
float32
float64
uint8
| 計算 | NumPy 1.x | NumPy 2.x |
|---|---|---|
np.float32(3) + 3.0 |
float64 | float32(実行で確認) |
float32の配列 * np.float64(2.0) |
float32 | float64(実行で確認) |
np.uint8(1) + 300 |
型を広げて計算 | OverflowError(実行で確認) |
1.x の列は公式の移行ガイドの記載で、手元では動かしていません。
Python の数値は「相手の型に合わせる」、NumPy の型どうしは「精度の高いほうに合わせる」と覚えると整理できます。その結果、型に収まらない Python の整数を足すと、エラーで知らせてくれます。
np.array([1, 2], dtype=np.uint8) + 300
OverflowError: Python integer 300 out of bounds for uint8
一方、計算の結果が範囲を超える場合は、例外にならず値が一周します。
print(repr(np.array([200], dtype=np.uint8) + 100))
print(repr(np.uint8(200) + 100)) # スカラーは警告つき
array([44], dtype=uint8)
np.uint8(44)
RuntimeWarning: overflow encountered in scalar add
200 + 100 = 300 が 0〜255 に収まらず、44 になりました。画像データなど uint8 の配列を計算するときは、先に astype(np.int64) などで広い型に変えると安心です。
print(repr(np.array([200], dtype=np.uint8).astype(np.int64) + 300))
array([500])
削除された名前と置き換え先
np.float_ などの別名が 2.0 で削除されました。エラーメッセージが置き換え先を教えてくれます。
np.float_
AttributeError: `np.float_` was removed in the NumPy 2.0 release. Use `np.float64` instead.
2.4.4 で実際にアクセスして確かめた一覧です。
| 使えなくなった名前 | 置き換え先 | 2.4.4 での結果 |
|---|---|---|
np.float_ |
np.float64 |
AttributeError(2.0 で削除) |
np.complex_ |
np.complex128 |
AttributeError(2.0 で削除) |
np.unicode_/np.string_ |
np.str_/np.bytes_ |
AttributeError(2.0 で削除) |
np.Inf/np.infty/np.PINF |
np.inf |
AttributeError(2.0 で削除) |
np.NINF |
-np.inf |
AttributeError(2.0 で削除) |
np.NaN |
np.nan |
AttributeError(2.0 で削除) |
np.product/np.cumproduct |
np.prod/np.cumprod |
AttributeError |
np.sometrue/np.alltrue |
np.any/np.all |
AttributeError(2.0 で削除) |
np.round_ |
np.round |
AttributeError(2.0 で削除) |
np.asfarray |
np.asarray(a, dtype=float) |
AttributeError(2.0 で削除) |
np.in1d |
np.isin |
AttributeError(2.4 で削除) |
np.trapz |
np.trapezoid |
AttributeError(2.4 で削除) |
np.int/np.float/np.object |
組み込みの int/float/object |
AttributeError(1.24 で削除済み) |
np.bool は 2.0 で NumPy の真偽値型の名前として使えるようになり、2.4.4 ではエラーになりません。np.in1d と np.trapz は 2.0 で非推奨になり、2.4.0 のリリースノートに削除の記載があります。
表示(repr)が変わった
print(repr(np.float64(3.14)), str(np.float64(3.14)))
print([np.int64(1), np.float64(2.5)])
print([x.item() for x in [np.int64(1), np.float64(2.5)]])
np.float64(3.14) 3.14
[np.int64(1), np.float64(2.5)]
[1, 2.5]
リストや辞書に NumPy のスカラーが入っていると、np.int64(1) のように表示されます。値は同じです。画面や JSON に出す前に .item() か .tolist() で Python の型に戻すと、すっきりします。
np.array(..., copy=False) の意味が変わった
np.array([1, 2, 3], copy=False, dtype=float)
ValueError: Unable to avoid copy while creating an array as requested.
If using `np.array(obj, copy=False)` replace it with `np.asarray(obj)` to allow a copy when needed (no behavior change in NumPy 1.x).
2.x の copy=False は「コピーが必要ならエラー」という意味です。「必要なときだけコピーする」動きがほしいときは np.asarray(obj) に書き換えます。
Ruffで書き換え箇所を見つける
公式の移行ガイドは、Ruff のルール NPY201 を案内しています。Ruff 0.15.11 で試した結果です。
# old.py
import numpy as np
x = np.float_(1.5)
y = np.NaN
z = np.in1d([1, 5], [1, 2])
$ ruff check --select NPY201 old.py --output-format concise
old.py:3:5: NPY201 [*] `np.float_` will be removed in NumPy 2.0. Use `numpy.float64` instead.
old.py:4:5: NPY201 [*] `np.NaN` will be removed in NumPy 2.0. Use `numpy.nan` instead.
old.py:5:5: NPY201 [*] `np.in1d` will be removed in NumPy 2.0. Use `numpy.isin` instead.
Found 3 errors.
ruff check --select NPY201 --fix old.py を実行すると、3か所が np.float64(1.5)、np.nan、np.isin(...) に自動で書き換わりました。
リストとの速度比較
100万要素で、Python のリストと NumPy の配列を比べました(Python 3.11.15、NumPy 2.4.4、timeit で5回×15セットの最小値。数値は環境によって変わります)。
import math, timeit
import numpy as np
n = 1_000_000
lst = list(range(n))
arr = np.arange(n)
| 処理 | リスト | NumPy | 差 |
|---|---|---|---|
| 全要素を2倍 | [x * 2 for x in lst] 28.93 ms |
arr * 2 0.54 ms |
約54倍 |
| 合計 | sum(lst) 3.12 ms |
arr.sum() 0.27 ms |
約12倍 |
| 平方根 | [math.sqrt(x) for x in lst] 43.43 ms |
np.sqrt(arr) 1.29 ms |
約34倍 |
| 偶数だけ取り出す | [x for x in lst if x % 2 == 0] 20.50 ms |
arr[arr % 2 == 0] 6.40 ms |
約3倍 |
| メモリ | 36,000,056 バイト | 8,000,000 バイト | 約4.5分の1 |
速さの理由は、同じ型の値が連続したメモリに並び、ループが C 言語の層で実行されるためです。 逆に言うと、NumPy の配列を Python の for で1要素ずつ回すと、この利点が使えません。
| 書き方 | 時間 |
|---|---|
arr * 2(配列のまま一括計算) |
0.54 ms |
[x * 2 for x in lst](リストを内包表記で) |
28.93 ms |
[x * 2 for x in arr](配列を for で1要素ずつ) |
75.88 ms |
np.sum(lst)(リストを NumPy の関数に渡す) |
23.68 ms |
配列を for で回すと、リストより時間がかかりました。 また、リストを NumPy の関数に渡すと、毎回配列への変換が入ります。「最初に1回 np.array にして、あとは配列のまま計算する」のがコツです。
要素が数個のときは、変換や呼び出しの手間のほうが大きくなります。3要素の合計は、リストの sum が 0.044 マイクロ秒、配列の .sum() が 0.761 マイクロ秒でした。NumPy は「たくさんの数値をまとめて計算する」ときに力を発揮します。
ビューとコピーの違い
NumPy のスライスは、元の配列と同じメモリを見る「ビュー」を返します。 Python のリストのスライス(コピーを返す)とは動きが違うので、最初に確かめておくと安心です。
base = np.arange(6)
s = base[1:4] # スライス → ビュー
s[0] = 99
print(base) # 元の配列も変わる
lst = [0, 1, 2, 3, 4, 5]
ls = lst[1:4] # リストのスライス → コピー
ls[0] = 99
print(lst) # 元のリストは変わらない
[ 0 99 2 3 4 5]
[0, 1, 2, 3, 4, 5]
元を変えたくないときは、.copy() を付けます。
base = np.arange(6)
c = base[1:4].copy()
c[0] = 99
print(base, np.shares_memory(base, c))
[0 1 2 3 4 5] False
どの操作がビューで、どれがコピーかを、np.shares_memory(メモリを共有しているかを調べる関数)で確かめた結果です。
| 操作 | 返るもの | np.shares_memory |
|---|---|---|
スライス base[1:4] |
ビュー | True |
reshape |
ビュー(連続したメモリの場合) | True |
転置 .T |
ビュー | True |
ravel() |
ビュー(可能な場合) | True |
b = a(代入) |
同じ配列そのもの | b is a が True |
.copy() |
コピー | False |
リストで指定 base[[1, 2, 3]] |
コピー | False |
ブールインデックス base[base > 2] |
コピー | False |
flatten() |
コピー | False |
astype(float) |
コピー | False |
「スライスと形の変更はビュー、リスト・条件での取り出しはコピー」が基本です。ravel() は転置した配列のようにメモリが連続していない場合はコピーを返します(base.T.ravel() は False でした)。確実にコピーがほしいときは flatten() か .copy() を選びます。
なお、ブールインデックスへの代入は、元の配列を書き換えます。
base = np.arange(6)
base[base > 2] = 0
print(base)
[0 1 2 0 0 0]
取り出して変数に入れたもの(x = base[base > 2])はコピーですが、左辺に書いた base[条件] = 値 は元の配列への書き込みです。
よくある質問
Q. NumPyの配列とPythonのリストは何が違いますか?
A. 配列は全要素が同じ型で、arr * 2 のように全体を一括で計算できます。100万要素を2倍にする処理は、リストの内包表記が 28.93 ms、配列が 0.54 ms でした。一方、型の違う値を混ぜたい、要素を1件ずつ頻繁に足したい、という用途はリストが向いています。
Q. axis=0 と axis=1 はどちらが行でどちらが列ですか?
A. axis=0 は行方向(縦)につぶすので「列ごと」の結果、axis=1 は列方向(横)につぶすので「行ごと」の結果になります。shape (3, 4) に axis=0 を指定すると、0番目の 3 が消えて (4,) になる、と覚えると確実です。
Q. NumPy 2.x に上げたら np.float_ でエラーになりました。どうすればよいですか?
A. np.float64 に書き換えます。エラーメッセージに置き換え先が書かれています。まとめて直すなら ruff check --select NPY201 --fix が便利です。利用しているライブラリが NumPy 2.x に対応した版かどうかも、あわせて確認するとよいです。
Q. reshape の -1 は何を意味しますか?
A. 「その次元の長さは、全要素数から自動で計算する」という指定です。np.arange(6).reshape(3, -1) は (3, 2) になります。-1 を使えるのは1か所だけです。
まとめ
- NumPy の配列(ndarray)は、全要素が同じ型で、ループなしに一括計算できる。100万要素の2倍は、リストの約54分の1の時間だった
- 作成は
np.array/arange/linspace/zeros、確認はshape/ndim/dtype。2次元以上の形はタプルで渡す - 参照は
a[行, 列]、条件での取り出しはa[a > 6]。条件の組み合わせは&、|、~と丸かっこで書く axis=0は列ごと、axis=1は行ごとの集計。keepdims=Trueを付けると、後の計算で形が合いやすい- ブロードキャストは shape を右端から比べ、同じか1なら計算できる。エラーが出たら
shapeを表示し、np.newaxisで次元を足す - NumPy 2.x では Python の数値が NumPy 側の型に合わせられ(NEP 50)、
np.float_などの別名は削除された。置き換え先はエラーメッセージと Ruff の NPY201 で分かる - スライスと reshape はビュー、リスト・条件での取り出しはコピー。元を変えたくないときは
.copy()
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「NumPyの使い方 基本一覧|配列の作成・演算・形状変更」
株式会社RJCでは、NumPyをはじめとするPythonの数値計算を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。