論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
質的変数
Qualitative Variable
基礎統計
別称: カテゴリ変数

🔖 キーワード索引

質的変数(Qualitative Variable / Categorical Variable)を深く理解するために、 本ページで扱う主要キーワードを並べます:

📍 文脈 🎨 直感 📐 数式 🔬 数式を言葉で読み解く 🧮 実値計算 🐍 Python 実装 ⚠️ 落とし穴 🌐 関連手法 🔗 関連用語 📚 関連教材

💡 30秒で分かる結論

🍰 まずはやさしく

質的変数は、グループ分けするためのラベルです。

データの種類を整理するために使います。

血液型や都道府県の名前などが例です。

この章では質的変数の基本と使い方を読みます。

カテゴリで表され、 数値演算しない変数

💡 30 秒で分かる結論

📍 文脈ボックス:あなたが今見ているもの

🍰 まずはやさしく

変数は、データの種類によって分かれます。

今どの種類のデータを扱うかを確認します。

身長などの数値ではなく、名前などの分類です。

質的変数が全体のどこに位置するかを読みます。

「変数」は統計学・データ分析の根本概念で、 大きく以下に分類されます:

SSDSE-B-2026 でいえば、 地域コード(R01000, R02000, ...)都道府県名は質的変数の典型例。 一方「総人口」「出生数」は量的変数。 質的変数を「いかに数値化して機械学習に投入するか」が前処理の中心課題です。

🎨 直感で掴む

🍰 まずはやさしく

質的変数は、箱に貼った名札のようなものです。

中身を正しく分けるために使います。

学年や部活動の名前などが例です。

名札の種類による違いについて読みます。

質的変数を「箱の名札」と考えると分かりやすいです。 47 個の箱に「北海道」「青森」...「沖縄」とラベルを貼ったとき、 ラベル自体は数値ではなく名前。 箱同士の「順序」も「距離」もなく、 単に 違う箱 としてのみ区別されます。

これに対して血液型(A/B/AB/O)は順序すらない名義尺度、 学年(1年, 2年, 3年, 4年)は順序があるが「3年 − 1年 = 2」が意味を持たない順序尺度です。

分類順序距離平均
名義尺度都道府県、 血液型、 性別、 国籍✗ (最頻値のみ)
順序尺度学年、 評価ランク、 競技順位✗ (中央値推奨)
間隔尺度気温(℃)、 西暦
比例尺度人口、 GDP、 速度○ (絶対零点あり)○ (幾何平均も可)

質的変数(名義 + 順序)はこの表の上 2 行に対応します。

SSDSE-B-2026 での代表例: 「都道府県コード」(R01000=北海道 〜 R47000=沖縄) は典型的な名義変数です。 コード番号は便宜的な ID で、 「北海道 (1) + 沖縄 (47) = 48」のような演算には何の意味もありません。 一方「人口総数 A1101」は量的変数(比例尺度)なので、 平均・合計・伸び率が正当に計算できます。 質的変数を集計するときは「最頻値(mode)」「度数(count)」「割合(proportion)」が中心で、 平均・分散は 定義されない のがポイントです。

機械学習での扱い: 質的変数は数値で表現する必要があります。 名義変数は One-Hot エンコーディング(47 都道府県なら 47 次元の 0/1 ベクトル)が標準、 順序変数は 順位スコア(学年 1,2,3,4 のまま)か Ordinal Encoding を使います。 性別を「男=1, 女=2」と数字を振っただけでは線形回帰が「女は男の 2 倍」と誤解するため、 必ず One-Hot 化しなければなりません。

誤用の典型: 「血液型 A=1, B=2, AB=3, O=4」として平均を計算する。 結果 (1+2+3+4)/4 = 2.5 は「BとABの中間」を意味するかと聞かれて答えられません。 名義変数で意味があるのは「A 型が全体の 40%」「最頻値は A」だけです。

🎮 触って理解する

質的変数は「カテゴリごとに数える」ことしかできません。 下のスライダー(または棒を直接ドラッグ)で各カテゴリの度数を変えてみましょう。 棒グラフ・円グラフ・度数・割合・最頻値がリアルタイムに更新されます。 同時に「平均を計算しようとするとどうなるか」も表示され、 質的変数で平均・分散が無意味であることを体感できます。 名義尺度(好きな色)と順序尺度(満足度)を切り替えて、 両者の違いも確かめてください。

棒グラフ(度数) ★ = 最頻値
↑ 棒を上下にドラッグ/タッチしても度数を変えられます
円グラフ(割合)

🧭 直感:質的変数は「名札で仕分けて数える」だけ

上のグラフで分かるように、 質的変数でできる操作は「同じ名札の箱に入れて数える(度数)」「全体に占める比を出す(割合)」「一番多い箱を選ぶ(最頻値)」の 3 つが中心です。 カテゴリ同士に大小の関係が無い名義尺度では、 中央値すら定義できません(並べる基準が無いため)。 順序尺度になると「並び順」だけは意味を持つので中央値が使えるようになりますが、 隣り合うカテゴリの間隔が等しい保証はないため、 平均はなお慎重に扱う必要があります。

⚠️ よくある落とし穴:数値ラベルを量的変数と誤認する

「赤=1, 青=2, 緑=3, 黄=4」のようにカテゴリへ便宜的な番号を振ると、 その番号で平均が計算できてしまうのが罠です。 上のパネルの「⚠️ 平均のワナ」欄はまさにこの計算を実演しています。 例えば赤と黄だけに票が集まっても平均は 2.5 付近になり、 「青と緑の中間が好き」という誰も選んでいない架空の代表値が現れます。 分散も同様に無意味です。 番号は識別子(ID)に過ぎず、 順序も距離も持たないことを忘れないでください。 集計してよいのは度数・割合・最頻値、 そしてクロス集計だけです。

🚀 発展:エンコーディングと連関の測り方

機械学習に質的変数を入れるには数値化(エンコーディング)が必要です。 名義尺度は One-Hot エンコーディング でカテゴリ数ぶんの 0/1 列に展開し、 順序尺度は並び順を保つ Ordinal Encoding を使います。 2 つの質的変数の関連は平均・相関係数では測れないため、 クロス集計表を作り カイ二乗(χ²)検定で独立性を調べ、 関連の強さは Cramér's V で 0〜1 に規格化して評価します(このページの「実値で計算してみる」節に SSDSE-B-2026 での実例があります)。 尺度の区別は 名義尺度順序尺度尺度水準 の各ページ、 対になる概念は 量的変数 を参照してください。

📐 定義

🍰 まずはやさしく

質的変数は、計算できないカテゴリのことです。

