NumPyには、数値計算を目的としたライブラリであるため、あらかじめ数学関数が数多く用意されています。NumPyの数学関数を知っていると、自分で実装する手間が省けます。

そこで、この記事を読めば一通りよく使う関数を把握することができるように、NumPyの数学関数を中心に使い方を紹介します。

四則演算

まずは基本的な四則演算を紹介します。四則演算はPythonにある+,-,+,/の4つの演算子を適用することができます。この場合、配列の各要素に対して演算が行われることになります。

どのshape同士のndarrayを計算することができるかは、NumPyのブロードキャストで詳しく解説しているので、以下の記事を参考にしてください。

NumPyのブロードキャストのメリットと解説 /features/numpy-broadcasting.html

四則演算に相当するNumPyの関数(np.add()など)がありますが、ndarrayの配列操作であることを明示したい場合以外にはあまり使用されません。

足し算(np.add)

足し算です。特別な関数とかはなく、演算子の+を用います。配列の要素同士の足し算ができるだけでなく、配列の各要素に同じ値を加算することも可能です。

In [1]: import numpy as np

In [2]: a = np.array([0, 1, 2, 3, 4])

In [3]: b = np.array([2, 4, 6, 8, 10])

In [4]: a + b # 配列同士を足し合わせると各々の要素の和が返ってくる。
Out[4]: array([ 2,  5,  8, 11, 14])

In [5]: a + 4 # 各要素に4を足す
Out[5]: array([4, 5, 6, 7, 8])

引き算(np.subtract)

引き算も全く同様に-演算子で処理することが可能です。

In [6]: a - b # 先ほど作ったa,bを用いる。
Out[6]: array([-2, -3, -4, -5, -6])

In [7]: b - a
Out[7]: array([2, 3, 4, 5, 6])

In [8]: a - 4 # 各々の要素から4ずつ引く。
Out[8]: array([-4, -3, -2, -1,  0])

掛け算(np.multiply)

掛け算は、*演算子を用いて計算します。この場合、配列同士のそれぞれ対応する要素同士の掛け算を行います。外積や内積といったベクトルや行列の演算とは違い、アダマール積と呼ばれる成分同士の演算なので注意してください。

In [9]: a * b
Out[9]: array([ 0,  4, 12, 24, 40])

In [10]: a * 2
Out[10]: array([0, 2, 4, 6, 8])

割り算・剰余(np.divide, np.mod)

割り算は/演算子を使います。また、商を整数にしたい場合はPython3の演算子と同様に//とスラッシュを2回続けて使うとできます。剰余を求めたいときは%を使います。全てPythonと同じ演算子なので直感的に演算することができます。

In [11]: b / a # b÷aを行うが、aの要素の中に0が含まれているので1つだけ無限を表す`inf`になっている。  
Out[11]: array([        inf,  4.        ,  3.        ,  2.66666667,  2.5       ])

In [12]: b / 2 # 2でわってみる
Out[12]: array([ 1.,  2.,  3.,  4.,  5.])

In [13]: b / 3 # 3でわってみる
Out[13]: array([ 0.66666667,  1.33333333,  2.        ,  2.66666667,  3.33333333])

In [14]: b // 3
Out[14]: array([0, 1, 2, 2, 3])

In [15]: b % 3
Out[15]: array([2, 1, 0, 2, 1])

剰余(%)と同じことはnp.mod()でもすることができる。

In [15]: np.mod(b, 3) # 3で割ったあまり
Out[15]: array([2, 1, 0, 2, 1])

累乗(np.power)・平方根(np.sqrt)

累乗はnp.power(x, t)x^t を求められます。また、Pythonのビルトイン演算子と同様にx**2で求めることも可能です。平方根を求めたいときはnp.sqrt(x)で求めることができます。

In [1]: import numpy as np

In [2]: np.power(2, 3) # 2の3乗の値を求める。
Out[2]: 8

In [3]: 2**3 # これはPythonの累乗計算
Out[3]: 8

In [4]: a = np.arange(1, 11, 1)

In [5]: b = np.array([1,2,1,2,1,2,1,2,1,2])

In [6]: a
Out[6]: array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10])

