機械学習のためのデータ前処理:数値データの「かたより」をなくす変換方法【Pandas入門】
はじめに:データのかたよりがなぜ問題になるのか
機械学習モデルを構築する際、データの「前処理」は非常に重要なステップです。生データには、モデルがそのままではうまく扱えない様々な問題が含まれていることがあります。その一つに、数値データの「かたより」、統計的な言葉で言うと「歪み(わいゆみ)」があります。
たとえば、多くの人の年収データを見てみましょう。多くの人は比較的低い年収ですが、一部には非常に高い年収の人もいます。これをグラフにすると、低い年収のほうに多くのデータが集まり、高い年収のほうにデータが少なく、裾野が広がるような形になります。このように、データの大部分が一方に偏っている状態を「かたよりがある」「歪んでいる」と言います。
なぜこの「かたより」が問題なのでしょうか。特に、線形回帰やロジスティック回帰といった線形モデルや、いくつかの機械学習アルゴリズムは、入力されるデータが特定の分布(たとえば正規分布に近い形)に従うことを前提としている場合があります。データにかたよりがあると、これらのモデルがうまく学習できず、予測精度が低下する可能性があるのです。
この記事では、このような数値データの「かたより」をどのように見つけ、そしてどのように「かたより」をなくす(あるいは軽減する)ための変換を行うかを、Pythonのpandasライブラリを使った具体的なコード例とともに解説します。
データのかたより(歪み)を理解する
データのかたよりを理解するためには、データの分布を視覚的に確認するのが一番分かりやすい方法です。代表的なのはヒストグラムを使う方法です。
ヒストグラムは、データの値をいくつかの区間に分け、それぞれの区間に含まれるデータの個数を棒グラフで表したものです。もしデータにかたよりがない場合、ヒストグラムは左右対称の釣鐘型(正規分布のような形)に近くなります。しかし、かたよりがある場合は、棒グラフの山が左右どちらかに偏り、長い裾を引くような形になります。
例として、以下のような人工的なデータ(年収を模したデータとします)を考えてみましょう。多くの値が小さい側に集中しているデータです。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
# サンプルデータの生成 (右に歪んだデータ)
# ランダムな対数正規分布に従うデータを生成し、指数を取ることで歪みを作る
np.random.seed(42) # 再現性のため
data = np.random.lognormal(mean=2.0, sigma=1.0, size=1000) # mean=2.0, sigma=1.0はパラメータ
df = pd.DataFrame({'value': data})
# ヒストグラムの表示
plt.figure(figsize=(8, 5))
sns.histplot(df['value'], kde=True, bins=50)
plt.title('元のデータのヒストグラム')
plt.xlabel('値')
plt.ylabel('度数')
plt.show()
# 基本統計量の確認(歪度 skewness を確認)
print("元のデータの歪度 (Skewness):")
print(df['value'].skew())
上記のコードを実行すると、データの多くが小さい値に集まり、右側に長い裾を引くようなヒストグラムが表示されるでしょう。また、skew()
メソッドで計算される「歪度」という値も大きくなる(正の値で大きくなる場合は右に歪んでいる)ことが確認できます。この歪度が大きい場合、データにはかたよりがある、と判断できます。
かたよりをなくすための代表的な変換方法
データにかたよりがあることが分かったら、次にそのかたよりを軽減するための「変換」を行います。ここでは、代表的な変換方法をいくつかご紹介します。
1. 対数変換 (Log Transform)
対数変換は、数値データにかたよりがある場合に最もよく使われる手法の一つです。元の値 $x$ を $\log(x)$ に変換します。特に、元のデータが大きな値になるにつれて散らばりが大きくなる(例:年収データなど)場合に有効です。
注意点: 対数変換は、元のデータが正の値である場合にのみ使用できます。0や負の値を含むデータには直接適用できません。もし0が含まれる場合は、$\log(x+1)$ のように微小な値(ここでは1としています)を加えてから変換することがあります。
PandasとNumPyを使ったコード例は以下の通りです。
# 対数変換 (np.log または np.log1p を使用)
# 元の値に0が含まれる可能性を考慮し、log1p (log(x+1)) を使用することが多い
df['value_log'] = np.log1p(df['value']) # np.log1p(x) は np.log(x+1) と同じ
# 変換後のデータのヒストグラム表示
plt.figure(figsize=(8, 5))
sns.histplot(df['value_log'], kde=True, bins=50)
plt.title('対数変換後のデータのヒストグラム')
plt.xlabel('対数変換後の値')
plt.ylabel('度数')
plt.show()
# 変換後の歪度を確認
print("対数変換後の歪度 (Skewness):")
print(df['value_log'].skew())
対数変換後のヒストグラムを見ると、元のデータに比べてかたよりが軽減され、より左右対称に近い形になっていることが分かります。歪度の値も元のデータより0に近くなっているでしょう。
2. 平方根変換 (Square Root Transform)
平方根変換は、元の値 $x$ を $\sqrt{x}$ に変換します。対数変換と同様に、データの散らばりを抑え、かたよりを軽減する効果があります。
対数変換が使えない場合、たとえば元のデータに0が含まれる場合に使われることがあります。ただし、負の値には適用できません。
PandasとNumPyを使ったコード例です。
# 平方根変換 (np.sqrt を使用)
# 元の値が非負である必要あり
df['value_sqrt'] = np.sqrt(df['value'])
# 変換後のデータのヒストグラム表示
plt.figure(figsize=(8, 5))
sns.histplot(df['value_sqrt'], kde=True, bins=50)
plt.title('平方根変換後のデータのヒストグラム')
plt.xlabel('平方根変換後の値')
plt.ylabel('度数')
plt.show()
# 変換後の歪度を確認
print("平方根変換後の歪度 (Skewness):")
print(df['value_sqrt'].skew())
平方根変換でも、ある程度のかたよりの軽減効果が見られるでしょう。対数変換と比較して、どちらがより効果的にかたよりを軽減できるかは、データの性質によります。
3. Box-Cox変換
Box-Cox変換は、対数変換や平方根変換を含む、より一般的な変換手法です。最適な変換の度合いをデータ自身から決定します。元の値 $x$ を以下の式で変換します(ただし、$\lambda \ne 0$ の場合)。
$ y = \frac{x^\lambda - 1}{\lambda} $
$\lambda=0$ の場合は対数変換(の定数倍シフト)に一致します。Box-Cox変換は、データが正の値である場合にのみ使用できます。また、この変換を行うには、Scikit-learnライブラリのPowerTransformer
(method='box-cox'
を指定)などを使用するのが一般的です。
ここでは簡単なコード例と概念的な説明に留めます。
# Box-Cox変換 (Scikit-learnのPowerTransformerを使用)
from sklearn.preprocessing import PowerTransformer
# Box-Cox変換は正の値にのみ適用可能
# 0以下の値がある場合は別途対応が必要
# 今回のサンプルデータは正の値のみのためそのまま適用
pt = PowerTransformer(method='box-cox', standardize=False) # standardize=Falseで標準化はしない
# numpy配列に変換してから変換を適用
df['value_boxcox'] = pt.fit_transform(df[['value']])
# 変換後のデータのヒストグラム表示
plt.figure(figsize=(8, 5))
sns.histplot(df['value_boxcox'], kde=True, bins=50)
plt.title('Box-Cox変換後のデータのヒストグラム')
plt.xlabel('Box-Cox変換後の値')
plt.ylabel('度数')
plt.show()
# 変換後の歪度を確認
print("Box-Cox変換後の歪度 (Skewness):")
print(df['value_boxcox'].skew())
Box-Cox変換は、データに応じて最適な変換を自動的に選択してくれるため、対数変換や平方根変換よりも効果的にかたよりを軽減できる場合があります。コード例のようにScikit-learnを使うと比較的簡単に実装できます。
変換後の注意点
数値データの分布を変換してかたよりを軽減することは、機械学習モデルの性能向上に繋がります。しかし、変換を行ったデータを使ってモデルを学習させ、予測を行った場合、その予測結果は変換されたスケールでの値になります。
たとえば、年収を対数変換してモデルを作り、予測結果が「6」という値だったとします。これは対数変換後の値なので、元の年収に戻すには逆変換(対数変換の場合は指数関数)が必要です。
元の値 $x$ を $\log(x+1)$ で変換した場合、予測結果 $y_{pred}$ を元のスケールに戻すには、$e^{y_{pred}} - 1$ という計算が必要になります。
モデルによっては、予測結果を元のスケールに戻す必要がない場合もありますが(例:分類問題)、特に回帰問題で元の単位での予測値が必要な場合は、この逆変換を忘れないように注意が必要です。
まとめ
この記事では、機械学習のためのデータ前処理として、数値データの「かたより」(歪み)とその対処法について解説しました。
- 多くの機械学習モデルは、入力データの分布が特定の形に近い場合に性能を発揮しやすいです。
- 数値データにかたよりがある場合、ヒストグラムや歪度を確認することでその状態を把握できます。
- 代表的なかたよりを軽減する変換方法として、対数変換、平方根変換、そしてBox-Cox変換があります。
- これらの変換を適用することで、データの分布をよりモデルが扱いやすい形に近づけることができます。
- 変換したデータでモデルを学習・予測する場合、予測結果を元のスケールに戻す必要があるかを確認し、必要であれば逆変換を行います。
データの性質や目的とするモデルによって、最適な変換方法や、そもそも変換が必要かどうかも異なります。まずはデータの分布をしっかりと観察し、この記事で紹介した手法を試してみてください。データ前処理の引き出しを増やすことが、より良い機械学習モデル構築への一歩となります。