正しく分析するための準備に使いましょう。

スマホの機種名や、商品の種類などが例です。

質的変数の詳しい定義とルールを読みます。

カテゴリで表され、 数値演算しない変数

英語名 Qualitative Variable。 同義・関連語:カテゴリ変数。

🎯 質的変数を扱う場面

📋 質的変数を扱う前提・適用範囲

質的変数を統計解析・機械学習で扱うとき、 次の前提を確認する:

📐 数式または定義

(1) One-hot Encoding: カテゴリ変数 X が K 個の値を持つとき、 K 次元の 0/1 ベクトルに変換する:

$$x_i \in \{c_1, c_2, \ldots, c_K\} \;\mapsto\; \mathbf{e}_i = (\underbrace{0, \ldots, 0, 1, 0, \ldots, 0}_{i\text{番目だけ}1})$$

(2) Label Encoding: 各カテゴリに整数 0, 1, ..., K-1 を割り当てる:

$$X \;\mapsto\; f(X) \in \{0, 1, \ldots, K-1\}$$

(3) 最頻値(Mode): 質的変数で最も多く出現するカテゴリ:

$$\mathrm{Mode}(X) = \arg\max_{c \in \{c_1, \ldots, c_K\}} \#\{i : x_i = c\}$$

(4) カイ二乗統計量: 2 つの質的変数の独立性検定:

$$\chi^2 = \sum_{i=1}^{r} \sum_{j=1}^{c} \frac{(O_{ij} - E_{ij})^2}{E_{ij}}$$

ここで O_ij は観測度数、 E_ij = (行合計 × 列合計) / 全合計 は期待度数。

(5) Cramér's V: カイ二乗を 0〜1 に正規化した連関係数:

$$V = \sqrt{\frac{\chi^2 / n}{\min(r-1, c-1)}}$$

(6) エントロピー: 質的変数の散らばり度合い:

$$H(X) = -\sum_{k=1}^{K} p_k \log_2 p_k$$

p_k はカテゴリ c_k の出現確率。 H=0 なら全データが 1 カテゴリ、 H=log2(K) なら均等分布。

🔬 数式を言葉で読み解く

「数式を言葉で読み解く」ことで、 質的変数の数学的扱いを直感的に把握できます:

記号意味SSDSE-B-2026 での実体
Kカテゴリ数都道府県なら 47
c_ii 番目のカテゴリラベルR01000=北海道, ..., R47000=沖縄
e_iOne-hot ベクトル北海道 → (1,0,0,...,0) (47 次元)
O_ij観測度数クロス表のマス目の実数
E_ij期待度数(独立仮説下)行合計×列合計/総数
χ²独立性からのずれ大きいほど 2 変数に関連あり
VCramér's V(連関の強さ)0=独立, 1=完全連関
H(X)エントロピー(情報量)カテゴリの均等度合い
p_kカテゴリ k の出現割合k 番目の都道府県のサンプル比

読み下し例: 「One-hot は『どれか 1 つだけが 1』の K 次元ベクトル。 カイ二乗は『観測と期待のずれの 2 乗を足したもの』、 Cramér's V は『カイ二乗を 0-1 に正規化したもの』」。

🧮 実値で計算してみる(SSDSE-B-2026 都道府県データ)

SSDSE-B-2026 の 都道府県コード(R01000〜R47000)は質的変数の典型例です。 ここでは「人口規模カテゴリ × 地方ブロック」の独立性をクロス集計と χ² で検定する例を電卓レベルで示します。

▶ クロス集計表(観測値)

「人口 500 万以上」「100-500 万」「100 万未満」の 3 区分 × 「東日本(北海道〜東海)」「西日本(近畿〜沖縄)」の 2 区分。 SSDSE-B-2026 の 2022 年実値から作成:

地方ブロック人口500万+100-500万100万未満行合計
東日本 (24 県)6 (北海道,埼玉,千葉,東京,神奈川,愛知)15324
西日本 (23 県)3 (大阪,兵庫,福岡)13723
列合計9281047

▶ 期待度数(独立仮説下)

E_ij = (行合計 × 列合計) / 全体合計:

E(東,500万+) = 24×9/47 = 4.596 E(東,100-500万) = 24×28/47 = 14.298 E(東,100万未満) = 24×10/47 = 5.106 E(西,500万+) = 23×9/47 = 4.404 E(西,100-500万) = 23×28/47 = 13.702 E(西,100万未満) = 23×10/47 = 4.894

▶ χ² 統計量計算

(6-4.596)²/4.596 + (15-14.298)²/14.298 + (3-5.106)²/5.106 + (3-4.404)²/4.404 + (13-13.702)²/13.702 + (7-4.894)²/4.894 = 0.4289 + 0.0345 + 0.8686 + 0.4476 + 0.0360 + 0.9063 = 2.7218

自由度 = (2-1)×(3-1) = 2。 χ²=2.72 は p≈0.26 で独立性を棄却できない → 「東日本/西日本 と 人口規模 の連関は統計的に有意でない」と結論。

▶ Cramér's V

V = √(2.7218/47 / min(2-1, 3-1)) = √(0.05791 / 1) = 0.241

V=0.241 は「弱〜中程度の連関」(0=独立、 0.1=弱、 0.3=中、 0.5=強)。 東日本に 100 万未満県が少ない偏りは見えるが、 n=47 では統計的に有意と言えるほどではない。

▶ 都道府県のエントロピー

SSDSE-B-2026 は 47 行(各都道府県 1 行)で均等分布なので:

H(都道府県) = -Σ (1/47) log₂(1/47) = log₂(47) ≈ 5.555 bits

最大値 5.555 bits は「完全に均等」を意味し、 各サンプルから都道府県を特定するのに平均 5.555 ビット必要であることを示す。

🧮 数式に値を入れて手で計算する: 質的変数の集計

合成カテゴリデータで頻度と構成比を計算する。

Step 1: 観測

カテゴリ頻度構成比
満足400.40
普通350.35
不満250.25

Step 2: 集計

合計 = 100 最頻: 満足 不満率 = 25%

🐍 Python で再現

1
2
3
import pandas as pd
s = pd.Series(['満足']*40 + ['普通']*35 + ['不満']*25)
print(s.value_counts(normalize=True))

📤 実行結果

満足 0.40 普通 0.35 不満 0.25

💬 手計算 (Step 2) と Python 出力が完全一致。

🐍 Python での扱い

SSDSE-B-2026 から「都道府県」「地方区分」など質的変数を抜き出して頻度集計し、 量的変数との関係をクロス集計で確認する基本パターン。

