【NumPy入門】配列の基礎的な扱い方(ndarray)

Python言語とNumPyで配列を扱う際の基礎的な部分をソースコード付きで解説します。

NumPyとは

Pythonには、C言語の配列に類似する機能として 「リスト」 が標準で備わっています。リストは 要素数を自由に増減できる うえに、異なるデータ型の値を同時に格納できる という柔軟さが特徴です。その一方で、この柔軟性ゆえに 処理速度が遅い という欠点があります。

特に画像処理や機械学習のように、膨大なデータを高速に処理する必要がある分野では、この遅さが大きなボトルネックになります。

そこで登場するのが、Pythonで数値計算を行う際の定番ライブラリである NumPy です。NumPy が提供する ndarray(多次元配列) は、内部処理が C言語や Fortran で実装 されているため、Python のリストと比べて 圧倒的に高速 に動作します。

さらに、NumPy 配列は算術演算子をそのまま使って ベクトル演算や行列演算を直感的に記述できる ため、短いコードで効率的な数値計算を実現できます。高速性と表現力の両方を兼ね備えている点が、NumPy が科学技術計算で広く利用されている理由です。

ポイント Pythonリスト NumPy配列(ndarray)
処理速度 遅い(Pythonレベルのループ) 大量データ処理で非常に速い(C/Fortran実装)
メモリ効率 低い(オブジェクト参照の集合) 高い(連続領域に生データを格納するため大規模データでも省メモリ)
データ型 バラバラでOK 全要素が同じ dtype
ベクトル・行列演算 for 文が必要 a + b のように一行で可能。コードが短く、可読性が高い
エコシステムとの相性 弱い Pandas / SciPy / scikit-learn の基盤。データ分析・機械学習に必須

動画で見る

本ページの内容は、以下動画で解説しています。

コードの記述量と実行速度

「Pythonリスト」と 「NumPy 配列」で同じ処理を行ったとき、どれだけ速度に差が出るのかを比較してみます。以下のコードは、1,000万個の整数データもつ「Pythonリスト」と「NumPy配列」を生成し、各要素の値に対して「平方根を取る」「3を足す」「2倍する」「sinを取る」といった計算を行ったときの処理時間を計測します。

import time
import math
import numpy as np

# ============================================
#  比較用データの準備
#  N は処理する要素数(ここでは 1,000万)
#  大きいほどリストと NumPy の速度差が顕著になる
# ============================================
N = 10000000


# ============================================
#  Pythonリストでの処理
# ============================================
print("=== Pythonリストでの処理 ===")

# 0〜N-1 の整数を順番に格納した Python のリストを作成
lst = list(range(N))

start = time.time()  # 計測開始

# リスト内包表記で1つずつ処理
#   - math.sqrt(x): 平方根
#   - +3: 加算
#   - *2: 乗算
#   - math.sin(...): 三角関数
lst_result = [
    math.sin((math.sqrt(x) + 3) * 2)
    for x in lst
]

end = time.time()  # 計測終了
print(f"処理時間: {end - start:.4f} 秒")


# ============================================
#  NumPy配列での処理
# ============================================
print("\n=== NumPy配列での処理 ===")

# 0〜N-1 の整数を連続したメモリ領域に格納した NumPy 配列を生成
arr = np.arange(N)

start = time.time()  # 計測開始

# Python の for 文を使わず、一括で計算される
arr_result = np.sin((np.sqrt(arr) + 3) * 2)

end = time.time()  # 計測終了
print(f"処理時間: {end - start:.4f} 秒")

実行結果は以下の通りです。

=== Pythonリストでの処理 ===
処理時間: 4.7779 秒

=== NumPy配列での処理 ===
処理時間: 0.1948 秒

「Pythonリスト」と「NumPy配列」の処理速度を比較すると、「NumPy配列」のほうが約25倍速いという結果になっています。また、「NumPy配列」のほうが計算部分のコードも「np.sin((np.sqrt(arr) + 3) * 2)」と簡潔に実装できることがわかります。

【検証環境】
Surface Pro 12インチ(メモリ容量:16GB CPU:Snapdragon X Plus)
Python 3.13.6
【Python】リスト型とNumPy型の処理速度比較
この記事では、PythonでリストとNumPy配列で画像処理した場合の処理速度を比較してみたので紹介します。

