単変量分析(Univariate Analysis)とは、データセットの各カラム(変数)を1つずつ個別に分析する手法です。多変量の関係性を見る前に、まず各変数の特徴——中心傾向、ばらつき、分布の形状、外れ値の有無——を把握しておくことが、正しいモデリングの出発点になります。この記事では、数値型・カテゴリ型・テキスト型の各データに対する単変量分析の手法を包括的に解説します。
記述統計量の基本 — データの「要約」を読む
記述統計量(Descriptive Statistics)は、データの特徴を少数の数値で要約したものです。pandas の describe() メソッドで基本的な統計量を一括取得できますが、それぞれの指標が何を意味し、どのような場合に注意が必要かを理解しておくことが重要です。
import pandas as pd
import numpy as np
from scipy import stats
df = pd.read_csv("dataset.csv")
# 基本的な記述統計量
print(df.describe())
# より詳細な統計量を追加
def detailed_stats(series: pd.Series) -> dict:
"""数値カラムの詳細な記述統計量を計算する。"""
return {
"件数": series.count(),
"平均": series.mean(),
"中央値": series.median(),
"最頻値": series.mode().iloc[0] if not series.mode().empty else None,
"標準偏差": series.std(),
"分散": series.var(),
"最小値": series.min(),
"25%点": series.quantile(0.25),
"75%点": series.quantile(0.75),
"最大値": series.max(),
"範囲": series.max() - series.min(),
"IQR": series.quantile(0.75) - series.quantile(0.25),
"歪度": series.skew(),
"尖度": series.kurtosis(),
"変動係数": series.std() / series.mean() if series.mean() != 0 else None,
}
for col in df.select_dtypes(include=["number"]).columns:
print(f"\n=== {col} ===")
for k, v in detailed_stats(df[col]).items():
if isinstance(v, float):
print(f" {k}: {v:.4f}")
else:
print(f" {k}: {v}")平均と中央値 — データの「中心」を捉える
平均(Mean)は全データの総和をデータ数で割った値で、最も一般的な中心傾向の指標です。しかし、平均は外れ値の影響を強く受けます。例えば、年収データで1人が10億円の年収であれば、平均年収は大幅に上がります。中央値(Median)はデータを昇順に並べた中央の値で、外れ値の影響を受けにくい頑健な指標です。平均と中央値の乖離が大きい場合は、分布に歪みがあることを示しています。
平均 > 中央値 であれば右に歪んだ分布(右裾が長い)、平均 < 中央値 であれば左に歪んだ分布を示唆します。年収や売上などの経済データは右に歪む傾向があり、この場合は中央値の方がデータの「典型的な値」をよく表します。
標準偏差と分散 — データの「ばらつき」を測る
標準偏差(Standard Deviation)は、データが平均からどの程度散らばっているかを測る指標です。分散(Variance)はその二乗です。標準偏差が大きいほどデータのばらつきが大きく、小さいほど平均付近に集中していることを意味します。変動係数(CV = 標準偏差 / 平均)を使えば、単位やスケールが異なるカラム間でばらつきの程度を比較できます。
四分位数とIQR — 分布の広がりを段階的に把握する
四分位数(Quartiles)はデータを4等分する値で、第1四分位数(Q1, 25%点)、第2四分位数(Q2, 中央値)、第3四分位数(Q3, 75%点)から成ります。四分位範囲(IQR = Q3 - Q1)はデータの中央50%の範囲を表し、外れ値の検出にも使用されます。箱ひげ図(Box Plot)は四分位数を視覚化したもので、分布の形状と外れ値を直感的に把握できます。
- 1
Q1(第1四分位数、25%点)
データの下位25%の境界値です。これより小さい値はデータ全体の4分の1です。
- 2
Q2(中央値、50%点)
データの中央の値です。データを半分に分ける点になります。
- 3
Q3(第3四分位数、75%点)
データの上位25%の境界値です。Q3 より大きい値はデータ全体の4分の1です。
- 4
IQR(四分位範囲)= Q3 - Q1
中央50%のデータの幅を示します。Q1 - 1.5×IQR より小さい値、Q3 + 1.5×IQR より大きい値は外れ値の候補とされます。
歪度と尖度 — 分布の「形」を数値化する
歪度(Skewness)は分布の左右非対称性を表す指標です。歪度が0なら左右対称(正規分布に近い)、正なら右裾が長い(右に歪んでいる)、負なら左裾が長い(左に歪んでいる)ことを示します。尖度(Kurtosis)は分布の「尖り具合」を表し、正規分布の尖度を0として、正なら尖った分布(裾が厚い)、負なら平たい分布(裾が薄い)を意味します。歪度の絶対値が2以上、または尖度の絶対値が7以上の場合は、正規分布から大きく外れている可能性が高いです。
from scipy.stats import skew, kurtosis
# 数値カラムの歪度・尖度を一括計算
numeric_cols = df.select_dtypes(include=["number"]).columns
skew_kurt = pd.DataFrame({
"歪度": df[numeric_cols].apply(skew, nan_policy="omit"),
"尖度": df[numeric_cols].apply(kurtosis, nan_policy="omit"),
})
skew_kurt["歪みの方向"] = skew_kurt["歪度"].apply(
lambda s: "右に歪み" if s > 0.5 else ("左に歪み" if s < -0.5 else "ほぼ対称")
)
skew_kurt["尖り具合"] = skew_kurt["尖度"].apply(
lambda k: "尖った分布" if k > 1 else ("平たい分布" if k < -1 else "正規分布に近い")
)
print(skew_kurt.round(3))ヒストグラム — 数値データの分布を視覚化する
ヒストグラムは数値データの分布を視覚的に把握するための最も基本的なグラフです。横軸にデータの値域を区間(ビン)に分割し、縦軸に各区間に入るデータの頻度を示します。ビンの数を適切に設定することが重要で、少なすぎると分布の特徴が消え、多すぎるとノイズが目立ちます。Sturges の公式(bins = 1 + log2(n))やFreedman-Diaconis のルールが一般的に使われます。
ヒストグラムで二峰性(2つの山がある分布)が見えた場合は、データが2つの異なる母集団の混合である可能性があります。例えば、身長データに男女が混在していると二峰性を示すことがあります。この場合はカテゴリ変数で層別して分析することで、各群の特徴がより明確になります。
カテゴリ型データの値集計 — Value Counts
カテゴリ型のカラムでは、各カテゴリの出現回数と割合を集計します。ユニーク値の数、最頻値、最頻値の割合などが基本的な確認項目です。極端に出現頻度が低いカテゴリ(レアカテゴリ)は、モデルの学習に十分な情報量がないため、他のカテゴリと統合することを検討します。
# カテゴリ型カラムの分析
for col in df.select_dtypes(include=["object", "category"]).columns:
print(f"\n=== {col} ===")
print(f" ユニーク値数: {df[col].nunique()}")
print(f" 最頻値: {df[col].mode().iloc[0]}")
print(f" 最頻値の割合: {df[col].value_counts(normalize=True).iloc[0]:.2%}")
print(f"\n 上位10カテゴリ:")
vc = df[col].value_counts()
for val, count in vc.head(10).items():
pct = count / len(df) * 100
bar = "█" * int(pct / 2)
print(f" {val:30s} {count:>6,} ({pct:5.1f}%) {bar}")
if len(vc) > 10:
others = vc.iloc[10:].sum()
print(f" {'(その他)':30s} {others:>6,} ({others / len(df) * 100:5.1f}%)")テキスト型データの分析 — 文字数の分布
テキスト型カラム(自由記述のコメントやレビューなど)では、文字数の分布を確認することが基本的な分析の出発点です。文字数のヒストグラムを作成することで、極端に短いテキスト(1〜2文字の「はい」「いいえ」など)や異常に長いテキスト(コピー&ペーストの事故など)を発見できます。また、空文字列の割合も確認しておくことで、実質的な欠損を把握できます。
- 1
文字数分布の確認
各テキストの文字数を計算し、平均・中央値・最大値を確認します。最大値が極端に大きい場合は、データ入力の問題やHTMLタグの混入が疑われます。
- 2
空文字列と短文の検出
空文字列("")やスペースのみの文字列はプログラム上は欠損ではありませんが、情報としては欠損と同等です。これらを NaN に変換してから欠損分析を行います。
- 3
単語数の分布
文字数に加えて単語数(スペースや句読点で分割)の分布も確認します。日本語テキストの場合は形態素解析で分かち書きしてからカウントします。
パーセンタイルとユニーク値数 — 追加の視点
四分位数に加えて、1パーセンタイル、5パーセンタイル、95パーセンタイル、99パーセンタイルを確認することで、分布の裾の広がりをより正確に把握できます。特に外れ値の検出において、99パーセンタイルと最大値の差が大きい場合は、極端な外れ値が存在する可能性が高いです。また、ユニーク値の数と全体の行数の比率は、そのカラムの情報量を推測する手がかりになります。
# パーセンタイル分析
percentiles = [0.01, 0.05, 0.1, 0.25, 0.5, 0.75, 0.9, 0.95, 0.99]
for col in df.select_dtypes(include=["number"]).columns:
vals = df[col].quantile(percentiles)
print(f"\n{col}:")
for p, v in vals.items():
print(f" {p*100:5.1f}%: {v:>12,.2f}")
# 外れ値の候補を検出
p99 = df[col].quantile(0.99)
max_val = df[col].max()
if max_val > p99 * 3: # 99%点の3倍以上
n_extreme = (df[col] > p99 * 3).sum()
print(f" ⚠ 極端な外れ値の候補: {n_extreme}件(99%点の3倍超)")Qast の単変量分析ダッシュボード
Qast の EDA 機能では、データセット内のすべてのカラムに対して単変量分析が自動実行されます。数値型カラムには記述統計量・ヒストグラム・箱ひげ図が、カテゴリ型カラムには値の集計表・棒グラフが、テキスト型カラムには文字数の分布がそれぞれ表示されます。各カラムの統計量は一覧テーブルでまとめて確認でき、異常値や偏りが検出されたカラムにはアラートマークが付与されます。コードを書かずに全カラムの特徴を把握できるため、分析の初期フェーズを大幅に効率化できます。
単変量分析で把握した各カラムの特徴は、後続の多変量分析や特徴量エンジニアリングの基盤になります。例えば、右に歪んだ分布を持つカラムは対数変換で正規化できる可能性があり、これによりモデルの精度が向上するケースがあります。Qast ではこうした前処理の候補も自動で提案されます。