🎯 このコードでやること: 都道府県名 (名義尺度) を地方区分にマッピングし、 value_counts で頻度分布、 pd.crosstab で「地方区分 × 高齢化レベル」のクロス集計を行う。 質的変数は数値計算ではなく頻度と関連の集計が中心。

📥 入力: data/raw/SSDSE-B-2026.csv都道府県 (質的) と 高齢化率 (量的) 列。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
df = df[df['年度'] == 2023].copy()   # 2023 年の 47 県に絞る
df['高齢化率'] = df['65歳以上人口'] / df['総人口'] * 100

# 都道府県を地方区分にマッピング (質的 → 質的の集約)
# 7 県だけ書いて「以下省略」にすると、残り 40 県が NaN になって
# 集計が壊れる。地域コードから 47 県ぶんを割り当てる。
def _block(code):
    n = int(str(code).lstrip('R')) // 1000
    if n == 1: return '北海道'
    if n <= 7: return '東北'
    if n <= 14: return '関東'
    if n <= 23: return '中部'
    if n <= 30: return '近畿'
    if n <= 35: return '中国'
    if n <= 39: return '四国'
    return '九州'
df['地方区分'] = df['地域コード'].apply(_block)

# 質的変数の頻度集計
print(df['地方区分'].value_counts())

# 量的変数を 3 階級に離散化 → クロス集計
df['高齢化Lv'] = pd.cut(df['高齢化率'], bins=[0, 28, 33, 100],
                      labels=['低', '中', '高'])
print(pd.crosstab(df['地方区分'], df['高齢化Lv']))

📤 実行例:

地方区分 東北 2 北海道 1 関東 1 中部 1 近畿 1 九州 1 Name: count, dtype: int64 高齢化Lv 低 高 地方区分 中部 1 0 九州 1 0 北海道 0 1 東北 0 2 近畿 1 0 関東 1 0

💬 結果の読み方: この出力は上のコードをそのまま動かした実測値で、 合計 7 県分しかない。 block 辞書に 7 県しか書いていないため、 残り 40 県は map の結果が NaN になり集計から落ちるからである。 「以下省略」と書いたマッピングは、 省略した分がそのまま欠損になる——質的変数を作るときに最も多い事故がこれ。 df['地方区分'].isna().sum() で必ず取りこぼしを確認すること。 なお 高齢化Lv の列に「中」が出てこないのも同じ理由で、 7 県の中にたまたま 28〜33% の県が無かっただけ。 全 47 県を書けば「地方区分」は 8 カテゴリの名義尺度になり、 東北に「高」が偏るといった傾向が読める。 質的 × 質的のクロス集計はカイ二乗検定 (scipy.stats.chi2_contingency) で「地方区分と高齢化レベルが独立か」を検定できる。 量的変数のような平均は意味がないが、 頻度・割合・独立性検定が分析の主軸となる。

📝 レポートでの報告

分析結果を報告するときに含めるべき情報:

✅ チェックリスト

🐍 Python 実装

SSDSE-B-2026 を題材に、 質的変数の処理を 4 つのコードブロックで実装します。

① One-hot エンコーディング

🎯 このコードでやること: SSDSE-B-2026 の都道府県を pd.get_dummies で 47 列の One-hot に変換する。

📥 入力例:

SSDSE-2026 都道府県 総人口 R01000 北海道 5140 R02000 青森 1238 R03000 岩手 1196 ... R47000 沖縄 1468
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == 2022][['Code', 'Prefecture', 'A1101']].reset_index(drop=True)

# One-hot エンコーディング
df_onehot = pd.get_dummies(df['Prefecture'], prefix='pref').astype(int)
print(f"元データ shape: {df.shape}")
print(f"One-hot shape: {df_onehot.shape}")
print(df_onehot.iloc[:5, :5])  # 先頭 5 行 × 5 列

📤 実行結果:

元データ shape: (47, 3) One-hot shape: (47, 47) pref_三重県 pref_京都府 pref_佐賀県 pref_兵庫県 pref_北海道 0 0 0 0 0 1 1 0 0 0 0 0 2 0 0 0 0 0 3 0 0 0 0 0 4 0 0 0 0 0

💬 結果の読み方: 47 都道府県 × 47 列の疎行列が得られる。 各行は「1 つだけが 1、 他は 0」。 線形回帰や深層学習でカテゴリを入力するときの標準前処理。

② カイ二乗独立性検定

🎯 このコードでやること: 「東日本/西日本」と「人口規模カテゴリ」の独立性を scipy で検定する。

📥 入力例: 上記クロス集計表 (観測値)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import pandas as pd
import numpy as np
from scipy.stats import chi2_contingency

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == 2022].copy()

# 人口カテゴリを作成
def pop_cat(x):
    if x >= 5_000_000: return '500万+'
    elif x >= 1_000_000: return '100-500万'
    else: return '100万未満'
df['人口カテゴリ'] = df['A1101'].apply(pop_cat)

# 地方ブロック (24=東日本, 23=西日本)
east_codes = ['R01000','R02000','R03000','R04000','R05000','R06000','R07000',
              'R08000','R09000','R10000','R11000','R12000','R13000','R14000',
              'R15000','R16000','R17000','R18000','R19000','R20000','R21000',
              'R22000','R23000','R24000']
df['地方'] = df['Code'].apply(lambda c: '東日本' if c in east_codes else '西日本')

# クロス集計
cross = pd.crosstab(df['地方'], df['人口カテゴリ'])
print(cross)

# カイ二乗検定
chi2, p, dof, expected = chi2_contingency(cross)
print(f"\nχ² = {chi2:.4f}, p = {p:.4f}, dof = {dof}")
print(f"独立性 棄却? -> {'YES' if p < 0.05 else 'NO'}")

📤 実行結果:

人口カテゴリ 100-500万 100万未満 500万+ 地方 東日本 15 3 6 西日本 13 7 3 χ² = 2.7228, p = 0.2563, dof = 2 独立性 棄却? -> NO

💬 結果の読み方: p=0.26 で独立性を棄却できず、 「地方ブロックと人口規模の連関は統計的に有意でない」と結論。 東日本に 100 万未満県が少ない傾向は見えるが、 n=47 では偶然の範囲を超えない。

③ Cramér's V による連関の強さ

🎯 このコードでやること: カイ二乗から Cramér's V を計算し、 連関の強さを 0-1 で表現。

📥 入力例: 上の cross 表

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np
import pandas as pd
from scipy.stats import chi2_contingency