In [7]: b
Out[7]: array([1, 2, 1, 2, 1, 2, 1, 2, 1, 2])

In [8]: np.power(a, b) # 1乗、2乗の値が交互に出る。
Out[8]: array([  1,   4,   3,  16,   5,  36,   7,  64,   9, 100])

In [9]: a ** b # 同様に
Out[9]: array([  1,   4,   3,  16,   5,  36,   7,  64,   9, 100])

In [10]: np.sqrt(2) # 平方根はnp.sqrt()を使う
Out[10]: 1.4142135623730951

In [11]: 2 ** 0.5 # もちろんこのようにして平方根を求めることもできる。
Out[11]: 1.4142135623730951

In [12]: np.sqrt(a) # もちろん、配列を入れることも可能。
Out[12]:
array([ 1.        ,  1.41421356,  1.73205081,  2.        ,  2.23606798,
        2.44948974,  2.64575131,  2.82842712,  3.        ,  3.16227766])

三角関数

以下では、三角関数の基本であるサイン、コサイン、タンジェントからその逆関数、また度からラジアンへ変換する関数などを紹介します。引数としてndarrayを指定することができます。

三角関数(np.sin, np.cos, np.tan)

NumPyにはサイン、コサイン、タンジェントを計算する関数も実装されています。それぞれ、np.sin(),np.cos(),np.tan()を使います。また、円周率を示すnp.piもあります。

引数には、degreeではなくradianを指定します。

In [1]: import numpy as np

In [2]: np.sin(0)
Out[2]: 0.0

In [3]: np.cos(0)
Out[3]: 1.0

In [4]: np.tan(0)
Out[4]: 0.0

In [5]: np.sin(np.pi*0.5) # π/2のときのサインの値は1
Out[5]: 1.0

In [6]: np.cos(np.pi*0.5) # 0になるはず。
Out[6]: 6.123233995736766e-17

In [7]: np.tan(np.pi*0.5) # 無限に発散する。
Out[7]: 16331239353195370.0

In [8]: np.sin(1)
Out[8]: 0.8414709848078965

In [9]: np.cos(1)
Out[9]: 0.54030230586813977

In [10]: np.tan(1)
Out[10]: 1.5574077246549021

逆三角関数(np.arcsin, np.arccos, np.arctan)

三角関数の逆関数もNumPyの関数として存在します。関数名の頭にarcが付きます。逆関数なので、例えばarcsinsin(x)=yのとき、xの値を求めたかったらarcsin(y) = xとして求めることができます。

ここでも出力される値はdegreeではなくradianになります。

In [1]: import numpy as np

In [2]: np.arcsin(0.5)
Out[2]: 0.52359877559829882

In [3]: np.arccos(0.5)
Out[3]: 1.0471975511965976

In [4]: np.arctan(1.0)
Out[4]: 0.78539816339744828

In [5]: np.arcsin(-1.0)
Out[5]: -1.5707963267948966

In [6]: np.arccos(-1.0)
Out[6]: 3.1415926535897931

In [7]: np.arctan(-0.5)
Out[7]: -0.46364760900080615

ラジアンと度の相互変換

NumPyで実装されている三角関数系の関数は基本的にradianで操作されます。そのため、度をラジアン、ラジアンを度に変換する関数が必要になります。関数を使わなくても、\frac{\pi}{180} をかければラジアンに変換することができ、\frac{180}{\pi} をかければ度に変換することができますが、この操作をする関数が分かりやすいように用意されています。

そこで、NumPyではnp.rad2deg()がdegreeへの変換として存在し、np.radians()np.deg2rad()がラジアンへの変換用の関数として存在しています。

In [54]: import numpy as np

In [55]: np.radians(120)
Out[55]: 2.0943951023931953

In [56]: np.deg2rad(120)
Out[56]: 2.0943951023931953

In [57]: np.rad2deg(3.14)
Out[57]: 179.9087476710785

In [58]: np.deg2rad(np.rad2deg(2.3))
Out[58]: 2.3

実行速度も比べてみましょう。

In [8]: %timeit np.radians(24)
1.44 µs ± 89.8 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [9]: %timeit np.deg2rad(24)
1.43 µs ± 37.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

