numpy.vectorize
は関数f:a-> bを受け取り、それをg:a []-> b []に変換します。
a
とb
がスカラーの場合、これは正常に機能しますが、bをndarray
またはリスト、つまりfとして機能しない理由は考えられません。 :a-> b []およびg:a []-> b [] []
例えば:
import numpy as np
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
print(g(a))
これにより、
array([[ 0. 0. 0. 0. 0.],
[ 1. 1. 1. 1. 1.],
[ 2. 2. 2. 2. 2.],
[ 3. 3. 3. 3. 3.]], dtype=object)
OK、それで正しい値が得られますが、dtypeが間違っています。さらに悪いことに:
g(a).shape
利回り:
(4,)
したがって、この配列はほとんど役に立ちません。私はそれを変換することができることを知っています:
np.array(map(list, a), dtype=np.float32)
私が欲しいものを与えるために:
array([[ 0., 0., 0., 0., 0.],
[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.]], dtype=float32)
しかし、それは効率的でもPythonicでもありません。これを行うためのよりクリーンな方法を見つけられる人はいますか?
前もって感謝します!
np.vectorize
は便利な関数です。実際には コードをより速く実行する ではありません。 np.vectorize
を使用するのが不都合な場合は、希望どおりに機能する独自の関数を記述してください。
np.vectorize
の目的は、numpyを認識しない関数(たとえば、floatを入力として受け取り、floatを出力として返す)を、numpy配列を操作(および返す)できる関数に変換することです。
関数f
はすでにnumpyに対応しています-定義でnumpy配列を使用し、numpy配列を返します。したがって、np.vectorize
は、ユースケースに適していません。
したがって、解決策は、あなたが望む方法で機能する独自の関数f
をロールすることです。
1.12.0の新しいパラメーターsignature
は、まさにあなたが何をしているかを実行します。
_def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, signature='()->(n)')
_
次に、g(np.arange(4)).shape
は_(4L, 5L)
_を返します。
ここでは、f
の署名が指定されています。 _(n)
_は戻り値の形状であり、_()
_はスカラーのパラメーターの形状です。また、パラメーターも配列にすることができます。より複雑なシグネチャについては、「 一般化ユニバーサル関数API 」を参照してください。
import numpy as np
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
b = g(a)
b = np.array(b.tolist())
print(b)#b.shape = (4,5)
c = np.ones((2,3,4))
d = g(c)
d = np.array(d.tolist())
print(d)#d.shape = (2,3,4,5)
これで問題が解決し、入力のサイズに関係なく機能します。 「マップ」は、1つの次元の入力に対してのみ機能します。 「.tolist()」を使用して新しいndarrayを作成すると、問題がより完全かつ適切に解決されます(私は信じています)。お役に立てれば。
私は関数を書きました、それはあなたのニーズに合っているようです。
def amap(func, *args):
'''array version of build-in map
amap(function, sequence[, sequence, ...]) -> array
Examples
--------
>>> amap(lambda x: x**2, 1)
array(1)
>>> amap(lambda x: x**2, [1, 2])
array([1, 4])
>>> amap(lambda x,y: y**2 + x**2, 1, [1, 2])
array([2, 5])
>>> amap(lambda x: (x, x), 1)
array([1, 1])
>>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4])
array([[1, 9], [4, 16]])
'''
args = np.broadcast(None, *args)
res = np.array([func(*arg[1:]) for arg in args])
shape = args.shape + res.shape[1:]
return res.reshape(shape)
やってみよう
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
amap(f, np.arange(4))
アウトプット
array([[ 0., 0., 0., 0., 0.],
[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.]], dtype=float32)
便宜上、ラムダまたはパーシャルでラップすることもできます
g = lambda x:amap(f, x)
g(np.arange(4))
vectorize
のドキュメント文字列に注目してください
vectorize
関数は、パフォーマンスのためではなく、主に便宜上提供されています。実装は基本的にforループです。
したがって、ここでのamap
はvectorize
と同様のパフォーマンスを期待します。私はそれをチェックしませんでした、どんなパフォーマンステストも歓迎です。
パフォーマンスが本当に重要な場合は、他のことを検討する必要があります。純粋なループを回避するためのreshape
とbroadcast
を使用した直接配列計算python(vectorize
とamap
の両方が後である場合)。
関数をベクトル化したい
import numpy as np
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
結果として単一のnp.float32
配列を取得したい場合、これをotype
として指定する必要があります。あなたの質問では、otypes=[np.ndarray]
を指定しましたが、これは、すべての要素をnp.ndarray
にすることを意味します。したがって、dtype=object
の結果を正しく取得できます。
正しい呼び出しは
np.vectorize(f, signature='()->(n)', otypes=[np.float32])
ただし、そのような単純な関数の場合、numpy
のufunctionを利用する方が適切です。 np.vectorize
はループします。したがって、あなたの場合は関数を次のように書き直してください
def f(x):
return np.multiply.outer(x, np.array([1,1,1,1,1], dtype=np.float32))
これはより高速で、不明瞭なエラーが少なくなります(ただし、複素数または4倍精度の数値を渡す場合、結果dtype
はx
に依存するため、結果も同様です)。
これを解決する最良の方法は、2-D NumPy配列(この場合は列配列)をoriginal関数への入力として使用することです。結果はあなたが期待していたと思います。
コードでは次のようになります。
import numpy as np
def f(x):
return x*np.array([1, 1, 1, 1, 1], dtype=np.float32)
a = np.arange(4).reshape((4, 1))
b = f(a)
# b is a 2-D array with shape (4, 5)
print(b)
これは、操作を完了するためのはるかに簡単でエラーが発生しにくい方法です。このメソッドは、numpy.vectorizeを使用して関数を変換しようとするのではなく、配列をブロードキャストするNumPyの自然な機能に依存します。トリックは、少なくとも1つの次元が配列間で同じ長さであることを確認することです。