# 上で計算した cross 表を再利用
chi2, p, dof, expected = chi2_contingency(cross)
n = cross.sum().sum()
r, c = cross.shape
cramers_v = np.sqrt(chi2 / n / min(r-1, c-1))

print(f"サンプル数 n = {n}")
print(f"χ²        = {chi2:.4f}")
print(f"Cramér's V = {cramers_v:.4f}")

# 評価基準
if cramers_v < 0.1: rank = "ほぼ独立"
elif cramers_v < 0.3: rank = "弱い連関"
elif cramers_v < 0.5: rank = "中程度の連関"
else: rank = "強い連関"
print(f"評価 -> {rank}")

📤 実行結果:

サンプル数 n = 47 χ² = 2.7228 Cramér's V = 0.2407 評価 -> 弱い連関

💬 結果の読み方: V=0.24 は「弱い連関」レベル (0.3 未満)。 χ² 検定でも有意でなく、 明確な連関があるとは言えない。

④ ラベルエンコーディング & 順序エンコーディング

🎯 このコードでやること: sklearn の LabelEncoderOrdinalEncoder で都道府県と人口カテゴリを数値化する。

📥 入力例: 上記の df

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import pandas as pd
from sklearn.preprocessing import LabelEncoder, OrdinalEncoder

# Label Encoding (名義変数 → 整数)
le = LabelEncoder()
df['pref_id'] = le.fit_transform(df['Prefecture'])

# Ordinal Encoding (順序変数 → 整数, 順序指定可能)
order = [['100万未満', '100-500万', '500万+']]
oe = OrdinalEncoder(categories=order)
df['pop_rank'] = oe.fit_transform(df[['人口カテゴリ']]).astype(int)

print(df[['Prefecture', 'pref_id', '人口カテゴリ', 'pop_rank']].head(5))
print("\nラベル対応:", dict(zip(le.classes_[:5], range(5))))

📤 実行結果:

Prefecture pref_id 人口カテゴリ pop_rank 1 北海道 4 500万+ 2 13 青森県 41 100-500万 1 25 岩手県 19 100-500万 1 37 宮城県 11 100-500万 1 49 秋田県 36 100万未満 0 ラベル対応: {'三重県': 0, '京都府': 1, '佐賀県': 2, '兵庫県': 3, '北海道': 4}

💬 結果の読み方: Label Encoder は辞書順で 0,1,...,46 に。 名義変数を Label Encoding すると順序を誤って解釈される恐れがあるので、 線形モデルでは要注意。 Ordinal Encoder は順序を明示的に指定できる。

⚠️ よくある落とし穴

❌ 外れ値の影響
平均は外れ値に弱い。 中央値・IQR と組み合わせて確認。
❌ 単位を忘れない
数値は単位とセット。 「3」だけでは意味を持ちません。
❌ 集約レベルに注意
同じデータでも集計の単位を変えると傾向が逆転することがあります(シンプソン)。

⚠️ 落とし穴

⚠️ 落とし穴 拡張版

  1. Train/Test での未知カテゴリ: train にない値が test に来たとき One-hot は全 0 ベクトルになり予測不能。 「unknown」専用カテゴリを事前に学習。
  2. カテゴリ順序の暗黙仮定: ABC順 / 五十音順 などで Label Encoding すると、 アルゴリズムが「順序的に意味あり」と誤解。 ツリーモデルでは問題少ないが線形では致命的。
  3. テストデータでの fit リーク: テストデータも含めて Target Encoding を計算するとテスト情報が学習に漏れる。 必ず train でのみ fit。
  4. カテゴリ数の固定化: 学習時に 50 カテゴリ → 本番で新カテゴリ追加 → モデル再学習が必要。 オンライン拡張可能なエンコーディング(Hash, Embedding 追加学習)を選択。
  5. マルチホットエンコーディング: 1 サンプルに複数カテゴリが当てはまる場合(例: 商品タグ)は One-hot ではなく Multi-hot 必須。
  6. カテゴリの意味的グルーピング欠落: 「都道府県」を単純 One-hot するより「地方ブロック」「人口階層」を追加特徴とした方が一般化性能が高いことが多い。
  7. 順序尺度の数値化: 「とても満足=5, 満足=4, ..., とても不満=1」のように線形数値化することの妥当性検証は必須。 順序の間隔が等しい保証はない。
  8. クロス集計のサンプル数不足: クロス表のセルが 5 未満なら χ² 検定の近似が崩れる。 Fisher 正確検定を使用。
  9. 循環カテゴリの線形扱い: 曜日(月〜日)、 月(1〜12)は循環カテゴリ。 Label Encoding すると「土→日」と「日→月」が違う距離になる。 sin/cos エンコーディングで対応。
  10. 階層カテゴリの平坦化: 「動物 → 哺乳類 → 猫」のような階層を平坦な One-hot にすると情報損失。 階層を保つエンコーディング(H2E 等)を検討。

🎯 まとめ

質的変数(Qualitative Variable)はカテゴリで分類される変数で、 名義尺度(順序なし)と順序尺度(順序あり)に分かれる。 平均は計算できず、 最頻値や度数分布が記述統計の中心。 ML に投入する際は One-hot / Label / Target / Embedding など複数のエンコーディングから cardinality とモデルに応じて選択。 SSDSE-B-2026 の 47 都道府県を題材に、 χ²=2.72, Cramér's V=0.24 で「東日本・西日本と人口規模の連関は有意でない」と検証できた。 Target Encoding はリーク対策が必須で、 CatBoost の OTS や K-fold 内計算が定番。 高 cardinality データでは Entity Embedding が深層学習の標準手法。

📜 質的変数取扱いの歴史

概念・手法概要
1900Karl Pearson の χ² 検定2 つのカテゴリ変数の独立性検定
1934Fisher の正確検定サンプル数が少ない場合の精密検定
1946Stevens の尺度水準名義・順序・間隔・比例 の 4 分類提案
1946Cramér's Vχ² を 0-1 に正規化した連関係数
1973Multiple Correspondence Analysis複数質的変数の次元削減・可視化
1989Cohen's κ (kappa)評価者間一致度測定
2001Target Encoding 論文Micci-Barreca による高 cardinality 対応
2013Word2Vec単語(離散カテゴリ)の連続ベクトル埋め込み
2016Entity Embedding 論文深層学習でカテゴリ変数を低次元化
2017LightGBMcategorical_feature の自動最適化
2018CatBoostOrdered Target Statistics
2020+Tabular Transformer 系TabNet / TabTransformer / FT-Transformer

🏢 産業導入事例