1.44マイクロ秒と、1.43マイクロ秒なのでほとんど差はありません。

指数関数、対数関数

指数関数

指数関数のなかでもネイピア数e を底(base)とするものがNumPyでは実装されています。e^x のことです。

これはnp.exp(x)で計算することが可能です。ネイピア数自体はnp.eで出すことができます。

In [1]: import numpy as np

In [2]: np.exp(1) # 1乗
Out[2]: 2.7182818284590451

In [3]: np.exp(2)
Out[3]: 7.3890560989306504

In [4]: np.exp(0)
Out[4]: 1.0

ネイピア数に関しての詳しい解説は以下のサイトでされているので参考にしてみてください。

ネイピア数eの定義がなぜあの形か、先生は説明をしてくれなかった

対数関数

対数関数は底が特殊な場合のみ用意されています。

  • 底がネイピア数e np.log(x)
  • 底が2 np.log2(x)
  • 底が10 np.log10(x)
  • log(1 + x)を計算(底はe)np.log1p(x)
n [1]: import numpy as np

In [2]: np.log(np.e) # np.eでネイピア数e
Out[2]: 1.0

In [3]: a = np.array([1., 2., np.e**2, 10])

In [4]: np.log(a) # 配列を指定することもできる。(他の数学関数も同様)
Out[4]: array([ 0.        ,  0.69314718,  2.        ,  2.30258509])

In [5]: b = np.array([1., 2., 4., 7])

In [6]: np.log2(b)
Out[6]: array([ 0.        ,  1.        ,  2.        ,  2.80735492])

In [7]: c = np.array([1., 10., 20., 100])

In [8]: np.log10(c)
Out[8]: array([ 0.     ,  1.     ,  1.30103,  2.     ])

In [9]: np.log1p(a)
Out[9]: array([ 0.69314718,  1.09861229,  2.12692801,  2.39789527])

底を他の値に変えたい時は、その変えたい底を真数とする対数で割ります。

In [10]: np.log(2)/np.log(4) # log4(2)がこれでできる。
Out[10]: 0.5

In [11]: np.log(9)/np.log(3) # log3(9)
Out[11]: 2.0

双曲線関数(hyperbolic)

双曲線関数とは、以下のように定義される2つの関数からできる一連の関数のことです。

\sinh x = \frac{e^x-e^{-x}}{2},\ \ \cosh x = \frac{e^x+e^{-x}}{2}\\

sinhはハイパボリックサイン、coshはハイパボリックコサインなどと呼ばれます。
また、tanh(ハイパボリックタンジェント)は

\tanh x = \frac{sinh x}{cosh x}

のように定義されます。
形は全く違いますが、これらの関数は三角関数と似た性質をいくつか示すため、sin, cos, tanのような語がついてきています。

詳しい解説は以下のサイトを参照してください。

高校数学の基本問題

これもNumPyの関数として呼び出すことが可能です。 逆関数としてnp.arcsinh(x),np.arccosh(x),np.arctanh(x)も存在します。

In [12]: np.sinh(2)
Out[12]: 3.6268604078470186

In [13]: np.cosh(2)
Out[13]: 3.7621956910836314

In [14]: np.tanh(2)
Out[14]: 0.9640275800758169

In [15]: np.sinh(-1)
Out[15]: -1.1752011936438014

In [16]: np.cosh(-1)
Out[16]: 1.5430806348152437

In [17]: np.tanh(-1)
Out[17]: -0.76159415595576485

In [18]: np.arcsinh(2)
Out[18]: 1.4436354751788103

In [19]: np.arccosh(1)
Out[19]: 0.0

In [20]: np.arctanh(0.7)
Out[20]: 0.86730052769405319

切り捨て、切り上げ、四捨五入

概数を求めるメソッドには、以下のようなものがあります。

  • 切り捨てnp.floor()
  • 切り捨てnp.trunc()
  • 切り上げnp.ceil()
  • 四捨五入np.round()
  • 四捨五入np.around()
  • 四捨五入np.rint()
  • 0に近い方向で整数をとるnp.fix()
In [1]: import numpy as np