本章では、NumPyの配列の基本的な扱い方を紹介していきます。なお、本ページ中に何度も登場する「配列」という言葉は「NumPyの配列」を示すものとします。

NumPyのインストール

コマンドプロンプトもしくはターミナル上で、以下のpipコマンドを実行するとインストールできます。

pip install numpy
NumPyのインストール (Windows、Linux、Ubuntu)
Python環境にNumPyをインストールする方法ついて入門者向けに使い方を解説します。

配列の生成(numpy.array、numpy.empty)

numpy.array(list, dtype)メソッドにPythonリストを渡すことで配列を生成できます。
1次元配列を生成する場合は引数listに1次元リスト、2次元配列を生成する場合は引数に2次元リストを渡します。
配列のデータ型はdtypeで指定します。データ型を指定しなくても自動的に決まりますが、バグの元になりやすいため、きちっとおこなった方が安全です。

配列の要素を確認したい場合は、リストと同じようにprint関数を用います。

print(ndarray)

配列のデータ型を確認した場合は、dtype属性を用います。

print(ndarray.ndarray)

サンプルコード

以下は、配列の生成・中身を確認するサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np

# 1次元配列の生成
list1 = [1, 2, 3]
x = np.array(list1, dtype='int32')
print(x.dtype) # int32
print(x) # [1, 2, 3]


# 2次元配列の生成
list2 = [[1, 2, 3], [4, 5, 6]]
X = np.array(list2, dtype='float32')
print(X.dtype) # float32
print(X) # [[1 2 3]
           #  [4 5 6]]
種類 データ型の一覧表
bool 論理値型
inti OS依存の整数(64bitのOSならint型64ビット)
int8 8ビットの整数型
int16 16ビットの整数型
int32 32ビットの整数型
int64 64ビットの整数型
unit8 8ビット符号なし整数型(画像処理などでよく使います)
unit16 16ビット符号なし整数型
unit32 32ビット符号なし整数型
unit64 64ビット符号なし整数型
float16 16ビットの実数型
float32 32ビットの実数型
float64 64ビットの実数型
complex64 64ビットの複素数型
complex128 128ビットの複素数型

要素の値を初期化する必要がない場合は、numpy.empty(shape)を使ったほうが高速に配列を生成できます。
その場合、引数にはリストではなく配列の形状(2次元なら行と列のサイズ)を指定します。

# 2 × 3の空配列を生成
X = np.empty((2, 3), dtype='float32')
print(X) # [[  2.31948455e-310   2.31948455e-310   2.31948683e-310]
           #  [  2.31948683e-310   7.06652082e-096   7.05479222e-308]]

他にも、NumPyには配列を生成するのに便利なメソッドが数多く用意されています。

【NumPy入門】よく使う機能一覧
Pythonの数値計算ライブラリNumPyでよく使う機能(関数・メソッド・算術演算子など)を一覧にしてまとめてみました。
【NumPy】データ型の種類一覧 dtype
この記事では、Python言語とNumPyを用いて、配列生成時にデータ型を設定する方法をソースコード付きで解説します。

CSVファイルの読み込み(numpy.genfromtxt)

NumPyでは、外部ファイルからデータを読み込んで配列に格納するnumpy.genfromtxt(path, delimiter, dtype) メソッドが用意されています。
ここで、pathは読み込むファイルパス、delimiterは区切り文字、dtypeはデータ型を指定します。
このメソッドを使えば、CSVファイルなどのテキスト形式のファイルを読み込み、中身のデータを配列に格納するまで処理をたった1行で記述できます。後で紹介する応用例では、ネット上で公開されている日経平均株価の過去1年分のデータが記録されているCSVファイルを読み込んで統計処理してみます。

サンプルコード

以下は、CSVファイルを読み込むサンプルコードです。

import numpy as np

# CSVファイルを読み込み(区切り文字はカンマ)
X = np.genfromtxt('data.csv', delimiter=',')

# 配列の中身を確認
print(X) #[[  1.   2.   3.   4.]
           # [  5.   6.   7.   8.]
           # [  9.  10.  11.  12.]]