分野質的変数の例cardinality用途
EC商品カテゴリ・ブランド数千〜数十万推薦・需要予測
広告広告ID・配信先サイト数百万CTR 予測
不動産最寄駅・町名・物件種別数千価格・賃料予測
医療ICD-10 病名・薬剤コード数万転帰・再入院予測
物流荷物種別・出発地数千配送時間予測
金融業種コード・地域コード数百〜数千与信モデル
SaaSプラン種別・業界数十解約予測
人事職種・スキル・部署数百退職予測・適性配置
教育科目・難易度・大学数千合格予測・教材推薦
政府統計SSDSE 都道府県・年齢区分47 / 数十政策効果分析

❓ よくある質問

📑 クイックリファレンス(チートシート)

# pandas One-hot
df_oh = pd.get_dummies(df['col'], prefix='col', drop_first=True)

# sklearn Label Encoding
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder(); df['col_enc'] = le.fit_transform(df['col'])

# sklearn Ordinal (順序指定)
from sklearn.preprocessing import OrdinalEncoder
oe = OrdinalEncoder(categories=[['low','med','high']])
df['col_ord'] = oe.fit_transform(df[['col']])

# pandas カテゴリ型変換 (メモリ節約)
df['col'] = df['col'].astype('category')

# scipy χ² 検定
from scipy.stats import chi2_contingency
chi2, p, dof, exp = chi2_contingency(pd.crosstab(df['A'], df['B']))

# 度数分布
df['col'].value_counts()
df['col'].value_counts(normalize=True) # 比率

# LightGBM (categorical 直接指定)
import lightgbm as lgb
model = lgb.LGBMRegressor()
model.fit(X, y, categorical_feature=['pref','city'])

# CatBoost (cat_features 指定)
from catboost import CatBoostRegressor
model = CatBoostRegressor(cat_features=['pref','city'])
model.fit(X, y)

# PyTorch Embedding
import torch.nn as nn
emb = nn.Embedding(num_embeddings=47, embedding_dim=8)
out = emb(torch.LongTensor([0, 1, 46])) # shape: (3, 8)

🗾 SSDSE-B-2026 を用いた質的変数の徹底分析

SSDSE-B-2026 をベースに、 質的変数特有の前処理・分析を 4 種類実行します(すべて実データ)。

▶ 実験 1: 都道府県カテゴリの度数分布と最頻値

🎯 このコードでやること: SSDSE-B-2026 内の都道府県カテゴリの度数を計算し、 視覚的に整理。

📥 入力例: SSDSE-B-2026 全年度データ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
print(f"総レコード数: {len(df)}")
print(f"年度範囲: {df['SSDSE-B-2026'].min()} - {df['SSDSE-B-2026'].max()}")
print(f"都道府県数: {df['Prefecture'].nunique()}")

# 度数分布
freq = df['Prefecture'].value_counts()
print(f"\n度数分布 (各都道府県の出現回数):")
print(f"  最大: {freq.max()} ({freq.idxmax()})")
print(f"  最小: {freq.min()} ({freq.idxmin()})")
print(f"  最頻値 (mode): {df['Prefecture'].mode().values[0]}")

# 等頻度確認 (47 都道府県全てが同じ年度数で出現)
print(f"\n等頻度? {(freq == freq.max()).all()}")

📤 実行結果:

総レコード数: 564 年度範囲: 2012 - 2023 都道府県数: 47 度数分布 (各都道府県の出現回数): 最大: 12 (北海道) 最小: 12 (北海道) 最頻値 (mode): 三重県 等頻度? True

💬 結果の読み方: SSDSE-B-2026 はパネルデータで 47 都道府県 × 12 年度 = 564 行。 全都道府県が等頻度なので「最頻値」は辞書順 1 番目(三重県)が便宜上返るのみ。 実データでは度数の偏りが情報源となる。

▶ 実験 2: 都道府県を 8 地方ブロックに集約

🎯 このコードでやること: 47 都道府県を 8 地方(北海道・東北・関東・中部・近畿・中国・四国・九州沖縄)に集約し、 地方別の人口平均を計算。

📥 入力例: SSDSE-B-2026 2022 年データ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == 2022].copy()

region_map = {
    '北海道': '北海道',
    '青森県': '東北', '岩手県': '東北', '宮城県': '東北', '秋田県': '東北', '山形県': '東北', '福島県': '東北',
    '茨城県': '関東', '栃木県': '関東', '群馬県': '関東', '埼玉県': '関東', '千葉県': '関東', '東京都': '関東', '神奈川県': '関東',
    '新潟県': '中部', '富山県': '中部', '石川県': '中部', '福井県': '中部', '山梨県': '中部', '長野県': '中部',
    '岐阜県': '中部', '静岡県': '中部', '愛知県': '中部',
    '三重県': '近畿', '滋賀県': '近畿', '京都府': '近畿', '大阪府': '近畿', '兵庫県': '近畿', '奈良県': '近畿', '和歌山県': '近畿',
    '鳥取県': '中国', '島根県': '中国', '岡山県': '中国', '広島県': '中国', '山口県': '中国',
    '徳島県': '四国', '香川県': '四国', '愛媛県': '四国', '高知県': '四国',
    '福岡県': '九州沖縄', '佐賀県': '九州沖縄', '長崎県': '九州沖縄', '熊本県': '九州沖縄',
    '大分県': '九州沖縄', '宮崎県': '九州沖縄', '鹿児島県': '九州沖縄', '沖縄県': '九州沖縄',
}
df['地方'] = df['Prefecture'].map(region_map)

agg = df.groupby('地方').agg(
    県数=('Prefecture', 'count'),
    人口平均=('A1101', 'mean'),
    人口合計=('A1101', 'sum'),
).round(0).sort_values('人口合計', ascending=False)
print(agg)

📤 実行結果:

県数 人口平均 人口合計 地方 関東 7 6219286 43535000 近畿 7 3156286 22094000 中部 9 2320667 20886000 九州沖縄 8 1763500 14108000 東北 6 1404333 8426000 中国 5 1427400 7137000 北海道 1 5140000 5140000 四国 4 905000 3620000

💬 結果の読み方: 関東が圧倒的(約 4354 万人)。 47 → 8 集約で「地方別」という新しい質的変数を作成。 cardinality を下げることで一般化性能が上がるケースが多い。

▶ 実験 3: 人口階層カテゴリと出生率の関連

🎯 このコードでやること: 都道府県を人口階層 3 区分にカテゴリ化し、 階層ごとの合計特殊出生率の平均を比較。

📥 入力例: SSDSE-B-2026 2022 年

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == 2022].copy()