In [2]: a = np.array([-1.8, -1.4, -1.0, -0.6, -0.2, 0., 0.2, 0.6, 1.0, 1.4, 1.8])

In [3]: np.floor(a)  # 切り捨て(値が小さい方の整数にする)
Out[3]: array([-2., -2., -1., -1., -1.,  0.,  0.,  0.,  1.,  1.,  1.])

In [4]: np.trunc(a) # 切り捨て(小数部分を切り捨てる)
Out[4]: array([-1., -1., -1., -0., -0.,  0.,  0.,  0.,  1.,  1.,  1.])

In [5]: np.ceil(a) # 切り上げ(大きい方の整数にする)
Out[5]: array([-1., -1., -1., -0., -0.,  0.,  1.,  1.,  1.,  2.,  2.])

In [6]: np.round(a) # 四捨五入
Out[6]: array([-2., -1., -1., -1., -0.,  0.,  0.,  1.,  1.,  1.,  2.])

In [7]: np.around(a) # 四捨五入
Out[7]: array([-2., -1., -1., -1., -0.,  0.,  0.,  1.,  1.,  1.,  2.])

In [8]: np.rint(a) # 四捨五入
Out[8]: array([-2., -1., -1., -1., -0.,  0.,  0.,  1.,  1.,  1.,  2.])

In [9]: np.fix(a) # 0に近い方向の整数をとる
Out[9]: array([-1., -1., -1., -0., -0.,  0.,  0.,  0.,  1.,  1.,  1.])

複素数

NumPyは複素数も扱うことができます。虚部のところにjを添えるだけです。また、複素数に関する関数として実部を返すnp.real(),虚部を返すnp.imag(),複素共役(虚部の符号を反転させたもの)を返すnp.conj()があります。

複素数は、音波の位相やコンピューターグラフィックスでよく使われるクォータニオンなどに便利な概念です。

In [6]: import numpy as np

In [7]: a = 1 + 2j # 1 + 2iの複素数

In [8]: b = -2 + 1j # -2 + iの複素数。1を忘れないようにする

In [9]: np.real(a) # aの実部は1
Out[9]: 1.0

In [10]: np.imag(a) # aの虚部は2
Out[10]: 2.0

In [6]: a+b # 複素数の計算と同じように、実部同士、虚部同士を足し合わせる。
Out[6]: (-1+3j)

In [7]: a*b
Out[7]: (-4-3j)

In [8]: a/b
Out[8]: (-0-1j)

In [9]: np.conj(a) # 複素共役を返す
Out[9]: (1-2j)

絶対値

絶対値をとる関数もあります。これには2つあって、np.fabsnp.absolute()があります。np.absolute()の略記でnp.abs()もあります。配列を引数として指定すると各要素の絶対値が返されます。この2つの関数の違いは複素数を引数として指定できるか否かです。

np.absolute()は複素数の絶対値を返すことができます。np.fabs()はできません。

In [1]: import numpy as np

In [2]: a = -2.5

In [3]: np.absolute(a)
Out[3]: 2.5

In [4]: np.fabs(a)
Out[4]: 2.5

In [5]: b = -2 + 3j # 複素数でやってみる。

In [6]: np.abs(b) # np.absはnp.absolute()の略記。
Out[6]: 3.6055512754639891

In [7]: np.fabs(b) # np.fabs()では、複素数の絶対値を計算することができない。
---------------------------------------------------------------------------
(エラーメッセージが表示される)

In [8]: c = np.array([-1, 2, -8, 12, 1+2j])

In [9]: np.abs(c) # 各々の要素の絶対値が返ってくる。
Out[9]: array([  1.        ,   2.        ,   8.        ,  12.        ,   2.23606798])

数学的な定数の呼び出し

最後に数学でよく用いられる定数としてNumPyに設定されているものをみていきましょう。
どうやらeとπしかなさそうです。
どちらも呼び出す時は()を必要とはしません。

定数 NumPyでの関数
ネイピア数e np.e
円周率π np.pi
In [1]: import numpy as np

In [2]: np.e
Out[2]: 2.718281828459045

In [3]: np.pi
Out[3]: 3.141592653589793