■data.csv

1,2,3,4
5,6,7,8
9,10,11,12

CSVファイルに書き込み(numpy.savetxt)

配列のデータを外部ファイルに保存する場合は、numpy.savetxt(path, ndarray, delimiter, fmt)メソッドを使います。
pathはファイルパス、ndarrayは配列、delimiterは区切り文字、fmtはデータ型を指定します。
例えば、区切り文字をカンマにして、CSV形式でデータを保存すればExcelなど多くのソフトウェアで開くことができます。

サンプルコード

以下は、CSVファイルに書き込むサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np

# 2次元配列の生成
X = np.array([[1, 2, 3],
              [4, 5, 6]])

# 二乗の計算
Y = X ** 2

# CSVファイルに2次元配列Yのデータを出力
np.savetxt('data.csv', Y, delimiter=",", fmt='%d')

■data.csv

1,4,9
16,25,36

統計処理

NumPyには、簡単な統計処理からフーリエ変換まで、様々なデータ処理用のメソッドが用意されています
NumPyは、これらのメソッドを活用して処理を実装していくことが重要です。
例えば、平均値を計算したい場合はnumpy.average(x)メソッドを用います。
引数に配列xを渡すだけで計算結果が返ってきます。

サンプルコード

以下は、簡単な統計処理を計算するサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np
### 
# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# 平均値の計算
ave = np.average(x)
print(ave) # 3.0

他にもNumPyには様々な計算用のメソッドが用意されています。

【NumPy入門】よく使う機能一覧
Pythonの数値計算ライブラリNumPyでよく使う機能(関数・メソッド・算術演算子など)を一覧にしてまとめてみました。

配列の四則演算(算術演算子)

NumPyでは、算術演算子だけで配列の要素同士の四則演算ができます。

サンプルコード

# -*- coding: utf-8 -*-
import numpy as np

# 1次元配列の生成
x = np.array([4, 5, 6])

y = np.array([3, 2, 1])

# 加算
print(x + y) # [7 7 7]

# 減算
print(x - y) # [1 3 5]

# 乗算
print(x * y) # [12 10  6]

# 除算
print(x / y) # [ 1.33333333   2.5  6.  ]

# 剰余
print(x % y) # [1 1 0]

# 冪乗(3乗)
print(x ** 3) # [ 64 125 216]

# 符号反転
print(-x) # [-4 -5 -6]

算術演算子「+」「-」だけで2つのベクトル・行列の加減算をすることができます。
ただし、「*」は内積でなく要素同士の積算となるので注意しましょう。
内積を計算したい場合は、「x.dot(y)」を用います。

スライスで要素へアクセス