bins = [0, 1_000_000, 5_000_000, 100_000_000]
labels = ['小 (<100万)', '中 (100-500万)', '大 (500万+)']
df['人口階層'] = pd.cut(df['A1101'], bins=bins, labels=labels)

agg = df.groupby('人口階層', observed=True).agg(
    県数=('Prefecture', 'count'),
    出生率平均=('A4103', 'mean'),
    出生率最小=('A4103', 'min'),
    出生率最大=('A4103', 'max'),
).round(3)
print(agg)

📤 実行結果(例):

県数 出生率平均 出生率最小 出生率最大 人口階層 小 (<100万) 10 1.440 1.180 1.600 中 (100-500万) 28 1.377 1.090 1.700 大 (500万+) 9 1.210 1.040 1.350

💬 結果の読み方: 「人口大の都道府県ほど出生率が低い」傾向が明確(小: 1.44 → 大: 1.21)。 質的変数(人口階層)と量的変数(出生率)の関連を ANOVA や Kruskal-Wallis で検定可能。

▶ 実験 4: Entity Embedding の概念デモ

🎯 このコードでやること: PyTorch で 47 都道府県の Entity Embedding 層を作成し、 ランダム初期化後の埋め込みを表示。

📥 入力例: SSDSE-B-2026 + Label Encoded 都道府県 id

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pandas as pd
import torch
import torch.nn as nn
from sklearn.preprocessing import LabelEncoder
torch.manual_seed(0)

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')
df = df[df['SSDSE-B-2026'] == 2022].copy()

le = LabelEncoder()
df['pref_id'] = le.fit_transform(df['Prefecture'])

# 8 次元 Embedding 層
num_categories = df['pref_id'].nunique()
emb_dim = 8
embedding = nn.Embedding(num_categories, emb_dim)

# 5 都道府県の埋め込みを取り出す
sample_ids = torch.LongTensor([0, 5, 13, 27, 46])
sample_pref = [le.inverse_transform([i.item()])[0] for i in sample_ids]
sample_emb = embedding(sample_ids).detach().numpy()

print(f"カテゴリ数: {num_categories}, 埋め込み次元: {emb_dim}")
print(f"\nサンプル埋め込み (一部 4 次元):")
for p, e in zip(sample_pref, sample_emb):
    print(f"  {p:6} -> [{e[0]:+.3f}, {e[1]:+.3f}, {e[2]:+.3f}, {e[3]:+.3f}, ...]")

📤 実行結果(例):

カテゴリ数: 47, 埋め込み次元: 8 サンプル埋め込み (一部 4 次元): 三重県 -> [-1.126, -1.152, -0.251, -0.434, ...] 千葉県 -> [-0.102, +0.792, -0.290, +0.053, ...] 富山県 -> [+0.247, +0.077, +0.338, +0.454, ...] 栃木県 -> [+0.955, +1.284, -0.666, +0.565, ...] 鹿児島県 -> [-1.897, -0.002, -1.972, -1.934, ...]

💬 結果の読み方: 初期はランダムだが、 学習後は「似た都道府県(例: 東京・神奈川)が近い距離」になる。 47 個の One-hot (47 次元) を 8 次元に圧縮しつつ意味的構造を獲得できる。

📖 学習リソース

🔀 量的変数との徹底比較

観点質的変数量的変数
値の型カテゴリ(文字列)数値
順序なし(名義)or あり(順序)あり
距離定義不可定義可能
代表値最頻値平均・中央値
ばらつきエントロピー・ジニ係数分散・標準偏差
関連性指標χ²・Cramér's V・相互情報量相関係数・共分散
可視化棒グラフ・円・モザイクヒストグラム・散布図・箱ひげ
前処理One-hot, Label, Target, Embedding標準化・正規化・log 変換
主な検定χ² 検定・Fisher 正確検定t 検定・ANOVA
回帰モデルロジスティック回帰 (目的変数の場合)線形回帰
SSDSE-B 例地域コード・都道府県名総人口・出生数・出生率

📊 順序尺度の特別な扱い

質的変数の中で「順序尺度」は特殊な扱いを必要とします。 名義尺度との違い・推奨手法を整理:

▶ 順序尺度の例

▶ 順序尺度の代表値・指標

指標使用可否理由
最頻値分布のピーク把握
中央値順序が定義されているため計算可
平均数値化の妥当性に依存(リッカート尺度では慣例的に使用)
分散・標準偏差平均と同様の制限
四分位範囲 (IQR)順序があれば計算可
Spearman 順位相関順序情報を利用
Kendall's τ順序の一致度

▶ 順序尺度の検定

▶ 順序尺度の機械学習エンコーディング

🐼 pandas でのカテゴリ型活用

pandas は category dtype を提供し、 メモリ削減・順序定義・高速処理を実現します。

▶ カテゴリ型の利点

▶ カテゴリ型変換と操作

🎯 このコードでやること: SSDSE-B-2026 の都道府県を pandas categorical 型に変換し、 メモリ使用量を比較。

📥 入力例: SSDSE-B-2026 全データ

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=[1], encoding='cp932')

# 文字列のメモリ
mem_str = df['Prefecture'].memory_usage(deep=True)

# カテゴリ型に変換
df['都道府県_cat'] = df['Prefecture'].astype('category')
mem_cat = df['都道府県_cat'].memory_usage(deep=True)

print(f"文字列型メモリ: {mem_str:,} bytes")
print(f"カテゴリ型メモリ: {mem_cat:,} bytes")
print(f"削減率: {(1 - mem_cat/mem_str)*100:.1f}%")
print(f"\nカテゴリ型情報:")
print(f"  カテゴリ数: {len(df['都道府県_cat'].cat.categories)}")
print(f"  最初の 3 カテゴリ: {list(df['都道府県_cat'].cat.categories[:3])}")

📤 実行結果:

文字列型メモリ: 40,812 bytes カテゴリ型メモリ: 5,637 bytes 削減率: 86.2% カテゴリ型情報: カテゴリ数: 47 最初の 3 カテゴリ: ['三重県', '京都府', '佐賀県']

💬 結果の読み方: 文字列 41KB → カテゴリ型 5.6KB と 86% 削減。 数百万行の大規模データでは GB 単位の削減効果。 plus、 groupby 等の処理も高速化される。

🗺 概念マップ:質的変数の知識体系

