【NumPy入門】np.vectorizeでPython関数を簡単にユニバーサル関数化!

こんにちは!インストラクターのフクロウです!NumPyにはホントにたくさんの関数があります。これを全部使いこなすのは少し難しいですし、NumPyの機能だけでは実装できない処理もあるはずです。

そんなときに役に立つのがnp.vectorize関数。Pythonの関数を簡単にNumPyの関数にしてくれます!この記事で使い方を学んで使ってみてください!

目次

np.vectorizeの使い方

np.vectorizeの基本的な使い方

np.vectorizeは、Pythonの関数を配列に適用できるように変換する関数です。NumPyにおいて、配列のすべての要素に一括で処理を適用できる関数のことをユニバーサル関数といいます。

np.vectorizeでユニバーサル関数にできる関数は、スカラー値や文字列が引数になっているものです。では、Pythonで実装したsigmoid関数を、np.vectorizeでユニバーサル関数化してみましょう。

import numpy as np
import math

# ①ターゲットのPython関数
def py_sigmoid(x):
    return 1/ (1+math.exp(-x))

# ②forループでユニバーサル関数っぽくしたもの
def vpy_sigmoid(X):
    result = np.zeros_like(X)
    for i in range(len(X)):
        result[i] = py_sigmoid(X[i])
    return result

# ③np.vectorizeでユニバーサル関数を作ったもの
vsigmoid = np.vectorize(py_sigmoid)

# ④NumPy関数で作ったユニバーサル関数
def np_sigmoid(x):
    return 1/ (1+np.exp(-x))

①はスカラーを受け取って、スカラーを返す関数です。

>>> X = np.linspace(-10,10, 10)

>>> py_sigmoid(X)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-a145f0f92b5a> in <module>()
----> 1 py_sigmoid(X)

<ipython-input-2-bcf4ceeeadd2> in py_sigmoid(x)
      1 def py_sigmoid(x):
----> 2     return 1/ (1+math.exp(-x))
      3 
      4 def vpy_sigmoid(X):
      5     result = np.zeros_like(X)

TypeError: only size-1 arrays can be converted to Python scalars

この関数を配列のすべての要素に適用するには、普通に考えるとfor文を使うことになります。それが②ですが、np.vectorizeを使うと一行で再現することができます。

では、これらの関数がちゃんと動作するか見てみましょう。

>>> vpy_sigmoid(X)

array([4.53978687e-05, 4.18766684e-04, 3.85103236e-03, 3.44451957e-02,
       2.47663801e-01, 7.52336199e-01, 9.65554804e-01, 9.96148968e-01,
       9.99581233e-01, 9.99954602e-01])

>>> vsigmoid(X)

array([4.53978687e-05, 4.18766684e-04, 3.85103236e-03, 3.44451957e-02,
       2.47663801e-01, 7.52336199e-01, 9.65554804e-01, 9.96148968e-01,
       9.99581233e-01, 9.99954602e-01])

>>> np_sigmoid(X)

array([4.53978687e-05, 4.18766684e-04, 3.85103236e-03, 3.44451957e-02,
       2.47663801e-01, 7.52336199e-01, 9.65554804e-01, 9.96148968e-01,
       9.99581233e-01, 9.99954602e-01])

どの書き方の関数でも、同様にすべての要素にsigmoid関数の計算ができていることがわかりますね。今回はsigmoidなのでNumPyの機能だけでキレイに書き下せますが、難しい場合には非常に便利ですね。

何よりfor文を書かずにNumPyっぽくプログラムがかけるのがいいですね!

np.vectorizeの速度比較

さて、Pythonの関数を気軽にユニバーサル関数にできるnp.vectorizeですが、実行速度はどうでしょうか。ipythonやjupyterなどの環境で、プログラムの実行時間を測る%timeit機能を使って測ってみましょう!

sample = np.linspace(-100,100, 1000)

%timeit vpy_sigmoid(sample)
%timeit vsigmoid(sample)
%timeit np_sigmoid(sample)

[Output]

357 µs ± 2.84 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
244 µs ± 2.73 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
15.3 µs ± 170 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

for文で書いたvpy_sigmoidよりは、<np.vectorizeで書いたvsigmoidの方が速いですね!でも実は、すべてNumPy関数で書いたものは桁違いの速さ(15.3 µs )です。

NumPyでかける関数はできるだけNumPyで書いてしまって、速度があまり必要でない関数やNumPyで書くのが難しい関数はnp.vectorizeで書くと良さそうです。

まとめ

この記事では、Python関数を簡単にNumPyのユニバーサル関数にする機能、np.vectorize関数を紹介しました。NumPyの関数は独特なので、最初から使いこなすのは難しいかもしれません。

そんなとき、np.vectorize関数を使うことで、手軽に欲しい機能が実装できます!簡単なので是非試して見てくださいね!

この記事を書いた人

第一言語はPythonです。
皆さんRustやりましょう。

目次