概要
NumPy 配列の各要素に関数を適用する apply_along_axis、apply_over_axes、vectorize について解説します。
numpy.apply_along_axis
指定した軸に沿って1次元配列を引数にとる関数を適用します。
numpy.apply_along_axis(func1d, axis, arr, *args, **kwargs)
名前 | 型 | デフォルト値 |
---|---|---|
func1d | function (M,) -> (Nj…) | |
この関数は1次元配列の引数を取らなければなりません。指定された軸に沿って arr をスライスしてできた部分配列が関数に渡されます。 | ||
axis | integer | |
arr がスライスされる軸。 | ||
arr | ndarray (Ni…, M, Nk…) | |
入力配列。 | ||
args | any | |
func1d への追加の引数。 | ||
kwargs | any | |
func1d への追加の名前付き引数。 |
名前 | 説明 |
---|---|
out | 出力配列。 |
out
の形状
入力配列の形状を arr.shape
、func1d
の返り値の形状が f
である場合、出力配列 out
の形状は arr.shape[:axis] + f + arr.shape[axis + 1:]
で計算されます。例えば、func1d
がスカラーを返す場合は次元が1つ減ります。
- 例1: スカラーを返す場合
arr.shape
:(d1, d2, d3)
axis
: 1func1d
:(d2,) -> ()
out.shape
:(d1, d3)
- 例2: 配列を返す場合
arr.shape
:(d1, d2, d3)
axis
: 1func1d
:(d2,) -> (e1, e2)
out.shape
:(d1, e1, e2, d3)
使い道として、2次元配列の行ごと、列ごとに関数を適用したい場合に使用します。
import numpy as np
np.random.seed(0)
a = np.random.rand(10, 10)
normalize = lambda x: x / np.linalg.norm(x)
# axis=0 に沿ってノルムが1になるように正規化する。
b = np.apply_along_axis(normalize, 0, a)
print(b.shape)
print(np.linalg.norm(b, axis=0)) # ノルムが1になっていることを確認
# axis=1 に沿ってノルムが1になるように正規化する。
b = np.apply_along_axis(normalize, 1, a)
print(np.linalg.norm(b, axis=1)) # ノルムが1になっていることを確認
(10, 10) [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.] [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
numpy.apply_over_axes
指定した複数の軸 axes
に沿って関数を繰り返し適用します。
func
は res = func(a, axis)
のように呼ばれます。関数の返り値 res
の形状は、a
と同じ形状か、1次元少ない形状でなければなりません。res
が a
より1次元少ない場合、axis
の前にサイズ1の次元が挿入されます。その後、func
への呼び出しは、axes
の各軸に対して、res
を第1引数として繰り返します。詳しくは以下のサンプルコードを見てください。
numpy.apply_over_axes(func, a, axes)
名前 | 型 | デフォルト値 |
---|---|---|
func | function | |
この関数は func(a, axis) の2つの引数を取らなければなりません。 | ||
a | array_like | |
入力配列。 | ||
axes | array_like | |
func が適用される軸。要素は整数でなければなりません。 |
名前 | 説明 |
---|---|
apply_over_axis | 出力配列。次元の数は a と同じですが、形状は異なることがあります。これは、func が入力に対して出力の形を変えるかどうかに依存します。 |
a = np.arange(24).reshape(2, 3, 4)
print(a)
b = np.apply_over_axes(np.sum, a, (0, 2))
print(b)
[[[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] [[12 13 14 15] [16 17 18 19] [20 21 22 23]]] [[[ 60] [ 92] [124]]]
これは以下のように動作しています。
1 res = np.sum(a, axis=0)
a.shape: (10, 10, 10)
[[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
[[12 13 14 15]
[16 17 18 19]
[20 21 22 23]]]
↓
res.shape: (10, 10)
[[12 14 16 18]
[20 22 24 26]
[28 30 32 34]]
↓ axis=0 の前にサイズ1の次元が挿入される
res.shape: (1, 10, 10)
2 res = np.sum(res, axis=2)
res.shape: (1, 10, 10)
[[12 14 16 18]
[20 22 24 26]
[28 30 32 34]]
↓
res.shape: (1, 10)
[[ 60 92 124]]
↓ axis=2 の前にサイズ1の次元が挿入される
res.shape: (1, 10, 1)
numpy.vectorize
指定した関数から配列を引数に取ることができる関数を作成します。
class numpy.vectorize(pyfunc, otypes=None, doc=None, excluded=None, cache=False, signature=None)
名前 | 型 | デフォルト値 |
---|---|---|
pyfunc | callable | |
python の関数またはメソッド。 | ||
otypes | str list of dtypes | None |
出力データ型。型を表す文字の文字列またはデータ型のリストのいずれかで指定する必要があります。各出力に対して、1つのデータ型指定子を指定する必要があります。 | ||
doc | str | None |
関数の docstring。None の場合、docstring は pyfunc.__doc__ となります。 | ||
excluded | set | None |
関数の位置引数やキーワード引数を表す文字列や整数の一覧。これらは、変更されずに直接 pyfunc に渡されます。 | ||
cache | bool | False |
True の場合、otypes が提供されていない場合、出力数を決定する最初の関数呼び出しをキャッシュします。 | ||
signature | string | None |
関数の入出力を表す表記。デフォルトでは、pyfunc はスカラを入出力として受け取ることを前提としています。 |
名前 | 説明 |
---|---|
vectorized | ベクトル化された関数。 |
標準関数 bin()
を配列の各要素に適用することを考えます。
そのまま配列を渡しても、関数が配列を受け取るように対応していないのでエラーとなります。
bin([1, 2, 3, 4, 5])
このような場合に、numpy.vectorize()
を使うことでスカラーしか受け取れない関数から配列を受け取って処理できる関数を作成することができます。
otypes の指定
関数の返り値に対応する numpy の型の一覧を otypes
に指定します。
- 例: 返り値が str 型の1つの場合:
otypes=[np.object]
またはotypes="O"
- 例: 返り値が str 型の返り値2つの場合:
otypes=[np.object, np.object]
またはotypes="OO"
func = np.vectorize(bin, otypes=[np.object])
print(func([1, 2, 3, 4, 5]))
['0b1' '0b10' '0b11' '0b100' '0b101']
返り値が複数の場合はそれぞれに対応する numpy の型を otypes
に指定します。
myfunc = lambda x: (hex(x), oct(x), bin(x))
func = np.vectorize(myfunc, otypes="OOO")
print(func([1, 2]))
(array(['0x1', '0x2'], dtype=object), array(['0o1', '0o2'], dtype=object), array(['0b1', '0b10'], dtype=object))
excluded の指定
指定した関数に直接渡す位置引数やキーワード引数を excluded
に指定します。
以下の例では、excluded=[1]
を指定して2個目の位置引数 a
を指定して、ベクトル化した関数を呼び出すときに直接渡しています。
myfunc = lambda x, a: x + a
func = np.vectorize(myfunc, otypes=[float], excluded=[1])
print(func([1, 2, 3, 4, 5], 5))
[ 6. 7. 8. 9. 10.]
キーワード引数の場合も同様です。
myfunc = lambda x, a, b: a * x + b
func = np.vectorize(myfunc, otypes=[float], excluded=["a", "b"])
print(func([1, 2, 3, 4, 5], a=3, b=2))
[ 5. 8. 11. 14. 17.]
signature の指定
Details of Signature に記載がある表記法で関数の入出力を規定します。なにも指定しなかった場合、スカラーを受け取り、スカラーを返す関数を仮定します。
指定方法の例
名前 | 説明 |
---|---|
加算 | (),()->() |
総和 | (i)->() |
内積 | (i),(i)->() |
行列積 | (m,n),(n,p)->(m,p) |
ベクトル行列積 | (n),(n,p)->(p) |
行列ベクトル積 | (m,n),(n)->(m) |
上記4つのいずれか | (m?,n),(n,p?)->(m?,p?) |
外積 | (i,t),(j,t)->(i,j) |
クロス積 | (3),(3)->(3) |
myfunc = lambda x: x * np.arange(5)
func = np.vectorize(myfunc, signature="()->(n)")
print(func([1, 2, 3, 4, 5]))
[[ 0 1 2 3 4] [ 0 2 4 6 8] [ 0 3 6 9 12] [ 0 4 8 12 16] [ 0 5 10 15 20]]
numpy.frompyfunc
指定した関数から配列を引数に取ることができる NumPy ufunc を作成します。numpy.vectorize()
でスカラーを引数にとり、スカラーを返す関数を指定した場合と同様のことができますが、この場合はこちらの関数のほうが高速に動作します。
numpy.frompyfunc(func, nin, nout)
名前 | 型 | デフォルト値 |
---|---|---|
func | Python function object | |
任意の関数。 | ||
nin | int | |
入力引数の数。 | ||
nout | int | |
func によって返される出力の数。 |
名前 | 説明 |
---|---|
out | NumPy ufunc オブジェクト |
func = np.frompyfunc(hex, 1, 1)
print(func([10, 11, 12, 13, 14]))
['0xa' '0xb' '0xc' '0xd' '0xe']
コメント