質的変数 (Qualitative Variable)
├── 下位分類
│ ├── 名義尺度 (順序なし: 都道府県、 血液型、 性別)
│ └── 順序尺度 (順序あり: 学年、 評価ランク)
├── 記述統計
│ ├── 最頻値 (Mode)
│ ├── 度数分布表
│ ├── クロス集計表
│ └── エントロピー
├── 推測統計
│ ├── カイ二乗独立性検定
│ ├── Fisher の正確検定 (小サンプル)
│ ├── Cramér's V
│ ├── 相互情報量
│ └── McNemar 検定 (対応あり)
├── 可視化
│ ├── 棒グラフ
│ ├── 円グラフ
│ ├── モザイク図
│ └── ヒートマップ
├── 機械学習前処理
│ ├── One-hot Encoding
│ ├── Label Encoding
│ ├── Ordinal Encoding
│ ├── Frequency Encoding
│ ├── Target Encoding (Mean / Leave-One-Out / James-Stein)
│ ├── Hash Encoding
│ ├── Binary Encoding
│ └── Entity Embedding (深層学習)
└── モデル特化対応
├── LightGBM (categorical_feature 引数)
├── CatBoost (Ordered Target Statistics)
├── XGBoost (1-hot 必須 / enable_categorical)
└── PyTorch / TensorFlow (nn.Embedding)

🗺 質的変数の現実マッピング例

質的変数を「より良い質的変数」に変換するマッピング例を SSDSE-B-2026 と関連付けて整理:

元カテゴリcardinality→ 変換後cardinality用途
都道府県478 地方ブロック8汎化性能向上
市町村~1700都道府県 or 地方47 or 8次元削減
JIS X 0402 コード~1700先頭 2 桁 (都道府県)47階層抽出
郵便番号 (7桁)~12万先頭 3 桁~1000地理的集約
業種コード (NACE)~500中分類 or 大分類~80 or 20統計分析
商品 SKU数十万カテゴリ → ブランド~100推薦システム
職業コード数百職業大分類 (10 程度)~10社会調査
年齢100+年齢階級 (5歳刻み)20人口統計

🧬 Entity Embedding 深掘り

高 cardinality カテゴリを深層学習で扱う標準手法。 詳細を解説:

▶ 仕組み

各カテゴリ k に対して d 次元のベクトル v_k ∈ R^d を学習する。 NN の最初の層は nn.Embedding(K, d) で、 入力(カテゴリ id)→ ベクトル変換を行う:

$$v_k = W[k, :], \quad W \in \mathbb{R}^{K \times d}$$

W は通常の重み行列で、 誤差逆伝播で学習。 学習後、 「意味的に近いカテゴリは v が近い」性質を獲得。

▶ 次元数の選び方

cardinality K推奨 dim d経験則
2-102-4log2(K) or √K
10-1004-16K^0.25 程度
100-100016-64min(50, K^0.25 × 6)
1000-10K32-12850-100
10K+64-512200-300

▶ 事前学習 Embedding

単語埋め込み(Word2Vec, GloVe, BERT 等)と同様に、 商品ID・ユーザIDも事前学習で意味的構造を獲得できる。 大規模な行動ログから学習し、 下流タスク(推薦・需要予測)に転移。

▶ Embedding の可視化

学習済 Embedding を t-SNE / UMAP で 2 次元に圧縮すると、 カテゴリ間の意味的近さが視覚的に分かる。 「東京・大阪・名古屋が近い」「鳥取・島根・福井が近い」など人口・地理的特徴を学習可能。

✅ 実装チェックリスト

▶ データ理解フェーズ

▶ 前処理フェーズ

▶ エンコーディングフェーズ

▶ モデル学習フェーズ

📋 補足: 用語の追加深掘り

▶ 二値カテゴリ(Binary Variable)

2 値のみのカテゴリ(性別: M/F, 喫煙: Yes/No 等)は質的変数の特殊ケース。 0/1 に直接変換するだけで OK。 χ² 検定の代わりに Fisher 正確検定が一般的。

▶ 多値ラベル(Multi-label)

1 サンプルに複数カテゴリが該当する場合(商品の複数タグ等)。 Multi-hot Encoding(複数の 1 が立つ)で表現。 sklearn の MultiLabelBinarizer が便利。

▶ 階層カテゴリ(Hierarchical)

「動物 → 哺乳類 → 食肉目 → ネコ科 → ネコ」のような階層を持つカテゴリ。 各階層を別の特徴量として扱う、 階層 path を文字列として扱う、 木構造 Embedding 等の手法がある。

▶ 循環カテゴリ(Cyclic)

曜日・月・時刻のように「最後と最初が連続する」カテゴリ。 sin/cos エンコーディングで連続性を保つのが標準:

$$x_{\sin} = \sin(2\pi k / K), \quad x_{\cos} = \cos(2\pi k / K)$$

「日→月」の距離が「土→日」と同じになる。

▶ Cohen's κ(カッパ係数)

2 人のアノテーターが質的変数を付けたときの「偶然超えた一致度」を測る指標。 アノテーション品質チェックで必須。 sklearn の cohen_kappa_score で計算可能。

▶ Phi 係数(φ)

2×2 クロス表での連関係数。 χ² から計算: φ = √(χ²/n)。 2 値変数間の関連性測定に使用。 数値変数の相関係数に対応する位置づけ。

▶ Lambda 係数(λ)

Goodman-Kruskal の λ。 「変数 X を知ると変数 Y の予測がどれくらい改善するか」を測る非対称指標。 連関の方向性を考慮できる。

🔭 今後のトレンド (2025-)

🎯 最終まとめ

質的変数は カテゴリで分類される変数であり、 統計分析・機械学習の両方で必須の概念。 SSDSE-B-2026 の都道府県データは典型的な 47-クラス名義変数で、 8 地方ブロックへの集約、 χ² 検定 (2.72, p=0.26)、 Cramér's V (0.24) など多様な分析を実演した。 ML への投入時は cardinality とモデル種別に応じて One-hot / Label / Target / Hash / Embedding を使い分ける。 Target Encoding のリーク対策(K-fold, smoothing, CatBoost OTS)と、 高 cardinality 時の Entity Embedding が現代的な定番手法。 順序尺度は名義尺度と区別して順序情報を活かす Ordinal / Polynomial / 順序回帰モデルを選ぶ。 今後は Pretrained Tabular Embedding や LLM 駆動の自動前処理が標準化される見込み。

📊 エンコーディング手法 完全比較表

手法出力次元cardinality 適性線形モデルツリーモデルNNリーク未知対応
One-hotKなし困難
Label1なし困難
Ordinal1○ (順序変数)なし困難
Frequency1非常に高なし○ (頻度 0)
Mean Target1非常に高高 (要対策)○ (全体平均)
LOO Target1非常に高
Hashm (任意)非常に高なし
Binarylog2(K)なし困難
Entity Embed.d (8-64)非常に高なし
CatBoost OTS1非常に高N/ACatBoost 専用N/Aなし

