データ処理における3つの主要な正規化・標準化手法の計算式と特徴の解説

こんにちは。ゆうせいです。

収集したデータには、数値の大きさや単位がバラバラに含まれていることが多々あります。例えば、身長(cm)と体重(kg)では数値の範囲が異なるため、そのまま比較すると正確な分析が困難です。こうしたデータの「尺度(ものさし)」を揃える処理を正規化や標準化と呼びます。

提示されたプログラムに含まれる3つの計算手法について、それぞれの定義と仕組みを解説します。

1. 最小最大正規化(Min-Max Normalization)

最小最大正規化は、データの全範囲を0から1の間に収める手法です。

仕組みと比喩

データの最小値を0、最大値を1として、その間の値を相対的な位置で表現します。これは、100点満点のテストと50点満点のテストの結果を、どちらも「達成率(パーセント)」に換算して比較することに似ています。

計算式

各データ $x$ に対して、新しい値 $x'$ は以下の式で求められます。

x' = \frac{x - \min(x)}{\max(x) - \min(x)}

ここで、$\min(x)$ はデータ全体の最小値、$\max(x)$ は最大値を示します。

メリットとデメリット

  • メリット:すべてのデータが0から1の範囲に収まるため、計算の扱いが容易になります。
  • デメリット:外れ値(極端に大きい、または小さい値)が1つでもあると、他の大部分のデータが狭い範囲に固まってしまい、情報の差異が失われます。

2. 標準化(Z-score Normalization)

標準化は、データの平均値を0、標準偏差を1にする手法です。

仕組みと比喩

データの平均からのズレを基準にする方法です。これは、学校のテストで使われる「偏差値」をイメージすると分かりやすいでしょう。平均点にいる人を0とし、そこからどれくらい離れているかを数値化します。

計算式

平均を $\mu$、標準偏差を $\sigma$ とすると、新しい値 $z$ は以下の式で計算されます。

z = \frac{x - \mu}{\sigma}

メリットとデメリット

  • メリット:外れ値の影響を最小最大正規化よりも受けにくく、多くの統計手法や機械学習アルゴリズムに適しています。
  • デメリット:変換後のデータの範囲が一定(0から1など)にならないため、最大値や最小値が事前に予測できない場合があります。

3. L2正規化(L2 Normalization / Unit Vector)

L2正規化は、データ群を1つのベクトル(矢印)と見なし、その長さ(ノルム)を1にする手法です。

仕組みと比喩

データの数値そのものよりも、「データの成分比率」や「方向」を重視します。例えば、料理のレシピで「塩1g、砂糖2g」という情報を、「塩1:砂糖2」という比率に変換するようなものです。全体の分量がどれだけ増えても、この比率(方向)は変わりません。

計算式

各要素の二乗和の平方根(L2ノルム)で各データを割ります。

x' = \frac{x}{\sqrt{\sum_{i=1}^{n} x_{i}^2}}

プログラム内では math.norm 関数を用いてこの分母の値を算出しています。

メリットとデメリット

  • メリット:データの大きさ(スケール)に左右されず、複数の項目間の「パターンの類似性」を比較するのに非常に有効です。
  • デメリット:個々の数値が持っていた絶対的な大きさの情報は完全に失われます。

まとめ

これら3つの手法は、分析の目的に応じて使い分けることが重要です。

  • データの範囲を厳密に揃えたい場合は「最小最大正規化」
  • データの分布や偏差を重視する場合は「標準化」
  • データの比率や方向性を比較したい場合は「L2正規化」

