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つです。

  1. shape を右端からそろえて比べる(次元が足りない側は、左に長さ1を補う)
  2. 各次元で「同じ長さ」か「どちらかが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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?