Python, SciPyでトリム平均を算出(ndarray, DataFrame)
Pythonでnumpy.ndarray
やpandas.DataFrame
のトリム平均(調整平均)を算出するには、SciPyのscipy.stats.trim_mean()
, scipy.stats.tmean()
を使う。
scipy.stats.trim_mean()
は指定した割合の要素を最大値・最小値から順に除外、scipy.stats.tmean()
は指定した値以上・以下の要素を除外して平均を算出する。
ここでは以下の内容について説明する。
- トリム平均(調整平均)
scipy.stats.trim_mean()
の使い方- 基本的な使い方
- 注意点: ソートされてから除外される
- 二次元配列の場合
scipy.stats.tmean()
の使い方- 基本的な使い方
- 注意点: SciPy1.7.1時点のバグ
pandas.DataFrame
,Series
に対するトリム平均scipy.stats.trim_mean()
,trimboth()
の例scipy.stats.tmean()
の例- 注意点: 文字列などが含まれる場合
以下のようにライブラリをインポートする。
import scipy.stats
import numpy as np
import pandas as pd
なお、以下のように関数を個別にインポートすることも可能。
from scipy.stats import trim_mean
サンプルコードのSciPyのバージョンは1.7.1
。
import scipy
print(scipy.__version__)
# 1.7.1
トリム平均(調整平均)
トリム平均(調整平均)は、元のデータから上位・下位の値をいくつか取り除いて算出する平均値。極端に大きかったり小さかったりする外れ値・異常値の影響を避けることができる。
英語ではtrimmed meanやtruncated meanなどと呼ばれる。
scipy.stats.trim_mean()の使い方
基本的な使い方
numpy.ndarray
に対する通常の平均値(算術平均)はmean()
メソッドやnp.mean()
関数で算出できる。
a = np.array([2**n for n in range(10)])
print(a)
# [ 1 2 4 8 16 32 64 128 256 512]
print(type(a))
# <class 'numpy.ndarray'>
print(a.mean())
# 102.3
print(np.mean(a))
# 102.3
トリム平均はscipy.stats.trim_mean()
を使う。第一引数に対象のnumpy.ndarray
、第二引数proportiontocut
に除外する要素の割合を指定する。
例えば第二引数が0.1
の場合は上位・下位からそれぞれ10%ずつ、0.2
の場合はそれぞれ20%ずつの要素が取り除かれる。
print(scipy.stats.trim_mean(a, 0.1))
# 63.75
print(scipy.stats.trim_mean(a, 0.2))
# 42.0
トリムされた後の配列はscipy.stats.trimboth()
で確認できる。
print(scipy.stats.trimboth(a, 0.1))
# [ 2 8 4 16 32 64 128 256]
print(scipy.stats.trimboth(a, 0.2))
# [ 4 16 8 32 64 128]
除外される個数は小数点以下切り捨て。例えば、元の配列の個数が10
個で第二引数を0.19
とすると、上位・下位からそれぞれ除外される個数は10 * 0.19 = 1.9
個 → 1
個(小数点以下切り捨て)となる。
print(scipy.stats.trimboth(a, 0.19))
# [ 2 8 4 16 32 64 128 256]
print(scipy.stats.trim_mean(a, 0.19))
# 63.75
注意点: ソートされてから除外される
scipy.stats.trim_mean()
, scipy.stats.trimboth()
では、最大値・最小値から順に要素が除外される。当然ながら、元の配列がソートされていない場合も、ソートされてから値の大きさを元に除外される。
a_unsorted = np.array([100, 1000, 0, 10000, 10])
print(a_unsorted)
# [ 100 1000 0 10000 10]
print(scipy.stats.trimboth(a_unsorted, 0.2))
# [ 10 100 1000]
print(scipy.stats.trim_mean(a_unsorted, 0.2))
# 370.0
元の配列の並びのまま左端・右端の要素を除外したい場合はスライスを用いればよい。
print(a_unsorted[1:-1])
# [ 1000 0 10000]
print(a_unsorted[1:-1].mean())
# 3666.6666666666665
二次元配列の場合
以下の二次元配列を例とする。
a_2d = np.array([2**n for n in range(16)]).reshape(4, 4)
print(a_2d)
# [[ 1 2 4 8]
# [ 16 32 64 128]
# [ 256 512 1024 2048]
# [ 4096 8192 16384 32768]]
scipy.stats.trim_mean()
, scipy.stats.trimboth()
もNumPyの多くの関数と同様に引数axis
を指定できる。
デフォルトでは列ごとに処理される(axis=0
)。
print(scipy.stats.trimboth(a_2d, 0.25))
# [[ 16 32 64 128]
# [ 256 512 1024 2048]]
print(scipy.stats.trim_mean(a_2d, 0.25))
# [ 136. 272. 544. 1088.]
axis=1
とすると行ごと、axis=None
とすると一次元化された配列に対して処理される。
print(scipy.stats.trimboth(a_2d, 0.25, axis=1))
# [[ 2 4]
# [ 32 64]
# [ 512 1024]
# [ 8192 16384]]
print(scipy.stats.trim_mean(a_2d, 0.25, axis=1))
# [3.0000e+00 4.8000e+01 7.6800e+02 1.2288e+04]
print(scipy.stats.trimboth(a_2d, 0.25, axis=None))
# [ 16 32 64 128 512 256 1024 2048]
print(scipy.stats.trim_mean(a_2d, 0.25, axis=None))
# 510.0
scipy.stats.tmean()の使い方
基本的な使い方
scipy.stats.tmean()
では、上限・下限の値を指定してその範囲内の値から平均が算出される(範囲外の値が除外される)。
第一引数に対象のnumpy.ndarray
、第二引数limits
に(下限値, 上限値)
のタプルを指定する。上限値・下限値にNone
を指定した場合は除外されずにすべての値が残る。
print(a)
# [ 1 2 4 8 16 32 64 128 256 512]
print(scipy.stats.tmean(a, (4, 32)))
# 15.0
print(scipy.stats.tmean(a, (None, 32)))
# 10.5
第三引数inclusive
に上限値・下限値に等しい値を残すかどうかを指定できる。
デフォルトはinclusive=(True, True)
で下限値 <= x <= 上限値
の範囲の値が残る。inclusive=(False, False)
とすると下限値 < x < 上限値
の範囲の値が残る。
print(scipy.stats.tmean(a, (4, 32), (False, False)))
# 12.0
なお、scipy.stats.trim_mean()
に対するscipy.stats.trimboth()
のように、scipy.stats.tmean()
でトリムされた後の配列を返す関数は公開されていない(SciPy1.7.1時点)。
注意点: SciPy1.7.1時点のバグ
scipy.stats.tmean()
にも引数axis
があるが、SciPy1.7.1時点では正しく動作していない。
print(a_2d)
# [[ 1 2 4 8]
# [ 16 32 64 128]
# [ 256 512 1024 2048]
# [ 4096 8192 16384 32768]]
print(scipy.stats.tmean(a_2d, (16, 2048)))
# 510.0
print(scipy.stats.tmean(a_2d, (16, 2048), axis=0))
# 510.0
# print(scipy.stats.tmean(a_2d, (16, 2048), axis=1))
# AxisError: axis 1 is out of bounds for array of dimension 1
例えば各列に対してscipy.stats.tmean()
を適用したい場合は、以下のような方法がある。
for i in range(a_2d.shape[1]):
print(scipy.stats.tmean(a_2d[:, i], (16, 2048)))
# 136.0
# 272.0
# 544.0
# 1088.0
pandas.DataFrame, Seriesに対するトリム平均
scipy.stats.trim_mean()
などの関数はpandas.DataFrame
, Series
に対して適用することも可能。
以下のpandas.DataFrame
を例とする。
df = pd.DataFrame(a_2d,
index=['R1', 'R2', 'R3', 'R4'],
columns=['C1', 'C2', 'C3', 'C4'])
print(df)
# C1 C2 C3 C4
# R1 1 2 4 8
# R2 16 32 64 128
# R3 256 512 1024 2048
# R4 4096 8192 16384 32768
scipy.stats.trim_mean(), trimboth()の例
pandas.Series
(= pandas.DataFrame
の列・行)に対する例。一次元配列として処理され、scipy.stats.trimboth()
はnumpy.ndarray
を返す。
print(type(df['C1']))
# <class 'pandas.core.series.Series'>
print(scipy.stats.trim_mean(df['C1'], 0.25))
# 136.0
print(scipy.stats.trimboth(df['C1'], 0.25))
# [ 16 256]
print(type(scipy.stats.trimboth(df['C1'], 0.25)))
# <class 'numpy.ndarray'>
pandas.DataFrame
に対する例。二次元配列として処理され、scipy.stats.trim_mean()
もscipy.stats.trimboth()
もnumpy.ndarray
を返す。
print(scipy.stats.trim_mean(df, 0.25))
# [ 136. 272. 544. 1088.]
print(type(scipy.stats.trim_mean(df, 0.25)))
# <class 'numpy.ndarray'>
print(scipy.stats.trimboth(df, 0.25))
# [[ 16 32 64 128]
# [ 256 512 1024 2048]]
print(type(scipy.stats.trimboth(df, 0.25)))
# <class 'numpy.ndarray'>
引数axis
の指定も可能。
print(scipy.stats.trim_mean(df, 0.25, axis=1))
# [3.0000e+00 4.8000e+01 7.6800e+02 1.2288e+04]
print(scipy.stats.trim_mean(df, 0.25, axis=None))
# 510.0
pandas.DataFrame
の各列に対する結果をpandas.Series
として扱いたい場合は、以下のような方法がある。
s_tm = pd.Series(scipy.stats.trim_mean(df, 0.25),
index=df.columns)
print(s_tm)
# C1 136.0
# C2 272.0
# C3 544.0
# C4 1088.0
# dtype: float64
scipy.stats.tmean()の例
scipy.stats.tmean()
に対する例。上述のように、SciPy1.7.1時点では引数axis
が正常に動作しないので注意。
print(scipy.stats.tmean(df, (16, 2048)))
# 510.0
print(scipy.stats.tmean(df['C1'], (16, 2048)))
# 136.0
各列に対してscipy.stats.tmean()
を適用したい場合は、以下のような方法がある。
for col in df:
print(col, scipy.stats.tmean(df[col], (16, 2048)))
# C1 136.0
# C2 272.0
# C3 544.0
# C4 1088.0
注意点: 文字列などが含まれる場合
文字列などの数値以外の要素が含まれる場合、scipy.stats.trim_mean()
などの関数はエラーとなる。
df['C5'] = ['A', 'B', 'C', 'D']
print(df)
# C1 C2 C3 C4 C5
# R1 1 2 4 8 A
# R2 16 32 64 128 B
# R3 256 512 1024 2048 C
# R4 4096 8192 16384 32768 D
# print(scipy.stats.trim_mean(df, 0.25))
# TypeError: unsupported operand type(s) for /: 'str' and 'int'
pandas.DataFrame
のselect_dtypes()
メソッドで数値列のみを取り出してから適用する必要がある。
print(df.select_dtypes(include='number'))
# C1 C2 C3 C4
# R1 1 2 4 8
# R2 16 32 64 128
# R3 256 512 1024 2048
# R4 4096 8192 16384 32768
print(scipy.stats.trim_mean(df.select_dtypes(include='number'), 0.25))
# [ 136. 272. 544. 1088.]