はじめに
この実験では、NumPy のユニバーサル関数(一般に ufuncs と呼ばれます)の基本を学びます。ufuncs は、Python における高性能コンピューティングの礎であり、データ全体の配列に対して高速な要素ごとの操作を実行できます。基本的な算術演算、強力なブロードキャスティングの概念、集計メソッド、および結果のデータ型を制御する方法について説明します。この実験の終わりには、ufuncs を使用して、よりクリーンで効率的なデータ処理コードを作成できるようになります。
この実験では、NumPy のユニバーサル関数(一般に ufuncs と呼ばれます)の基本を学びます。ufuncs は、Python における高性能コンピューティングの礎であり、データ全体の配列に対して高速な要素ごとの操作を実行できます。基本的な算術演算、強力なブロードキャスティングの概念、集計メソッド、および結果のデータ型を制御する方法について説明します。この実験の終わりには、ufuncs を使用して、よりクリーンで効率的なデータ処理コードを作成できるようになります。
ufuncs の核となるのは要素ごとの操作です。これは、2 つの配列に操作を適用すると、対応する各要素のペアに対して操作が実行されることを意味します。最も一般的な ufuncs は、+、-、*、/ のような標準的な算術演算子です。
まず、2 つの NumPy 配列に対して簡単な加算を実行してみましょう。
まず、左側のファイルエクスプローラーから ufunc_examples.py ファイルを開きます。既存の内容を以下のコードに置き換えてください。このコードは NumPy をインポートし、2 つの配列を作成して、それらを加算します。
import numpy as np
## 2 つの配列を作成
arr1 = np.array([0, 2, 3, 4])
arr2 = np.array([1, 1, -1, 2])
## '+' 演算子は配列を要素ごとに加算する ufunc です
result = arr1 + arr2
## 結果を表示
print("Step 1 Result:")
print(result)
コードを追加した後、ファイルを保存します。次に、ターミナルからスクリプトを実行して出力を確認します。
python ufunc_examples.py
要素ごとの加算の結果が表示されるはずです。
Step 1 Result:
[1 3 2 6]
これは、ufunc の基本的な動作を示しています。arr1[0] が arr2[0] に加算され、arr1[1] が arr2[1] に加算されるなどして、結果を含む新しい配列が生成されます。
ブロードキャスティングは、算術演算中に NumPy が異なる形状の配列を扱うことを可能にする強力なメカニズムです。内部的には、NumPy は互換性のある形状を持つように、小さい方の配列を大きい方の配列に「ブロードキャスト」します。
一般的な例は、配列の各要素を単一の数値で乗算することです。また、1D 配列が 2D 配列にブロードキャストされる、より複雑なケースも見てみましょう。
ufunc_examples.py ファイルを変更します。スクリプトの末尾に以下のコードを追加してください。
## --- ステップ 2 用に追加されたコード ---
## スカラー値を配列にブロードキャスト
arr1 = np.array([1, 2, 3])
scalar_result = arr1 * 10
print("\nStep 2 Result (Scalar Broadcast):")
print(scalar_result)
## 1D 配列を 2D 配列にブロードキャスト
arr2d = np.array([[1], [2], [3]]) ## Shape (3, 1)
arr1d = np.array([1, 2, 3]) ## Shape (3,)
broadcast_result = arr2d * arr1d
print("\nStep 2 Result (Array Broadcast):")
print(broadcast_result)
ファイルを保存し、ターミナルから再度実行します。
python ufunc_examples.py
ステップ 1 とステップ 2 の両方の出力が表示されます。
Step 1 Result:
[1 3 2 6]
Step 2 Result (Scalar Broadcast):
[10 20 30]
Step 2 Result (Array Broadcast):
[[1 2 3]
[2 4 6]
[3 6 9]]
2 番目の例では、1D 配列 arr1d (形状 (3,)) と 2D 配列 arr2d (形状 (3, 1)) が、要素ごとの乗算が行われる前に、共通の形状 (3, 3) にブロードキャストされます。
.reduce() による配列の集計ufuncs は要素ごとの操作に加えて、集計を実行するための特別なメソッドを持っています。.reduce() メソッドは最も有用なものの一つです。これは、指定された配列の軸に沿って ufunc を繰り返し適用し、最終的に 1 つの次元だけが残るようにします。
例えば、np.add.reduce(arr) は np.sum(arr) と同等です。これが 2D 配列でどのように機能するかを見てみましょう。
ufunc_examples.py ファイルに以下のコードを追加してください。
## --- ステップ 3 用に追加されたコード ---
## 3x3 の配列を作成
arr = np.arange(9).reshape(3, 3)
print("\nStep 3 Original Array:")
print(arr)
## axis=1 (列) に沿って配列を集計(加算)
## これにより、各行の要素が合計されます。
## 行 0 の場合:0 + 1 + 2 = 3
## 行 1 の場合:3 + 4 + 5 = 12
## 行 2 の場合:6 + 7 + 8 = 21
reduced_result = np.add.reduce(arr, axis=1)
print("\nStep 3 Result (reduce on axis=1):")
print(reduced_result)
ファイルを保存して実行します。
python ufunc_examples.py
出力には、このステップの結果も含まれるようになります。
... (前の出力) ...
Step 3 Original Array:
[[0 1 2]
[3 4 5]
[6 7 8]]
Step 3 Result (reduce on axis=1):
[ 3 12 21]
ご覧のとおり、.reduce() は指定された軸に沿って配列を折りたたみ、その要素に add 操作を適用しました。
NumPy は通常、出力配列のデータ型を自動的に決定します。しかし、dtype 引数を使用して出力データ型を明示的に指定することができます。これは、メモリ使用量を制御したり、数値精度を確保したりする場合に役立ちます。
乗算を使用してリダクションを実行し、入力が整数配列であっても、出力を浮動小数点数に強制してみましょう。
ufunc_examples.py の末尾に以下のコードを追加してください。
## --- ステップ 4 用に追加されたコード ---
## ステップ 3 と同じ 3x3 配列を使用
arr = np.arange(1, 10).reshape(3, 3) ## ゼロによる乗算を避けるため 1-9 を使用
print("\nStep 4 Original Array:")
print(arr)
## 乗算でリダクションを実行し、出力を float にキャスト
## 行 0 の場合:1 * 2 * 3 = 6
## 行 1 の場合:4 * 5 * 6 = 120
## 行 2 の場合:7 * 8 * 9 = 504
multiply_result = np.multiply.reduce(arr, axis=1, dtype=float)
print("\nStep 4 Result (multiply.reduce with dtype=float):")
print(multiply_result)
スクリプトを保存して実行します。
python ufunc_examples.py
ステップ 4 の出力を確認します。
... (前の出力) ...
Step 4 Original Array:
[[1 2 3]
[4 5 6]
[7 8 9]]
Step 4 Result (multiply.reduce with dtype=float):
[ 6. 120. 504.]
出力配列 [ 6. 120. 504.] の末尾にあるドット (.) に注意してください。これは、dtype=float で指定したように、要素が浮動小数点数になったことを示しています。
NumPy の ufunc システムは拡張可能です。ufunc がどのように操作されるかを定義する独自の配列ライクなオブジェクトを作成できます。これは通常、NumPy の ndarray をサブクラス化し、__add__ ( + 演算子用) のような特殊メソッドをオーバーライドすることによって行われる高度な機能です。
加算が実行されるたびにメッセージを表示する簡単なカスタム配列クラスを作成してみましょう。
この最後のコードブロックを ufunc_examples.py に追加してください。
## --- ステップ 5 用に追加されたコード ---
## np.ndarray をサブクラス化してカスタム配列クラスを定義
class MyArray(np.ndarray):
def __add__(self, other):
print("\nStep 5: Custom add method called!")
## 親クラスの元の実装を呼び出す
return super().__add__(other)
## カスタムクラスのインスタンスを作成
## ndarray をカスタムクラスにキャストするには .view() を使用する必要があります
my_arr = np.array([10, 20, 30]).view(MyArray)
## 加算を実行すると、カスタムメソッドがトリガーされます
override_result = my_arr + 5
print("Step 5 Result (Overridden Ufunc):")
print(override_result)
ファイルを保存して、もう一度実行します。
python ufunc_examples.py
最終的な出力を確認します。
... (前の出力) ...
Step 5: Custom add method called!
Step 5 Result (Overridden Ufunc):
[15 25 35]
加算結果の前にカスタムメッセージが表示されていることがわかります。これは、__add__ メソッドが呼び出されたことを確認するものです。これは、ufunc システムの強力な柔軟性を示しています。
この実験では、NumPy のユニバーサル関数 (ufunc) の基本を学びました。ベクトル化された計算の基礎となる基本的な要素ごとの算術演算から始めました。次に、NumPy が異なる形状の配列に対して操作を実行できるようにする主要な機能であるブロードキャスティングを探索しました。また、データ集計のための .reduce() のような ufunc メソッドの使用方法や、dtype 引数を使用した出力データ型の制御方法についても説明しました。最後に、np.ndarray をサブクラス化することによって ufunc の動作をカスタマイズする方法の高度な例を示しました。これらのスキルにより、NumPy を使用して効率的で読みやすく強力な数値コードを作成できるようになります。