NumPy徹底解説:多次元配列の生成と計算

プログラミング

NumPy徹底解説:多次元配列の生成と計算

NumPyは、Pythonにおける数値計算の基盤となるライブラリであり、特に多次元配列(ndarray)の操作に強力な機能を提供します。科学技術計算、データ分析、機械学習など、幅広い分野で不可欠な存在となっています。本解説では、NumPyの多次元配列の生成方法から、高度な計算機能、そしてそれに関連する様々な側面について網羅的に解説します。

NumPyの基礎:ndarrayとは

NumPyの核心は、ndarrayというデータ構造です。これは、同じデータ型の要素が格子状に配置された多次元配列です。リストと比較して、NumPy配列は以下の点で優れています。

  • 高速な計算: C言語で実装されたバックエンドにより、Pythonのリストよりもはるかに高速な数値計算が可能です。
  • メモリ効率: 同一データ型の要素のみを格納するため、メモリ使用量が効率的です。
  • 豊富な機能: ベクトル化された演算、ブロードキャスティング、高度なインデックス操作など、多様な機能を提供します。

ndarrayの生成方法

NumPy配列を生成するには、いくつかの方法があります。

リストからの生成

最も基本的な方法は、Pythonのリストを `np.array()` 関数に渡すことです。

import numpy as np

# 1次元配列
arr1d = np.array([1, 2, 3, 4, 5])
print(arr1d)

# 2次元配列
arr2d = np.array([[1, 2, 3], [4, 5, 6]])
print(arr2d)
特定の値で初期化された配列の生成
  • np.zeros(shape): 全ての要素が0の配列を生成します。
  • np.ones(shape): 全ての要素が1の配列を生成します。
  • np.full(shape, fill_value): 指定した値で埋められた配列を生成します。
  • np.empty(shape): 初期化されていない配列を生成します。メモリ上の既存の値がそのまま残るため、注意が必要です。
# 3x4のゼロ配列
zeros_arr = np.zeros((3, 4))
print(zeros_arr)

# 2x2の1配列
ones_arr = np.ones((2, 2))
print(ones_arr)

# 2x3の5で埋められた配列
full_arr = np.full((2, 3), 5)
print(full_arr)
連続した値を持つ配列の生成
  • np.arange(start, stop, step): 指定した範囲とステップで等間隔の値を持つ配列を生成します。
  • np.linspace(start, stop, num): 指定した範囲を等間隔に分割した `num` 個の値を持つ配列を生成します。
# 0から10まで(10は含まない)2ずつ増える配列
range_arr = np.arange(0, 10, 2)
print(range_arr)

# 0から1までを5つに等分割した配列
linspace_arr = np.linspace(0, 1, 5)
print(linspace_arr)
ランダムな値を持つ配列の生成

`np.random` モジュールは、様々な確率分布に基づいた乱数生成機能を提供します。

  • np.random.rand(d0, d1, ..., dn): 0から1の一様分布に従う乱数配列を生成します。
  • np.random.randn(d0, d1, ..., dn): 平均0、分散1の標準正規分布に従う乱数配列を生成します。
  • np.random.randint(low, high, size): 指定した範囲(low以上high未満)の整数乱数配列を生成します。
# 2x3の一様乱数配列
rand_arr = np.random.rand(2, 3)
print(rand_arr)

# 3x2の標準正規乱数配列
randn_arr = np.random.randn(3, 2)
print(randn_arr)

# 1から10までの整数乱数配列(サイズ3x3)
randint_arr = np.random.randint(1, 10, size=(3, 3))
print(randint_arr)

ndarrayの属性と操作

ndarrayオブジェクトは、その構造や内容に関する様々な属性を持っています。

主要な属性

  • ndim: 配列の次元数
  • shape: 配列の各次元のサイズを示すタプル
  • size: 配列の全要素数
  • dtype: 配列の要素のデータ型
arr = np.array([[1, 2, 3], [4, 5, 6]])
print(f"次元数: {arr.ndim}")
print(f"形状: {arr.shape}")
print(f"要素数: {arr.size}")
print(f"データ型: {arr.dtype}")

インデックスとスライシング

NumPy配列は、Pythonのリストと同様のインデックスとスライシング操作をサポートしますが、多次元配列においてはより強力な表現が可能です。

arr = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
print(arr[3])      # 4番目の要素 (インデックス3)
print(arr[2:5])    # インデックス2から4までの要素
print(arr[:5])     # 最初からインデックス4までの要素
print(arr[5:])     # インデックス5から最後まで

arr2d = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(arr2d[0, 1]) # 1行目(インデックス0)、2列目(インデックス1)の要素
print(arr2d[1, :]) # 2行目(インデックス1)の全ての要素
print(arr2d[:, 2]) # 全ての行の3列目(インデックス2)の要素
print(arr2d[1:, :2]) # 2行目以降、1列目と2列目の要素

