統計などでよく用いられる指標の1つとして中央値(メジアン)があります。データの中で外れ値があったときなどでもそれにはあまり影響を受けずにデータの中央を求めることができる指標としてよく用いられます。
よく用いられる例としては、マイクロソフトの食堂にいる社員の平均年収です。仮に、2020年にマイクロソフトの取締役を引退したビル・ゲイツが食堂にいれば平均年収は大幅に上昇してしまい、参考になりません。
中央値(メジアン)
中央値というのは、データを大きさ順に並べたときにちょうど中央にくるものの値を指します。
これはデータの個数が奇数個の場合は真ん中に相当する値が存在するのでその値をとればよく、
偶数個存在する場合は中央というのがちょうどデータとデータの間になってしまいます。
このときは2つのデータの平均をとることで中央値を取り出します。
昇順に並べられた2n個のデータがあるとします。
このとき、中央値は
データが奇数個(2n-1個)あるときは、
となり、中央値は
となります。
中央値をとる目的として、データの真ん中くらいにある値を知りたいというものがあります。
似たような指標として平均値があります。
平均値は集合から大きく外れた値である外れ値をとるものが含まれているとその集合を表す指標としては不適切になってしまいます。
中央値はこのような外れ値の影響をあまり受けないという点で使われることが多いです。
以下のサイトで詳しく解説されています。気になる方は参照してみてください。
平均値,中央値,最頻値の求め方といくつかの例 -高校数学の美しい物語-
np.median()
APIドキュメント
早速、この関数のAPIドキュメントをみていきましょう。
numpy.median(a, axis=None, out=None, overwrite_input=False, keepdims=False)
args:
パラメータ名 | 型 | 概要 |
---|---|---|
a |
array_like 配列に相当するもの |
中央値を求めたい配列を指定します。 |
axis |
int または None またはintのシーケンス |
(省略可能)初期値None どの軸(axis)方向に沿ったデータの中央値をとるかを指定します。axis=Noneの時は配列内全ての要素を対象に中央値を計算します。 |
out |
ndarray | (省略可能)初期値None 結果を格納するための配列を指定することができます。形状(shape)やデータ型などは揃えておく必要があります。 |
overwrite_input |
bool値 | (省略可能)初期値False ここがTrueのとき、入力された配列のメモリーに上書きする形で演算を行います。これをすることでメモリーの使用量を抑えることができますが元の配列の要素の順番などが変更される場合があります。 |
keepdims |
bool値 | (省略可能)初期値False ここをTrueにすると結果を出力する際、要素数が1になる次元を削除せず残します。これをすることで元の配列に対してこの値を用いて演算をする際にブロードキャスト機能を用いることができるようになります。 |
returns:
計算された中央値が出力されます。 (outに配列が指定されている時はその配列)
第一引数は中央値を求めたい配列を指定します。axis
でどの軸方向で中央値を求めるかを指定し、out
で結果を格納する配列を指定します(この引数はあまり使われることがありません。)。overwrite_input
は元の配列のデータに対して上書きする形で演算を行なっても良いかどうかを指定します。最後にkeepdims
は元の配列の次元数を保存するかどうかを指定します。
サンプルコード
APIドキュメントをみたところで次は実際のコードを見ながら使い方を確かめていきます。
まずは、特に引数を指定せず、全ての要素を対象に中央値を求めるものから。
In [1]: import numpy as np # numpyモジュールのインポート
In [4]: a = np.random.randint(100, size = (2, 3, 4)) # 2×3×4の3次元乱数配列を生成
In [5]: a # 中身を確認。
Out[5]:
array([[[77, 63, 49, 1],
[11, 77, 62, 38],
[32, 20, 54, 62]],
[[63, 72, 21, 87],
[ 7, 85, 4, 71],
[94, 98, 21, 71]]])
In [6]: np.median(a) # 全ての要素を対象にして中央値を求める。
Out[6]: 62.0
次に、axis
を指定します。これを指定することでどの軸方向を範囲として中央値を出すかを指定することができます。
axis
についてよくわからない方は以下の記事を参考にしてください。
NumPyの軸(axis)と次元数(ndim)とは何を意味するのか - DeepAge /features/numpy-axis.html
In [7]: np.median(a, axis=2) # axis=2の軸方向に沿った中央値を求めていく。
Out[7]:
array([[ 56. , 50. , 43. ],
[ 67.5, 39. , 82.5]])
In [8]: np.median(a, axis=1) # axis=1にする
Out[8]:
array([[ 32., 63., 54., 38.],
[ 63., 85., 21., 71.]])
In [9]: np.median(a, axis=(1, 2)) # 2つ指定すると2次元の中での中央値を出してくれる。
Out[9]: array([ 51.5, 71. ])
out
はほとんど使われることはないので説明は割愛します。
次はoverwrite_input
を使って見ましょう。配列の破壊的操作を行なって演算を行うので、配列の要素の順番が中央値を求める前と後で変わっていることがあります。
In [24]: b = a.copy()
In [25]: b
Out[25]:
array([[[77, 63, 49, 1],
[11, 77, 62, 38],
[32, 20, 54, 62]],
[[63, 72, 21, 87],
[ 7, 85, 4, 71],
[94, 98, 21, 71]]])
In [26]: np.median(b, axis=1, overwrite_input=True)
Out[26]:
array([[ 32., 63., 54., 38.],
[ 63., 85., 21., 71.]])
In [27]: np.all(a==b) # aとbの全ての要素が合致しているか調べる。
Out[27]: False
In [28]: a
Out[28]:
array([[[77, 63, 49, 1],
[11, 77, 62, 38],
[32, 20, 54, 62]],
[[63, 72, 21, 87],
[ 7, 85, 4, 71],
[94, 98, 21, 71]]])
In [29]: b # aとは違った並び方になっている。破壊的操作が行われた証拠。
Out[29]:
array([[[11, 20, 49, 1],
[32, 63, 54, 38],
[77, 77, 62, 62]],
[[ 7, 72, 4, 71],
[63, 85, 21, 71],
[94, 98, 21, 87]]])
最後に、keepdims
について扱います。
これをkeepdims=True
にすると配列の次元数が保存された形で結果が出力されます。
In [30]: np.median(a, axis=0, keepdims=True) # 3次元配列が出力される
Out[30]:
array([[[ 70. , 67.5, 35. , 44. ],
[ 9. , 81. , 33. , 54.5],
[ 63. , 59. , 37.5, 66.5]]])
In [31]: np.median(a, axis=1, keepdims=False) # axis=1でTrueの場合とFalseの場合とを見比べる
Out[31]:
array([[ 32., 63., 54., 38.],
[ 63., 85., 21., 71.]])
In [32]: np.median(a, axis=1, keepdims=True)
Out[32]:
array([[[ 32., 63., 54., 38.]],
[[ 63., 85., 21., 71.]]])
In [33]: np.median(a, axis=(0,2), keepdims=True)
Out[33]:
array([[[ 63.],
[ 50.],
[ 58.]]])