NumPy

【NumPy】ブロードキャスト(broadcast)の基本

【NumPy】ブロードキャスト(broadcast)の基本
naoki-hn

NumPy の特徴的な機能であるブロードキャスト(broadcast)の基本的な考え方を紹介します。

ブロードキャスト(broadcast)の基本

NumPy の特徴的な機能としてブロードキャスト(broadcast)という機能があります。ブロードキャストは、NumPy が高速にベクトル演算するユニバーサル関数(ufunc)の土台となる機能です。

ユニバーサル関数は、例えば、np.add や np.multiply というような関数のことです。NumPy 配列(ndarray)の +* 演算子は、内部でそれらの ufunc を呼び出す糖衣構文となっています。

この記事では、ブロードキャスト(broadcast)の基本について説明します。

NumPy のユニバーサル関数(ufunc)については「ユニバーサル関数(ufuncs)を用いた配列(ndarray)の計算」を参考にしてください。

ブロードキャスト(broadcast)の基本的な考え方

ブロードキャストの基本的な考え方について、簡単な例を使って見ていきましょう。

ユニバーサル関数のベクトル計算

ユニバーサル関数(ufunc)のベクトル計算のイメージを説明します。以下は同じサイズの NumPy の配列(ndarray)を足し算するプログラムです。

import numpy as np

x1 = np.arange(1, 6)
print(f'x1 = {x1}')
x2 = np.arange(6, 11)
print(f'x2 = {x2}')

print(f'x1 + x2 = {x1 + x2}')
【実行結果】
x1 = [1 2 3 4 5]
x2 = [ 6  7  8  9 10]
x1 + x2 = [ 7  9 11 13 15]

ユニバーサル関数では、ベクトル演算をするため、各要素ごとに演算が行われます。イメージ図にしてみると以下のようになります。今回は足し算(+)の例ですが、他の演算でも同様です。

ユニバーサル関数(ufunc) ベクトル計算

ブロードキャスト例(1 次元)

上記で説明した例は、配列の形状が一致している場合でした。配列の形状(shape)が異なる場合について見てみましょう。以下は配列と数値の足しているプログラムです。このような場合に、ブロードキャストの機能が動作します。

import numpy as np

x1 = np.arange(1, 6)
print(f'x1 = {x1}')

print(f'x1 + 5 = {x1 + 5}')
【実行結果】
x1 = [1 2 3 4 5]
x1 + 5 = [ 6  7  8  9 10]

結果を見ると、x1 の各要素にそれぞれ 5 が足し算されていることが分かります。この時、NumPy は 5 という数字を拡張して、x1 と同じ形状の配列にしてからベクトル計算をします。イメージにすると以下のようになります。

このように NumPy が内部的に形状を拡張して対応することを「ブロードキャスト」と言います。

ブロードキャスト(broadcast)1次元

ブロードキャスト例(2 次元:行方向)

2 次元配列の場合も見てみましょう。以下の例は、np.eye で 5 × 5 の単位行列を作成しています。足し算するのは、1 次元の 1 ~ 5 までの数字が入った配列です。

import numpy as np

x1 = np.eye(5)
print(f'x1 = \n{x1}')
x2 = np.arange(1, 6)
print(f'x2 = {x2}')

print(f'x1 + x2 = \n{x1 + x2}')
【実行結果】
x1 = 
[[1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 1.]]
x2 = [1 2 3 4 5]
x1 + x2 = 
[[2. 2. 3. 4. 5.]
 [1. 3. 3. 4. 5.]
 [1. 2. 4. 4. 5.]
 [1. 2. 3. 5. 5.]
 [1. 2. 3. 4. 6.]]

例では、行方向のサイズが異なっているため、x2 の配列を行方向に拡張し、x1 と同じ形状にして計算がされます。イメージは、以下のようになります。

ブロードキャスト(broadcast)2次元 行方向

ブロードキャスト例(2 次元:列方向)

列方向に拡張される例も見てみましょう。

import numpy as np

x1 = np.eye(5)
print(f'x1 = \n{x1}')
x2 = np.arange(1, 6)[:, np.newaxis]
print(f'x2 = {x2}')

print(f'x1 + x2 = \n{x1 + x2}')
【実行結果】
x1 = 
[[1. 0. 0. 0. 0.]
 [0. 1. 0. 0. 0.]
 [0. 0. 1. 0. 0.]
 [0. 0. 0. 1. 0.]
 [0. 0. 0. 0. 1.]]
x2 = [[1]
 [2]
 [3]
 [4]
 [5]]
x1 + x2 = 
[[2. 1. 1. 1. 1.]
 [2. 3. 2. 2. 2.]
 [3. 3. 4. 3. 3.]
 [4. 4. 4. 5. 4.]
 [5. 5. 5. 5. 6.]]

例では、列方向のサイズが異なっているため、x2 の配列を列方向に拡張して、x1 と同じ形状にして計算がされます。イメージは、以下のようになります。

ブロードキャスト(broadcast)2次元 列方向

ブロードキャスト(broadcast)のルール

これまで見てきた例は非常にシンプルな例となっていますが、NumPy の高速な演算を支えるブロードキャストという仕組みの概要です。より詳細にはブロードキャストの具体的なルールがありますが、この記事はブロードキャストのイメージを持ってもらうことに留めようかと思います。

より詳細なブロードキャストのルールに興味がある方は、公式ドキュメントのこちらを参考にしてください。

まとめ

NumPy の特徴的な機能であるブロードキャスト(broadcast)の基本的な考え方を紹介しました。

ブロードキャストは、NumPy の np.addnp.multiply といったユニバーサル関数(ufunc)が、内部的に形状を拡張して演算に対応することを言います。

ブロードキャストはNumPyの高速演算を支える重要な考え方の 1 つです。NumPy のユニバーサル関数(ufunc)ではブロードキャストという機能で内部的に形状を拡張して対応していることを意識してみてください。

あわせて読みたい
【Python Tech】プログラミングガイド
【Python Tech】プログラミングガイド
ABOUT ME
ホッシー
ホッシー
システムエンジニア
はじめまして。当サイトをご覧いただきありがとうございます。 私は製造業のメーカーで、DX推進や業務システムの設計・開発・導入を担当しているシステムエンジニアです。これまでに転職も経験しており、以前は大手電機メーカーでシステム開発に携わっていました。

プログラミング言語はこれまでC、C++、JAVA等を扱ってきましたが、最近では特に機械学習等の分析でも注目されているPythonについてとても興味をもって取り組んでいます。これまでの経験をもとに、Pythonに興味を持つ方のお役に立てるような情報を発信していきたいと思います。どうぞよろしくお願いいたします。

※キャラクターデザイン:ゼイルン様
記事URLをコピーしました