Pythonで正規化・標準化(リスト、NumPy配列、pandas.DataFrame)
Pythonのリストlist
, NumPy配列ndarray
, pandasのDataFrame
を正規化・標準化する方法について説明する。
Python標準ライブラリやNumPy, pandasのメソッドを利用して最大値や最大値、平均、標準偏差を求めて処理することも可能だが、SciPyやscikit-learnには正規化・標準化のための専用の関数やクラスが用意されている。
本記事のサンプルコードの各ライブラリのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。
import numpy as np
import scipy.stats
from sklearn import preprocessing
print(np.__version__)
# 1.26.2
print(scipy.__version__)
# 1.11.4
import sklearn # Required only for version check
print(sklearn.__version__)
# 1.3.2
import pandas as pd
print(pd.__version__)
# 2.1.4
なお、import sklearn
はバージョンの確認のため。実際の処理にはfrom sklearn import preprocessing
だけでよい。
正規化の種類
正規化、規格化、標準化、スケーリングなど様々な用語があるが、ここではその厳密な定義は行わない。英語でいうところのFeature scalingの処理のうち、以下の2つを対象とし、後者を特に標準化と呼ぶことにする。
最小値0・最大値1に正規化
Min-Max normalization。
$$ x' = \frac{x - \min{\left( x \right)}}{\max{\left( x \right)} - \min{\left( x \right)}} $$
平均0・分散1に正規化(標準化)
Standardization (Z-score normalization)。平均を$\overline{x}$、標準偏差を$\sigma$とすると以下のように表される。
$$ x' = \frac{x - \overline{x}}{\sigma} $$
標準偏差には、$n$で割る母集団の標準偏差と$n−1$で割る標本標準偏差がある。$n$はデータの個数。
母集団の標準偏差。
$$ \sigma^2=\frac{1}{n} \sum_{i=1}^{n} (x_i-\overline{x})^2 $$
標本標準偏差。
$$ s^2=\frac{1}{n-1} \sum_{i=1}^{n} (x_i-\overline{x})^2 $$
データを母集団全体と見なすか、母集団から抽出した標本と見なすかによって使い分けるが、以下で説明するライブラリや関数によってどちらの標準偏差が使われているが異なる。
データの個数$n$が十分大きければどちらでも結果に大差はないが、厳密に値を求めたい場合はどちらの標準偏差が使われているか注意が必要。
正規化・標準化を行うライブラリ
SciPyやscikit-learnでは正規化・標準化のための専用のクラスや関数が用意されている。
ここではPythonのリストlist
を例にそれぞれの使い方を説明する。ndarray
やDataFrame
に対する例、および、SciPyやscikit-learnを使わない方法については後述。
SciPyで標準化
SciPyには平均0・分散1に正規化(標準化)するscipy.stats.zscore()
関数がある。最小値0・最大値1に正規化する関数はない。
引数にリストやndarray
などのarray-likeオブジェクトを指定すると、標準化されたndarray
が返される。
import scipy.stats
l_1d = [0, 1, 2, 3, 4]
print(scipy.stats.zscore(l_1d))
# [-1.41421356 -0.70710678 0. 0.70710678 1.41421356]
print(type(scipy.stats.zscore(l_1d)))
# <class 'numpy.ndarray'>
リストとndarray
の相互変換については以下の記事を参照。
デフォルトは引数ddof
が0
で、$n$で割る標準偏差が使われる。ddof
を1
にすると$n−1$で割る標準偏差が使われる。
print(scipy.stats.zscore(l_1d, ddof=1))
# [-1.26491106 -0.63245553 0. 0.63245553 1.26491106]
リストのリスト(二次元配列)を指定すると、デフォルトでは列ごとに標準化される。引数axis
を1
にすると行ごと、None
にすると全体に対して標準化される。
l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
print(scipy.stats.zscore(l_2d))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
print(scipy.stats.zscore(l_2d, ddof=1))
# [[-1. -1. -1.]
# [ 0. 0. 0.]
# [ 1. 1. 1.]]
print(scipy.stats.zscore(l_2d, axis=1))
# [[-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]]
print(scipy.stats.zscore(l_2d, axis=1, ddof=1))
# [[-1. 0. 1.]
# [-1. 0. 1.]
# [-1. 0. 1.]]
print(scipy.stats.zscore(l_2d, axis=None))
# [[-1.54919334 -1.161895 -0.77459667]
# [-0.38729833 0. 0.38729833]
# [ 0.77459667 1.161895 1.54919334]]
print(scipy.stats.zscore(l_2d, axis=None, ddof=1))
# [[-1.46059349 -1.09544512 -0.73029674]
# [-0.36514837 0. 0.36514837]
# [ 0.73029674 1.09544512 1.46059349]]
scikit-learnで正規化・標準化
scikit-learnでは、sklearn.preprocessing
モジュールを使って正規化・標準化できる。
以下のリスト(一次元配列)、リストのリスト(二次元配列)を例とする。
from sklearn import preprocessing
l_1d = [0, 1, 2, 3, 4]
l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
最小値0・最大値1に正規化: MinMaxScaler, minmax_scale()
最小値0・最大値1に正規化するにはMinMaxScaler
クラスを使う。
インスタンスを生成してfit_transform()
メソッドを呼ぶ。引数にリストやndarray
などのarray-likeオブジェクトを指定すると、正規化されたndarray
が返される。
二次元配列のみが対象で一次元配列を指定するとエラーになる。列ごとに正規化され、行ごとや全体に対する処理はできない。
mm = preprocessing.MinMaxScaler()
# mm.fit_transform(l_1d)
# ValueError: Expected 2D array, got 1D array instead:
print(mm.fit_transform(l_2d))
# [[0. 0. 0. ]
# [0.5 0.5 0.5]
# [1. 1. 1. ]]
print(type(mm.fit_transform(l_2d)))
# <class 'numpy.ndarray'>
直接処理するminmax_scale()
関数もある。
こちらは一次元配列も処理可能。二次元配列に対してはデフォルトで列ごと、引数axis
を1
とすると行ごとの処理となる。全体に対する処理(axis=None
)はできない。
print(preprocessing.minmax_scale(l_1d))
# [0. 0.25 0.5 0.75 1. ]
print(preprocessing.minmax_scale(l_2d))
# [[0. 0. 0. ]
# [0.5 0.5 0.5]
# [1. 1. 1. ]]
print(preprocessing.minmax_scale(l_2d, axis=1))
# [[0. 0.5 1. ]
# [0. 0.5 1. ]
# [0. 0.5 1. ]]
# print(preprocessing.minmax_scale(l_2d, axis=None))
# InvalidParameterError: The 'axis' parameter of minmax_scale must be a int among {0, 1}. Got None instead.
平均0・分散1に正規化(標準化): StandardScaler, scale()
平均0・分散1に正規化(標準化)するにはStandardScaler
クラスかscale()
関数を使う。
- sklearn.preprocessing.StandardScaler — scikit-learn 1.3.2 documentation
- sklearn.preprocessing.scale — scikit-learn 1.3.2 documentation
上述のMinMaxScaler
クラス、minmax_scale()
関数と同様に、StandardScaler
クラスのfit_transform()
メソッドは二次元配列の列ごとの処理のみ。scale()
関数では引数axis=1
で行ごとの処理も可能だが、全体に対する処理(axis=None
)はできない。
常に$n$で割る標準偏差(ddof=0
)が使われる。$n−1$で割る標準偏差(ddof=1
)を使うことはできない。
ss = preprocessing.StandardScaler()
print(ss.fit_transform(l_2d))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
print(type(ss.fit_transform(l_2d)))
# <class 'numpy.ndarray'>
print(preprocessing.scale(l_1d))
# [-1.41421356 -0.70710678 0. 0.70710678 1.41421356]
print(preprocessing.scale(l_2d))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
print(preprocessing.scale(l_2d, axis=1))
# [[-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]]
Pythonのリストを正規化・標準化
SciPy, scikit-learnを使った方法は上述の通り。
最小値と最大値は組み込み関数min()
, max()
、平均と標準偏差はPython標準ライブラリのstatisticsモジュールで算出できる。statistics.pstdev()
は$n$で割る標準偏差で、statistics.stdev()
は$n−1$で割る標準偏差。
これらを利用して正規化・標準化が可能。一次元のリストに対しては、リスト内包表記で各要素を処理する。
- 関連記事: Pythonリスト内包表記の使い方
import statistics
l = [0, 1, 2, 3, 4]
def min_max_normalization(l):
l_min = min(l)
l_max = max(l)
return [(i - l_min) / (l_max - l_min) for i in l]
print(min_max_normalization(l))
# [0.0, 0.25, 0.5, 0.75, 1.0]
def standardization_pstdev(l):
l_mean = statistics.mean(l)
l_pstdev = statistics.pstdev(l)
return [(i - l_mean) / l_pstdev for i in l]
print(standardization_pstdev(l))
# [-1.414213562373095, -0.7071067811865475, 0.0, 0.7071067811865475, 1.414213562373095]
def standardization_stdev(l):
l_mean = statistics.mean(l)
l_stdev = statistics.stdev(l)
return [(i - l_mean) / l_stdev for i in l]
print(standardization_stdev(l))
# [-1.2649110640673518, -0.6324555320336759, 0.0, 0.6324555320336759, 1.2649110640673518]
二次元配列(リストのリスト)に対する例は以下の通り。ただし、二次元配列を処理する場合は後述のNumPyを使ったほうが何かと簡単。
結果を見やすくするために標準ライブラリのpprintモジュールを使っている。
二次元配列(リストのリスト)の行ごとの処理。各リストに上述の一次元配列の処理を行う。
import pprint
l_2d = [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
pprint.pprint([min_max_normalization(l_1d) for l_1d in l_2d], width=40)
# [[0.0, 0.5, 1.0],
# [0.0, 0.5, 1.0],
# [0.0, 0.5, 1.0]]
pprint.pprint([standardization_pstdev(l_1d) for l_1d in l_2d])
# [[-1.224744871391589, 0.0, 1.224744871391589],
# [-1.224744871391589, 0.0, 1.224744871391589],
# [-1.224744871391589, 0.0, 1.224744871391589]]
pprint.pprint([standardization_stdev(l_1d) for l_1d in l_2d], width=40)
# [[-1.0, 0.0, 1.0],
# [-1.0, 0.0, 1.0],
# [-1.0, 0.0, 1.0]]
二次元配列(リストのリスト)の列ごとの処理。転置してから行ごとの処理を行い、再度転置してもとに戻す。
pprint.pprint(
list(zip(*[min_max_normalization(l_1d) for l_1d in list(zip(*l_2d))])),
width=40
)
# [(0.0, 0.0, 0.0),
# (0.5, 0.5, 0.5),
# (1.0, 1.0, 1.0)]
pprint.pprint(list(zip(*[standardization_pstdev(l_1d) for l_1d in list(zip(*l_2d))])))
# [(-1.2247448713915892, -1.2247448713915892, -1.2247448713915892),
# (0.0, 0.0, 0.0),
# (1.2247448713915892, 1.2247448713915892, 1.2247448713915892)]
pprint.pprint(
list(zip(*[standardization_stdev(l_1d) for l_1d in list(zip(*l_2d))])),
width=40
)
# [(-1.0, -1.0, -1.0),
# (0.0, 0.0, 0.0),
# (1.0, 1.0, 1.0)]
二次元配列(リストのリスト)の全体に対する処理。
二次元配列を一次元配列に平坦化して全体の最大値や最小値、平均、標準偏差を算出し、リスト内包表記ですべての要素に対して処理を行う。
def min_max_normalization_all(l_2d):
l_flatten = sum(l_2d, [])
l_min = min(l_flatten)
l_max = max(l_flatten)
return [[(i - l_min) / (l_max - l_min) for i in l_1d] for l_1d in l_2d]
pprint.pprint(min_max_normalization_all(l_2d), width=40)
# [[0.0, 0.125, 0.25],
# [0.375, 0.5, 0.625],
# [0.75, 0.875, 1.0]]
def standardization_pstdev_all(l_2d):
l_flatten = sum(l_2d, [])
l_mean = statistics.mean(l_flatten)
l_pstdev = statistics.pstdev(l_flatten)
return [[(i - l_mean) / l_pstdev for i in l_1d] for l_1d in l_2d]
pprint.pprint(standardization_pstdev_all(l_2d))
# [[-1.5491933384829668, -1.161895003862225, -0.7745966692414834],
# [-0.3872983346207417, 0.0, 0.3872983346207417],
# [0.7745966692414834, 1.161895003862225, 1.5491933384829668]]
def standardization_stdev_all(l_2d):
l_flatten = sum(l_2d, [])
l_mean = statistics.mean(l_flatten)
l_stdev = statistics.stdev(l_flatten)
return [[(i - l_mean) / l_stdev for i in l_1d] for l_1d in l_2d]
pprint.pprint(standardization_stdev_all(l_2d))
# [[-1.4605934866804429, -1.0954451150103321, -0.7302967433402214],
# [-0.3651483716701107, 0.0, 0.3651483716701107],
# [0.7302967433402214, 1.0954451150103321, 1.4605934866804429]]
なお、上の例では簡単のために平坦化にsum()
を使っている。行数が多いとsum()
は遅いので、itertools.chain.from_iterable()
やリスト内包表記を使うとよい。詳細は以下の記事を参照。
NumPy配列ndarrayを正規化・標準化
SciPy, scikit-learnを使う方法とNumPyのメソッドを使う方法を示す。以下の一次元配列と二次元配列を例とする。
import numpy as np
import scipy.stats
from sklearn import preprocessing
a_1d = np.array([0, 1, 2, 3, 4])
print(a_1d)
# [0 1 2 3 4]
a_2d = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(a_2d)
# [[0 1 2]
# [3 4 5]
# [6 7 8]]
SciPy, scikit-learnを利用
scipy.stats.zscore()
引数ddof
とaxis
を指定可能。ddof
は0
がデフォルト。axis
は0
(列ごと、デフォルト)、1
(行ごと)、None
(全体)を指定できる。
print(scipy.stats.zscore(a_1d))
# [-1.41421356 -0.70710678 0. 0.70710678 1.41421356]
print(scipy.stats.zscore(a_2d))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
print(scipy.stats.zscore(a_2d, axis=None, ddof=1))
# [[-1.46059349 -1.09544512 -0.73029674]
# [-0.36514837 0. 0.36514837]
# [ 0.73029674 1.09544512 1.46059349]]
sklearn.preprocessing
fit_transform()
メソッドは二次元配列の列ごとの処理のみ。
minmax_scale()
, scale()
関数は一次元配列も二次元配列も処理可能。二次元配列に対してはデフォルトは列ごと。引数axis=1
で行ごとの処理も可能だが、全体に対する処理(axis=None
)はできない。
標準化において引数ddof
は指定できず、常に$n$で割る標準偏差(ddof=0
)となる。
mm = preprocessing.MinMaxScaler()
print(mm.fit_transform(a_2d))
# [[0. 0. 0. ]
# [0.5 0.5 0.5]
# [1. 1. 1. ]]
print(preprocessing.minmax_scale(a_1d))
# [0. 0.25 0.5 0.75 1. ]
print(preprocessing.minmax_scale(a_2d, axis=1))
# [[0. 0.5 1. ]
# [0. 0.5 1. ]
# [0. 0.5 1. ]]
ss = preprocessing.StandardScaler()
print(ss.fit_transform(a_2d))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
print(preprocessing.scale(a_1d))
# [-1.41421356 -0.70710678 0. 0.70710678 1.41421356]
print(preprocessing.scale(a_2d, axis=1))
# [[-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]]
NumPyのメソッドを利用
NumPy配列ndarray
のmin()
, max()
, mean()
, std()
メソッドで最小値、最大値、平均、標準偏差を算出できる。
これらのメソッドでは引数axis
によって配列全体、行ごと、列ごとの値を算出できる。デフォルトはaxis=None
で配列全体。また、引数keepdims=True
とすると次元が保持され、元の配列とそのまま減算が可能。
std()
はデフォルトでは引数ddof=0
で$n$で割る標準偏差が使われるが、引数ddof=1
とすると$n−1$で割る標準偏差が使われる。
一次元配列に対しても二次元配列に対しても同じ関数を適用可能。
def min_max_normalization(a, axis=None):
a_min = a.min(axis=axis, keepdims=True)
a_max = a.max(axis=axis, keepdims=True)
return (a - a_min) / (a_max - a_min)
print(min_max_normalization(a_1d))
# [0. 0.25 0.5 0.75 1. ]
print(min_max_normalization(a_2d))
# [[0. 0.125 0.25 ]
# [0.375 0.5 0.625]
# [0.75 0.875 1. ]]
print(min_max_normalization(a_2d, axis=0))
# [[0. 0. 0. ]
# [0.5 0.5 0.5]
# [1. 1. 1. ]]
print(min_max_normalization(a_2d, axis=1))
# [[0. 0.5 1. ]
# [0. 0.5 1. ]
# [0. 0.5 1. ]]
def standardization(a, axis=None, ddof=0):
a_mean = a.mean(axis=axis, keepdims=True)
a_std = a.std(axis=axis, keepdims=True, ddof=ddof)
return (a - a_mean) / a_std
print(standardization(a_1d))
# [-1.41421356 -0.70710678 0. 0.70710678 1.41421356]
print(standardization(a_1d, ddof=1))
# [-1.26491106 -0.63245553 0. 0.63245553 1.26491106]
print(standardization(a_2d))
# [[-1.54919334 -1.161895 -0.77459667]
# [-0.38729833 0. 0.38729833]
# [ 0.77459667 1.161895 1.54919334]]
print(standardization(a_2d, ddof=1))
# [[-1.46059349 -1.09544512 -0.73029674]
# [-0.36514837 0. 0.36514837]
# [ 0.73029674 1.09544512 1.46059349]]
print(standardization(a_2d, axis=0))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
print(standardization(a_2d, axis=0, ddof=1))
# [[-1. -1. -1.]
# [ 0. 0. 0.]
# [ 1. 1. 1.]]
print(standardization(a_2d, axis=1))
# [[-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]
# [-1.22474487 0. 1.22474487]]
print(standardization(a_2d, axis=1, ddof=1))
# [[-1. 0. 1.]
# [-1. 0. 1.]
# [-1. 0. 1.]]
pandasのDataFrame, Seriesを正規化・標準化
SciPy, scikit-learnを使う方法とpandasのメソッドを使う方法を示す。
以下のDataFrame
およびその列(Series
)を例とする。
import pandas as pd
import scipy.stats
from sklearn import preprocessing
df = pd.DataFrame([[0, 1, 2], [3, 4, 5], [6, 7, 8]],
columns=['col1', 'col2', 'col3'],
index=['row1', 'row2', 'row3'])
print(df)
# col1 col2 col3
# row1 0 1 2
# row2 3 4 5
# row3 6 7 8
s = df['col1']
print(s)
# row1 0
# row2 3
# row3 6
# Name: col1, dtype: int64
数値以外の列が含まれている場合は数値列のみを抽出してから処理する。以下の記事を参照。
Scipy, scikit-learnを利用
scipy.stats.zscore()
scipy.stats.zscore()
の引数にDataFrame
を指定すると、標準化されたDataFrame
が返される。引数axis
, ddof
を指定できるが、SciPy 1.11.4時点ではaxis=None
には対応していない。
print(scipy.stats.zscore(df))
# col1 col2 col3
# row1 -1.224745 -1.224745 -1.224745
# row2 0.000000 0.000000 0.000000
# row3 1.224745 1.224745 1.224745
print(scipy.stats.zscore(df, axis=1, ddof=1))
# col1 col2 col3
# row1 -1.0 0.0 1.0
# row2 -1.0 0.0 1.0
# row3 -1.0 0.0 1.0
# print(scipy.stats.zscore(df, axis=None))
# ValueError: Unable to coerce to DataFrame, shape must be (3, 3): given (1, 1)
axis=None
で処理したい場合は、DataFrame
のvalues
属性でndarray
を取得してscipy.stats.zscore()
に渡す。標準化されたndarray
が得られる。
print(scipy.stats.zscore(df.values, axis=None))
# [[-1.54919334 -1.161895 -0.77459667]
# [-0.38729833 0. 0.38729833]
# [ 0.77459667 1.161895 1.54919334]]
コンストラクタpd.DataFrame()
の第一引数data
にそのndarray
、引数index
とcolumns
に元のindex
とcolumns
を指定すれば、標準化されたDataFrame
が得られる。
print(pd.DataFrame(scipy.stats.zscore(df.values, axis=None),
index=df.index, columns=df.columns))
# col1 col2 col3
# row1 -1.549193 -1.161895 -0.774597
# row2 -0.387298 0.000000 0.387298
# row3 0.774597 1.161895 1.549193
Series
に対してもscipy.stats.zscore()
がそのまま使える。
print(scipy.stats.zscore(s))
# row1 -1.224745
# row2 0.000000
# row3 1.224745
# Name: col1, dtype: float64
sklearn.preprocessing
fit_transform()
メソッドも、minmax_scale()
, scale()
関数も、DataFrame
を引数に指定すると、結果がndarray
として返される。
mm = preprocessing.MinMaxScaler()
print(mm.fit_transform(df))
# [[0. 0. 0. ]
# [0.5 0.5 0.5]
# [1. 1. 1. ]]
print(preprocessing.minmax_scale(df))
# [[0. 0. 0. ]
# [0.5 0.5 0.5]
# [1. 1. 1. ]]
print(preprocessing.scale(df))
# [[-1.22474487 -1.22474487 -1.22474487]
# [ 0. 0. 0. ]
# [ 1.22474487 1.22474487 1.22474487]]
fit_transform()
は列ごとの処理のみ。minmax_scale()
, scale()
はデフォルトは列ごと。引数axis=1
で行ごとの処理も可能だが、全体に対する処理(axis=None
)は不可。
DataFrame
にするにはコンストラクタpd.DataFrame()
を使う。
print(pd.DataFrame(preprocessing.minmax_scale(df, axis=1),
index=df.index, columns=df.columns))
# col1 col2 col3
# row1 0.0 0.5 1.0
# row2 0.0 0.5 1.0
# row3 0.0 0.5 1.0
fit_transform()
メソッドはSeries
には使えないが、minmax_scale()
, scale()
関数は使える。DataFrame
の場合と同様にndarray
が返されるので、Series
にするにはコンストラクタpd.Series()
を使う。
# print(mm.fit_transform(s))
# ValueError: Expected 2D array, got 1D array instead:
print(preprocessing.minmax_scale(s))
# [0. 0.5 1. ]
print(pd.Series(preprocessing.minmax_scale(s), index=s.index, name=s.name))
# row1 0.0
# row2 0.5
# row3 1.0
# Name: col1, dtype: float64
DataFrame
に新たな列として追加する場合はndarray
のまま指定可能。
df_copy = df.copy()
df_copy['col1_min_max'] = preprocessing.minmax_scale(s)
df_copy['col1_standardization'] = preprocessing.scale(s)
print(df_copy)
# col1 col2 col3 col1_min_max col1_standardization
# row1 0 1 2 0.0 -1.224745
# row2 3 4 5 0.5 0.000000
# row3 6 7 8 1.0 1.224745
標準化において引数ddof
は指定できず、常に$n$で割る標準偏差(ddof=0
)となる。
pandasのメソッドを利用
DataFrame
のmin()
, max()
, mean()
, std()
メソッドで最小値、最大値、平均、標準偏差を算出可能。
デフォルトは引数axis=0
で、列ごとに処理される。DataFrame
のstd()
メソッドは引数ddof=1
がデフォルト。ndarray
のstd()
メソッド(ddof=0
がデフォルト)と異なるので注意。
print((df - df.min()) / (df.max() - df.min()))
# col1 col2 col3
# row1 0.0 0.0 0.0
# row2 0.5 0.5 0.5
# row3 1.0 1.0 1.0
print((df - df.mean()) / df.std())
# col1 col2 col3
# row1 -1.0 -1.0 -1.0
# row2 0.0 0.0 0.0
# row3 1.0 1.0 1.0
print((df - df.mean()) / df.std(ddof=0))
# col1 col2 col3
# row1 -1.224745 -1.224745 -1.224745
# row2 0.000000 0.000000 0.000000
# row3 1.224745 1.224745 1.224745
各メソッドは引数axis=1
で行ごとの処理になるが、NumPyの引数keepdims
に相当する引数がないため、DataFrame
との減算ができない。ここでは、.T
で転置してから列ごとに処理して再度転置してもとに戻している。もっといい方法があるかもしれない。
print(((df.T - df.T.min()) / (df.T.max() - df.T.min())).T)
# col1 col2 col3
# row1 0.0 0.5 1.0
# row2 0.0 0.5 1.0
# row3 0.0 0.5 1.0
print(((df.T - df.T.mean()) / df.T.std()).T)
# col1 col2 col3
# row1 -1.0 0.0 1.0
# row2 -1.0 0.0 1.0
# row3 -1.0 0.0 1.0
print(((df.T - df.T.mean()) / df.T.std(ddof=0)).T)
# col1 col2 col3
# row1 -1.224745 0.0 1.224745
# row2 -1.224745 0.0 1.224745
# row3 -1.224745 0.0 1.224745
pandas 2.0.0からmin()
, max()
, mean()
がaxis=None
に対応した。未対応のstd()
はvalues
属性でndarray
を取得してstd()
メソッドを呼ぶ。ndarray
のstd()
はデフォルトがddof=0
なので注意。
print((df - df.min(None)) / (df.max(None) - df.min(None)))
# col1 col2 col3
# row1 0.000 0.125 0.250
# row2 0.375 0.500 0.625
# row3 0.750 0.875 1.000
print((df - df.mean(None)) / df.values.std())
# col1 col2 col3
# row1 -1.549193 -1.161895 -0.774597
# row2 -0.387298 0.000000 0.387298
# row3 0.774597 1.161895 1.549193
print((df - df.mean(None)) / df.values.std(ddof=1))
# col1 col2 col3
# row1 -1.460593 -1.095445 -0.730297
# row2 -0.365148 0.000000 0.365148
# row3 0.730297 1.095445 1.460593
Series
もmin()
, max()
, mean()
, std()
メソッドが利用可能。
print((s - s.min()) / (s.max() - s.min()))
# row1 0.0
# row2 0.5
# row3 1.0
# Name: col1, dtype: float64
print((s - s.mean()) / s.std())
# row1 -1.0
# row2 0.0
# row3 1.0
# Name: col1, dtype: float64
print((s - s.mean()) / s.std(ddof=0))
# row1 -1.224745
# row2 0.000000
# row3 1.224745
# Name: col1, dtype: float64