NumPyには配列の要素の平均を求める関数numpy.average
とnumpy.mean
の2つの関数があります。
今回の記事では、
-
average
とmean
の違い - 各々の関数の使い方
について解説します。
averageとmeanの違い
まずはこれら2つの関数の違いについて解説します。
一番大きな違いは、average
関数には重み付き(weighted)の平均を求められますが、mean
関数にはそのような機能がないことです。すなわち、重み付き平均を求めたい場合にはaverage
を使用します。
その他には、mean
関数では平均を計算する際のデータ型をdtype
で指定することができますが、average
関数では指定することができません。
以下の表に、average
関数でできることとmean
関数でできることの詳細をまとめました。
np.average | np.mean | |
---|---|---|
重み付きの平均 | ◯ | × |
データ型を指定 | × | ◯ |
平均を計算する軸(axis)の方向を指定 | ◯ | ◯ |
軸(axis)の数を減らさない形で平均を返す | × | ◯ |
np.average
np.average
関数から使い方を解説します。np.average
関数のAPIドキュメントは以下の通りです。
**np.average(a, axis = None, weights = None, returned = False, keepdims=
params:
パラメータ名 | 型 | 概要 |
---|---|---|
a | array_like (配列に相当するもの) |
平均を求めたい配列を指定します。 |
axis | intもしくは intのタプル |
(省略可能)初期値None どの軸(axis)方向にそって平均を計算するかを指定します。デフォルトでは全ての要素についての平均を返します。 |
weights | array_like | (省略可能)初期値None 平均を求める際にそれぞれの要素につける重みを設定します。この割り当てられる値が大きいほどその要素の平均に対して及ぼす影響が大きくなります。 |
returned | bool | (省略可能)初期値”False” 値を返す際に、(平均, 重みの合計)といったタプルの形にするかどうかを指定します。 |
keepdims | bool | (省略可能)初期値no value。計算の結果、次元を縮小できる場合でも縮小可能な軸をサイズ1として維持します。 |
returns:
指定された方法で計算された平均の値が返されます。軸が指定されているときは配列の形で返され、returned=True
のときは(平均, 重みの合計)の形で値が返されます。
この関数は第1引数に、平均を求めたい配列を、第2引数に平均を求める軸の方向を、第3引数に重みを、第4引数に値の返し方を、第5引数に出力配列の次元数を入力配列と同じに維持するかどうかを指定します。
基本的な使い方
それぞれの引数を見ながら使い方を見て見ましょう。まずは、平均を求めたい配列だけを指定する場合から見ていきましょう。
In [1]: import numpy as np
In [2]: a = np.array([33, 44, 54, 23, 25, 55, 32, 76]) # 適当な配列を1つ作る。
In [3]: np.average(a) # まずはaの平均を求めてみる。
Out[3]: 42.75
In [4]: a = a.reshape(2,4) # aのshapeを変更。
In [5]: a
Out[5]:
array([[33, 44, 54, 23],
[25, 55, 32, 76]])
In [6]: np.average(a) # aの形状に関わらず、引数にaxisが指定されていなければ1つのスカラー量だけ返される。
Out[6]: 42.75
軸の指定
次に軸(axis)を指定してみます。
In [7]: np.average(a, axis = 0) # 軸(axis)を指定する。2次元配列でaxis =0のときは行方向の平均を求める。
Out[7]: array([ 29. , 49.5, 43. , 49.5])
In [8]: np.average(a, axis = 1) # axis = 1のときは列方向の平均を求める。
Out[8]: array([ 38.5, 47. ])
In [10]: b = np.random.rand(24).reshape(2,3,4) # 3次元の配列でも確かめておく。
In [11]: b
Out[11]:
array([[[ 0.11076868, 0.34832254, 0.36064024, 0.1229295 ],
[ 0.54269798, 0.84373625, 0.70098021, 0.32525671],
[ 0.11554616, 0.6299628 , 0.75165631, 0.59971063]],
[[ 0.28207248, 0.71686235, 0.81355244, 0.83476733],
[ 0.92493115, 0.05083893, 0.96601166, 0.98868334],
[ 0.41412407, 0.23077686, 0.71536231, 0.61265456]]])
In [12]: np.average(b, axis = 0) # 大きく2つに分かれた配列の2つの要素の平均をそれぞれ求める。
Out[12]:
array([[ 0.19642058, 0.53259244, 0.58709634, 0.47884841],
[ 0.73381456, 0.44728759, 0.83349594, 0.65697002],
[ 0.26483511, 0.43036983, 0.73350931, 0.6061826 ]])
In [13]: np.average(b, axis = 1) # 行方向の平均をとる。
Out[13]:
array([[ 0.25633761, 0.60734053, 0.60442558, 0.34929895],
[ 0.5403759 , 0.33282605, 0.83164214, 0.81203508]])
In [14]: np.average(b, axis = 2) # 列方向の平均をとる。
Out[14]:
array([[ 0.23566524, 0.60316779, 0.52421897],
[ 0.66181365, 0.73261627, 0.49322945]])
重みの指定
次に重み(weights)を設定します。
In [15]: a = a.flatten() # aを1次元配列に変形。
In [16]: w = np.array([0.1, 0.05, 0.2, 0.0, 0.0, 0.4, 0.2, 0.05]) # 重みを設定。
In [17]: np.average(a, weights = w) # 重みつきの平均を求める。
Out[17]: 48.5
In [18]: w2 = np.array([0.2, 0.8])
In [19]: a = a.reshape(2,4) # またaのshapeを変更する。
In [20]: np.average(a, axis = 0, weights = w2) # 指定した軸方向の要素数が一致しており、かつ重みの配列が1次元配列であるとき、ブロードキャストが適用される。
Out[20]: array([ 26.6, 52.8, 36.4, 65.4])
重みの合計を表示
最後に、returned
について見ていきます。これは、値を返す際に重みの合計を表示するかどうかを指定できる引数です。
引数であるweights
に何も指定していないと、各要素の重みは1.0
として重みの合計が計算されます。
In [21]: np.average(a, returned = "True") # 重みを設定しないと、各々の要素の重みが1.0に設定されるので、重みの合計は要素数と一致する。
Out[21]: (42.75, 8.0)
In [22]: a = a.flatten() # 1を1次元配列に変形。
In [23]: a
Out[23]: array([33, 44, 54, 23, 25, 55, 32, 76])
In [24]: w
Out[24]: array([ 0.1 , 0.05, 0.2 , 0. , 0. , 0.4 , 0.2 , 0.05])
In [25]: np.average(a, weights = w, returned = "True") # この状態で実行すれば、平均、重さの合計が表示される。
Out[25]: (48.5, 1.0)
np.mean
次に、np.mean
関数について解説します。この関数は、np.average
関数に比べると重み付きなどは出来ないので、純粋に要素の平均を求めるために使用します。
np.average
関数に無い機能として一番大きいのは、平均を求める際に使うデータ型を指定できることです。
np.mean
関数のAPIドキュメントは以下のようになっています。
**numpy.mean(a, axis = None, dtype = None, out = None, keepdims =
params:
パラメータ名 | 型 | 概要 |
---|---|---|
a |
array_like (arrayに相当するもの) |
平均を求めたい配列を指定します。 |
axis |
intもしくは intのタプル |
(省略可能)初期値None どの軸(axis)に沿って平均を求めるかを指定します。 |
dtype |
dtype | (省略可能)初期値None 平均を求める際に使用するデータ型を指定します。 |
out |
ndarray | (省略可能)初期値None 計算結果を格納するための配列を指定します。 |
keepdims |
bool (TrueまたはFalse) |
(省略可能)初期値”False” 返す配列の軸(axis)の数をそのままにする(その軸での要素数を1にする)かどうかを指定します。 |
where |
bool | 各要素を平均値の計算に含める要素かどうかの配列を指定します |
returns:
指定した配列の要素の平均、もしくは平均を要素とする配列が返されます。
1つ目の引数で平均を求めたい配列を指定します。次に、2つ目の引数axis
でどの軸(axis)に沿って平均を求めていくかを決め、3つ目の引数dtype
で計算する際に用いるデータ型を指定します。4つ目の引数out
で結果を格納する値を指定し、5つ目の引数keepdims
で結果の配列の軸(axis)の数をそのままにするかどうかを指定します。6つ目の引数where
で平均値の計算に含める要素を指定します。
やっている操作自体は純粋な要素の平均を求めているだけで、その結果をどう出力するかを引数で指定していくイメージです。
基本的な使い方
まずは、a
だけを指定した例から見ていきましょう。
In [1]: import numpy as np
In [2]: a = np.random.randint(0, 10, 20) # 0~9までのランダムな整数を20個生成。
In [3]: a
Out[3]: array([1, 2, 2, 5, 1, 0, 9, 0, 4, 6, 4, 4, 8, 5, 5, 8, 3, 6, 2, 7])
In [4]: np.mean(a) # 平均を求める。
Out[4]: 4.0999999999999996
In [5]: b = a.reshape(4,5) # aを4×5の二次元配列に変形したものをbに代入。
In [6]: b
Out[6]:
array([[1, 2, 2, 5, 1],
[0, 9, 0, 4, 6],
[4, 4, 8, 5, 5],
[8, 3, 6, 2, 7]])
In [7]: np.mean(b) # shapeを変更しても結果は変わらない。
Out[7]: 4.0999999999999996
軸を指定する
次に、軸(axis)を指定していきます。
In [8]: np.mean(b, axis = 0) # 行方向、つまり列ごとの平均を求めていく。
Out[8]: array([ 3.25, 4.5 , 4. , 4. , 4.75])
In [9]: np.mean(b, axis = 1) # 列方向、つまり行ごとの平均を求めていく。
Out[9]: array([ 2.2, 3.8, 5.2, 5.2])
In [10]: c = np.random.rand(24).reshape((2,3,4)) # 3次元配列でも試していく。
In [11]: c # 今度は0~1の範囲で乱数を24個生成。
Out[11]:
array([[[ 0.95979688, 0.08343238, 0.33695294, 0.78382111],
[ 0.36685429, 0.86955043, 0.88227388, 0.79091495],
[ 0.63368575, 0.5130265 , 0.0619997 , 0.6573761 ]],
[[ 0.45284015, 0.08635302, 0.94612675, 0.33949862],
[ 0.17685103, 0.26249988, 0.44127751, 0.3318031 ],
[ 0.18581007, 0.66045853, 0.29541049, 0.33626342]]])
In [12]: np.mean(c, axis = 0) # 軸(axis)が3つある配列でのaxis = 0は2つに分かれた配列方向のもの。この場合、2つの配列の相当する要素における平均をそれぞれ求めていっている。
Out[12]:
array([[ 0.70631851, 0.0848927 , 0.64153984, 0.56165986],
[ 0.27185266, 0.56602515, 0.6617757 , 0.56135903],
[ 0.40974791, 0.58674251, 0.17870509, 0.49681976]])
In [13]: np.mean(c, axis = 1) # これが軸が2つある状態での行方向、つまり列ごとの平均を求めるもの。
Out[13]:
array([[ 0.65344564, 0.48866977, 0.42707551, 0.74403739],
[ 0.27183375, 0.33643714, 0.56093825, 0.33585505]])
In [14]: np.mean(c, axis = 2) # これが軸が2つある状態での列方向、つまり行ごとの平均を求めるもの。
Out[14]:
array([[ 0.54100083, 0.72739839, 0.46652201],
[ 0.45620463, 0.30310788, 0.36948563]])
データ型の指定
次はdtype
を指定していきます。これによって計算の精度が変わってきます。
In [18]: d = np.random.rand(1000) # 乱数を1000個生成。
In [19]: d.dtype # dtypeを確認。
Out[19]: dtype('float64')
In [20]: np.mean(d) # まずは何もdtypeを指定しない状態で平均を計算。
Out[20]: 0.4961181909572322
In [21]: np.mean(d, dtype = "float32") # ビット数を半分にしてもう一度計算してみる。
Out[21]: 0.49611819
In [22]: np.mean(d, dtype = "float16") # 8ビットにするとさらに精度が低くなる
Out[22]: 0.49609
引数のout
についてですが、これはほとんど使用しません。メモリを使い回したいときに指定してください。
配列の次元数を落とさずに結果を求める
最後に、keepdims
について解説します。keepdims
にTrue
を設定すると、軸の数を減らしません。次の例のように返された配列のshape
を見れば、どのような変化をするか分かりやすいです。
In [27]: b # 2次元配列のbを使用する。
Out[27]:
array([[1, 2, 2, 5, 1],
[0, 9, 0, 4, 6],
[4, 4, 8, 5, 5],
[8, 3, 6, 2, 7]])
In [32]: e = np.mean(b, keepdims = True) # 次元を減らさない。
In [33]: e
Out[33]: array([[ 4.1]])
In [34]: e.shape
Out[34]: (1, 1)
In [35]: f = np.mean(b, keepdims = False)
In [36]: f
Out[36]: 4.0999999999999996
In [37]: g = np.mean(b, axis = 1, keepdims = True)
In [38]: g
Out[38]:
array([[ 2.2],
[ 3.8],
[ 5.2],
[ 5.2]])
In [39]: g.shape
Out[39]: (4, 1)
In [40]: h = np.mean(b, axis = 1, keepdims = False)
In [41]: h
Out[41]: array([ 2.2, 3.8, 5.2, 5.2])
In [42]: h.shape
Out[42]: (4,)