NumPyの配列は、リストと同じようにx[index]要素へアクセスできます。
indexは0始まりで、x[0]なら先頭要素、x[1]ならその次の要素を示します。
負の値を指定すると末尾からアクセスできます。(x[-1]なら末尾要素

勿論、リストでもお馴染みの便利なスライスも使えます。
ndarray[i:j]のようにコロン区切りでi~j-1番目までの要素を切り出します。

1次元配列で要素へアクセスする例を次のサンプルコードで見てみましょう。

サンプルコード

以下は、1次元配列の要素にアクセスするサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np

# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# 先頭要素の参照
print(x[0]) # 1

# 末尾要素の参照
print(x[-1]) # 5

# スライスで参照(1~3番要素)
print(x[1:4])  # [2 3 4]

2次元配列の要素にアクセスする場合は、 カンマ区切りで行・列の順にindexを指定します。
インデックスに数値でなくコロンを入れた場合は、指定した行もしくは列の全ての要素を指定します。

サンプルコード

以下は、2次元配列の要素にアクセスするサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np

# 2次元配列の生成
X = np.array([[1, 2, 3],
              [4, 5, 6]])

# 0行目, 1列目にある要素の参照
print(X[0,1]) # 2

# 行の参照(0行目)
print(X[0,:]) # [1 2 3]

#列の参照(2列目)
print(X[:,2]) # [3 6]

# スライスで参照(0~1行目、1~2列目にある要素)
print(X[0:2, 1:3]) # [[2 3]
                   #  [5 6]]

ブールインデックス(マスク処理)

要素にアクセスする他の手段として「ブールインデックス」があります。
ブールインデックスは、配列に対して同じサイズの論理値(True/False)を格納した配列を与えると、trueの要素についてのみ処理を行うことができる機能です。これを使うことで、マスク処理ができます。

サンプルコード

以下は、ブールインデックスで要素へアクセスするサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np # NumPyのインポート

# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# マスク用の配列を生成
mask = np.array([True, False, False, True, False])

# Trueの要素のみ取り出し
y = x[mask]
print(y) # [1 4]

# Trueの要素のみ値を代入
x[mask] = 7
print(x) # [7 2 3 7 5]

y = x[mask]」では、マスク配列のTrueとなっている部分の要素を配列xから取り出し、新たに生成された配列yに格納しています。
x[mask] = 7」では、マスク配列のTrueとなっている部分の要素のみ代入しています。

このマスク処理は、比較演算子と組み合わせることで応用できます。
配列に対して比較演算子を与えると、比較結果(論理値)を格納した配列を取得できます。
これを元の配列に与えることで、特定の条件を満たす要素に対してのみ処理を行うことが可能です。
サンプルコードを見てみましょう。

# -*- coding: utf-8 -*-
import numpy as np # NumPyのインポート

# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# 比較演算子でマスク配列を生成
mask = x >= 3
print(mask) # [False False  True  True  True]

# マスク処理
y = x[mask]
print(y) # [3 4 5]

このサンプルでは、配列xの要素が3以上ならTrue、そうでなければFalseとなるマスク配列を生成します。
そして、ブールインデックスを使って結果的に値が3以上の要素のみを取り出しています。

for文で要素へアクセス

リストと同様、配列でもfor文を使って順に要素へアクセスできます。

サンプルコード

以下は、for文で要素にアクセスするサンプルコードです。

# -*- coding: utf-8 -*-
import numpy as np

# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# for文+enumerate関数で配列から要素とインデックスを順に取り出す
for index, a in enumerate(x):
    print('x[%d]=%d' % (index, a))

# x[0]=1
# x[1]=2
# x[2]=3
# x[3]=4
# x[4]=5

for文でループ処理を行う時にenumerate関数を使うと、要素aとそのインデックス(index)の両方を同時に取得できます。

ただし、for文で要素にアクセスしていくと処理速度が著しく低下していまします。
特に画像処理などで大量の要素をfor文で順に処理した場合は命取りとなります。
for文はできるだけ使わずNumPyのメソッドで処理を実装するのがコツです。

例えば、配列から値が3である要素のインデックスを探索する場合の良い例とそうでない例を見てみましょう。

良いコード

# -*- coding: utf-8 -*-
import numpy as np

# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# 値3をもつ要素のインデックスを探索(NumPyのメソッドで実装)
index = np.where(x==3)

print('値3のインデックス:', index[0]) # # 値3のインデックス [2]

良くないコード

# -*- coding: utf-8 -*-
import numpy as np

# 1次元配列の生成
x = np.array([1, 2, 3, 4, 5])

# 値3をもつ要素のインデックスを探索(for文で実装)
for i, a in enumerate(x):
    if(a == 3):
        print('値3のインデックス:', i) # # 値3のインデックス2

どうしてもfor文で大量のデータを扱わざる負えない場合は、以下の方法を取ると処理速度の低下をある程度抑えることができます。
NumPyの使い方自体は簡単ですが、どんなメソッドが用意されているのか把握しておくのが大切です。

関連コンテンツ

NumPyの様々な使い方については、以下ページにまとめていますので、ご活用ください。

【NumPy超入門】使い方とサンプルコードを解説
Pythonライブラリ「NumPy」の使い方から応用例まで、サンプルコード付きで入門者向けにまとめました。
記事の監修者
西住技研

プログラミング言語「Python」を研究、仕事、趣味でデータ分析や作業自動化などに活用してきたノウハウを情報発信しています。
筆者の詳しいプロフィールやお問合せはこちらのページまで。
YoutubeX(旧Twitter)でも情報発信中です!

西住技研をフォローする
NumPy

コメント