ブールインデックス(条件による要素選択)

条件式を用いて、条件を満たす要素のみを選択できます。

arr = np.array([10, 20, 30, 40, 50])
condition = arr > 25
print(arr[condition]) # 条件を満たす要素のみが出力される

NumPyによる数値計算

NumPyの真価は、その強力な数値計算機能にあります。

要素ごとの演算

NumPy配列に対する算術演算は、デフォルトで要素ごとに行われます。

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

print(a + b) # 要素ごとの和
print(a - b) # 要素ごとの差
print(a * b) # 要素ごとの積
print(a / b) # 要素ごとの除算

print(a ** 2) # 各要素を2乗

ユニバーサル関数 (ufuncs)

NumPyは、要素ごとに適用される多くのユニバーサル関数を提供します。

  • np.sqrt(): 平方根
  • np.exp(): 指数関数
  • np.sin(), np.cos(), np.tan(): 三角関数
  • np.log(), np.log10(): 対数関数
  • np.abs(): 絶対値
arr = np.array([1, 4, 9, 16])
print(np.sqrt(arr))
print(np.exp(arr))

集約関数

配列全体の統計量を計算するための関数も豊富です。

  • np.sum(): 合計
  • np.mean(): 平均
  • np.median(): 中央値
  • np.std(): 標準偏差
  • np.min(), np.max(): 最小値、最大値
  • np.argmin(), np.argmax(): 最小値・最大値のインデックス

これらの関数は、`axis` 引数を指定することで、特定の軸に沿って計算することも可能です。

arr2d = np.array([[1, 2, 3], [4, 5, 6]])

print(np.sum(arr2d))         # 配列全体の合計
print(np.sum(arr2d, axis=0)) # 列ごとの合計
print(np.sum(arr2d, axis=1)) # 行ごとの合計

行列演算

NumPyは、線形代数演算のための機能も備えています。

  • np.dot(a, b) または a @ b (Python 3.5以降): 行列積
  • np.linalg.inv(a): 逆行列
  • np.linalg.det(a): 行列式
  • np.linalg.eig(a): 固有値と固有ベクトル
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])

print(np.dot(A, B)) # 行列積
print(A @ B)        # 行列積 (Python 3.5+)

ブロードキャスティング

NumPyのブロードキャスティングは、形状の異なる配列間で演算を行う際に、自動的に形状を拡張して互換性を持たせる機能です。これにより、明示的なループを書くことなく、効率的な演算が可能になります。

arr = np.array([[1, 2, 3], [4, 5, 6]])
scalar = 10

print(arr + scalar) # スカラー値が配列の各要素に加算される

row_vec = np.array([1, 2, 3])
print(arr + row_vec) # row_vec が各行にブロードキャストされる

高度なトピックと応用

NumPyは、さらに複雑な操作や応用にも対応しています。

配列の形状変更

  • reshape(shape): 配列の形状を変更します。
  • flatten(): 配列を1次元に平坦化します。
  • ravel(): `flatten()` と似ていますが、必要に応じてビューを返すことがあります。
arr = np.arange(1, 7)
reshaped_arr = arr.reshape((2, 3))
print(reshaped_arr)

flattened_arr = reshaped_arr.flatten()
print(flattened_arr)

配列の結合と分割

  • np.concatenate((a1, a2, ...), axis=0): 配列を結合します。
  • np.vstack((a1, a2, ...)): 垂直方向(行方向)に結合します。
  • np.hstack((a1, a2, ...)): 水平方向(列方向)に結合します。
  • np.split(a, indices_or_sections, axis=0): 配列を分割します。
a = np.array([1, 2])
b = np.array([3, 4])
print(np.concatenate((a, b))) # [1 2 3 4]

c = np.array([[1, 2]])
d = np.array([[3, 4]])
print(np.vstack((c, d))) # [[1 2] [3 4]]

ブロードキャストの応用例

ブロードキャスティングは、データ正規化や、ある配列の各要素に別の配列の要素を適用する際などに非常に役立ちます。

ファイル入出力

NumPyは、配列をファイルに保存・読み込みする機能も提供します。

  • np.save(file, arr): 配列を `.npy` 形式で保存
  • np.load(file): `.npy` ファイルから配列を読み込み
  • np.savetxt(file, arr): 配列をテキスト形式で保存
  • np.loadtxt(file): テキストファイルから配列を読み込み

まとめ

NumPyは、Pythonで数値計算を行う上で避けては通れない強力なライブラリです。その中心となるndarrayは、効率的かつ柔軟なデータ操作と高速な計算を可能にします。配列の生成、インデックス・スライシング、要素ごとの演算、集約関数、行列演算、そしてブロードキャスティングといった機能は、データサイエンスや機械学習のあらゆる場面で活用されます。NumPyを深く理解することは、これらの分野での成果を最大化するための第一歩と言えるでしょう。