凡例: ◎=最適, ○=良好, △=要注意, ✗=非推奨

質的変数 Target Encoding Frequency Encoding Hash Encoding Entity Embedding Leave-One-Out Enc. Binary Encoding

🔗 隣接手法への橋渡し

「質的変数」は単独で完結する手法ではなく、 隣接領域と連携することで真価を発揮する。

SSDSE-B-2026 の都道府県コード (R01000=北海道) や産業大分類は質的変数で、 ダミー変数化・カテゴリ別集計・カイ二乗検定が主な処理。 平均や和に意味は無く、 量的変数とは扱いを分ける。

🌳 手法選択フロー

「質的変数」を実際の課題に当てはめるとき、 状況別に何を選ぶかを 3 段階で判定する。

  1. 順序があるか? Yes → 順序尺度 (満足度 1-5)、 No → 名義尺度 (血液型・都道府県コード)
  2. カテゴリ数は少ないか? 少 (2-10) → One-Hot エンコーディング、 多 (100+) → Target/Frequency エンコーディング
  3. 関連性を測るか? 質的 × 質的 → カイ二乗 + Cramér V、 質的 × 量的 → ANOVA / 群別箱ひげ図

SSDSE-B-2026 の都道府県コード (R01000=北海道) は名義尺度なので、 数値演算ではなくダミー化または地域別集計で使う。 R01000 と R47000 の「平均」は意味を持たない。

🧩 解説深化 — 質的変数に「数字を貼る」と何が壊れるか

本ページの他の節は、 質的変数を「集計する(最頻値・エントロピー・クロス表・カイ二乗)」「機械学習用に符号化する(One-Hot/Ordinal)」という方向を扱った。 この節はもう一歩踏み込み、 順序尺度のカテゴリに数値ラベルを割り振り、 その数値で平均や比を計算してしまう罠を、 独自角度として深掘りする。 核心は一言で言える —— 質的変数の要約統計量には「貼った数字を変えても動かないもの(不変量)」と「貼り方しだいで自由に動くもの」があり、 後者を数値だと信じると分析が崩れる

🎨 直感 — 「順番」は伝わるが「間隔」は貼った人の勝手

質的変数のうち順序尺度は「S < M < L」という並び順だけを保証し、 「M と L の差 = S と M の差」といった間隔の等しさは保証しない。 満足度を「1=不満, 2=普通, 3=満足」と書いても、 その 1・2・3 は順位を表す記号であって長さではない。 だから順位から作れる統計量(最頻値・中央値・分位点)は安全に読めるが、 間隔を前提とする平均・標準偏差・比は「貼った数字」という恣意性をそのまま吸い込む。 質的変数を触るときは常に「いま自分は順番だけ使っているか、 それとも勝手に間隔を仮定していないか」を自問するのが直感の要である。

⚠️ 落とし穴(重要)── 同じデータ・同じ順序で「平均水準」が 0.51 → 0.30 に化ける

実データで見せる。 SSDSE-B-2026・2023 年・47 都道府県の総人口(列 A1101)を、 人口規模の順序尺度 5 水準~100万 < 100-200万 < 200-300万 < 300-500万 < 500万~)に畳み込む。 度数は下表の通り(すべて実測、 合計 47)。 この 5 水準に「等間隔コード 1,2,3,4,5」と「幾何コード 1,2,4,8,16」という順序は完全に同じ(単調増加)2 通りの数値を貼り、 それぞれで代表値を計算した実測値を並べる。

水準(順序尺度)度数(実測)等間隔コード幾何コード
~100万1011
100-200万 ← 最頻値2122
200-300万634
300-500万(静岡県のみ)148
500万~9516

不変量(貼った数字に依存しない): 最頻値は 100-200万(21 県)、 エントロピーは 1.9483 bit(最大 log₂5 = 2.3219 bit の約 84%)、 中央値も両コードで 100-200万(第 2 水準)。 これらは度数と順序だけで決まるので、 コードを別の単調増加な数字に貼り替えてもまったく動かない。 いっぽう 算術平均は —— 等間隔コードで 2.532(最大 5 に対し 0.506)、 幾何コードで 4.851(最大 16 に対し 0.303)。 同じ都道府県・同じ順序・同じデータなのに、 「平均的な人口水準」がスケール上位比で 0.51 から 0.30 へ跳ねた。 平均の値は人口が語ったのではなく、 分析者が貼った 3→8 の一手が語っている。 「満足度の平均は 3.4 だった」式の主張は、 この恣意性を明示しない限り再現も反証もできない数字だと肝に銘じたい。

# SSDSE-B-2026, 2023, 47都道府県, 列A1101(総人口)を順序5水準に離散化 (実測) 度数 : ~100万=10 / 100-200万=21 / 200-300万=6 / 300-500万=1 / 500万~=9 (計47) 最頻値 : 100-200万 # 不変(コード非依存) エントロピー: 1.9483 bit (max log2(5)=2.3219) # 不変 中央値 : 100-200万 (第2水準・両コード共通) # 単調変換に不変 算術平均 : 等間隔コード 2.532 (÷5 = 0.506) # コード依存で激変 幾何コード 4.851 (÷16 = 0.303) # 同じ順序なのに 0.51→0.30

🚀 発展 — 「許容変換」で見ると、なぜ平均だけ壊れるのかが分かる

この現象は測定尺度論(Stevens)の許容変換(permissible transformation)で厳密に説明できる。 各尺度は「意味を保ったまま許される数値の貼り替え」の範囲が違う: 名義尺度は 1 対 1 の任意の付け替え(ラベルの置換)、 順序尺度単調増加変換(順序さえ保てば 1,2,4,8,16 でも良い)、 間隔尺度アフィン変換 y=ax+b比例尺度比例変換 y=ax。 統計量が「意味を持つ」のは、 その尺度で許される全変換に対して結論が不変なときだけだ。 最頻値は 1 対 1 変換に不変なので名義尺度から使え、 中央値・分位点は単調変換に不変なので順序尺度から使える。 ところが算術平均はアフィン変換までしか耐えられない —— 順序尺度に許される単調(非アフィン)変換で値が変わってしまうため、 順序コードの平均は原理的に無意味になる。 上表の「幾何コード」はまさに単調だがアフィンでない貼り替えで、 平均が壊れる様子を実測で可視化したものだ。 実務では、 順序カテゴリは 中央値・最頻値・度数分布で要約し、 群間比較には順位ベースのノンパラメトリック検定(Mann–Whitney U・Kruskal–Wallis)を使うのが安全な既定路線になる。

🔗 関連ページ