次の学習ステップとして、まずは手元の数値をこれらの式に当てはめて計算し、結果がどのように変化するかを確認してください。その後、実際のプログラミング言語を用いて、外れ値を含んだデータセットで各手法の挙動の違いをシミュレーションしてみることを推奨します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Advanced Data Normalizer</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/11.8.0/math.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 2rem; background: #f0f2f5; }
        .container { max-width: 900px; margin: auto; background: white; padding: 2rem; border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.08); }
        .controls { display: grid; grid-template-columns: 1fr 1fr auto; gap: 1rem; margin-bottom: 2rem; align-items: end; }
        .control-group { display: flex; flex-direction: column; gap: 5px; }
        input, select, button { padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; }
        button { background: #007bff; color: white; border: none; font-weight: bold; transition: 0.2s; }
        button:hover { background: #0056b3; }
        .info-box { background: #e7f3ff; padding: 10px; border-radius: 6px; margin-bottom: 1rem; font-size: 0.85rem; color: #0c5460; }
        table { width: 100%; border-collapse: collapse; margin-top: 1rem; }
        th, td { border-bottom: 1px solid #eee; padding: 12px; text-align: right; }
        th { background: #fafafa; color: #666; }
        canvas { margin-top: 2rem; background: #fff; }
    </style>
</head>
<body>

<div class="container">
    <h2>📊 データ正規化 & L2スケーリング</h2>
    
    <div class="info-box">
        <strong>L2正規化 (Unit Vector):</strong> 各データをベクトルの成分と考え、全体のノルム(長さ)が1になるよう変換します。これはコサイン類似度を算出する前段階の処理と同じです。
    </div>

    <div class="controls">
        <div class="control-group">
            <label>データ (カンマ区切り):</label>
            <input type="text" id="dataInput" value="3, 4, 5, 2, 8">
        </div>
        <div class="control-group">
            <label>手法:</label>
            <select id="method">
                <option value="minmax">最小最大正規化 (0-1)</option>
                <option value="zscore">標準化 (Z-score)</option>
                <option value="l2">L2正規化 (Unit Vector)</option>
            </select>
        </div>
        <button onclick="processData()">実行</button>
    </div>

    <div id="resultArea" style="display:none;">
        <canvas id="myChart"></canvas>
        <table>
            <thead><tr><th>元の値</th><th>処理後の値</th></tr></thead>
            <tbody id="resultBody"></tbody>
        </table>
    </div>
</div>

<script>
let chartInstance = null;

function processData() {
    const input = document.getElementById('dataInput').value;
    const method = document.getElementById('method').value;
    const rawData = input.split(',').map(x => parseFloat(x.trim())).filter(x => !isNaN(x));
    
    if (rawData.length < 2) { alert("2つ以上の数値を入力してください"); return; }

    let processedData = [];
    let formula = "";

    if (method === 'minmax') {
        const min = math.min(rawData);
        const max = math.max(rawData);
        processedData = rawData.map(x => (x - min) / (max - min));
    } 
    else if (method === 'zscore') {
        const mean = math.mean(rawData);
        const std = math.std(rawData);
        processedData = rawData.map(x => (x - mean) / std);
    } 
    else if (method === 'l2') {
        // L2正規化: x / sqrt(sum(x_i^2))
        const norm = math.norm(rawData); // ベクトルの長さを計算
        processedData = rawData.map(x => x / norm);
    }

    displayResults(rawData, processedData);
    updateChart(rawData, processedData);
}

function displayResults(raw, processed) {
    const tbody = document.getElementById('resultBody');
    tbody.innerHTML = '';
    raw.forEach((val, i) => {
        const row = `<tr><td>${val}</td><td><strong>${processed[i].toFixed(4)}</strong></td></tr>`;
        tbody.innerHTML += row;
    });
    document.getElementById('resultArea').style.display = 'block';
}

function updateChart(raw, processed) {
    const ctx = document.getElementById('myChart').getContext('2d');
    if (chartInstance) chartInstance.destroy();

    chartInstance = new Chart(ctx, {
        type: 'bar',
        data: {
            labels: raw.map((_, i) => `Item ${i + 1}`),
            datasets: [{
                label: '元の値',
                data: raw,
                backgroundColor: '#dee2e6',
                yAxisID: 'y',
            }, {
                label: '正規化後の値',
                data: processed,
                backgroundColor: '#007bff',
                yAxisID: 'y1',
            }]
        },
        options: {
            responsive: true,
            scales: {
                y: { type: 'linear', position: 'left', title: { display: true, text: 'Original Scale' } },
                y1: { type: 'linear', position: 'right', grid: { drawOnChartArea: false }, title: { display: true, text: 'Normalized Scale' } }
            }
        }
    });
}
</script>

</body>
</html>

今回解説したアルゴリズムの選択が、データ分析の精度を大きく左右する第一歩となります。

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。