Pythonで正規化・標準化(リスト、NumPy配列、pandas.DataFrame)

Modified: | Tags: Python, リスト, NumPy, pandas, SciPy, scikit-learn

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を例にそれぞれの使い方を説明する。ndarrayDataFrameに対する例、および、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の相互変換については以下の記事を参照。

デフォルトは引数ddof0で、$n$で割る標準偏差が使われる。ddof1にすると$n−1$で割る標準偏差が使われる。

print(scipy.stats.zscore(l_1d, ddof=1))
# [-1.26491106 -0.63245553  0.          0.63245553  1.26491106]

リストのリスト(二次元配列)を指定すると、デフォルトでは列ごとに標準化される。引数axis1にすると行ごと、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()関数もある。

こちらは一次元配列も処理可能。二次元配列に対してはデフォルトで列ごと、引数axis1とすると行ごとの処理となる。全体に対する処理(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()関数を使う。

上述の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$で割る標準偏差。

これらを利用して正規化・標準化が可能。一次元のリストに対しては、リスト内包表記で各要素を処理する。

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()

引数ddofaxisを指定可能。ddof0がデフォルト。axis0(列ごと、デフォルト)、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配列ndarraymin(), 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で処理したい場合は、DataFramevalues属性で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、引数indexcolumnsに元のindexcolumnsを指定すれば、標準化された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のメソッドを利用

DataFramemin(), max(), mean(), std()メソッドで最小値、最大値、平均、標準偏差を算出可能。

デフォルトは引数axis=0で、列ごとに処理される。DataFramestd()メソッドは引数ddof=1がデフォルト。ndarraystd()メソッド(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()メソッドを呼ぶ。ndarraystd()はデフォルトが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

Seriesmin(), 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

関連カテゴリー

関連記事