第3回 データの把握
はじめに
本シリーズ「Pythonで学ぶデータ分析実践」の第3回では、データの把握について解説します。
データ分析の最初のステップとして、分析対象のデータ全体の傾向や特徴を大まかに把握することが重要です。前回の前処理でデータを整形した後、基本統計量の計算やデータの可視化を行い、データの「全体像」をつかみます。この段階でデータの特徴を正しく理解しておくことで、その後の分析手法の選択や仮説の立案がスムーズに進みます。
シリーズ全体の構成(全12回)
| 回 | テーマ |
|---|---|
| 第1回 | データ分析とは |
| 第2回 | データの前処理 |
| 第3回 | データの把握(本記事) |
| 第4回 | 相関分析 |
| 第5回 | 統計的推定 |
| 第6回 | 統計的検定 |
| 第7回 | 分散分析 |
| 第8回 | 回帰分析 |
| 第9回 | 時系列データ分析 |
| 第10回 | クラス分類 |
| 第11回 | クラスタリング |
| 第12回 | 次元削減 |
3-1. 基本統計量
基本統計量とは、データの基本的な特性を数値で表したものです。大きく代表値(データの分布全体を一つの数で表す指標)と散布度(データのばらつきの大きさを表す指標)に分けられます。
代表値と散布度の一覧
| 分類 | 指標 | 説明 |
|---|---|---|
| 代表値 | 平均値 | 全データの合計をデータ数で割った値 |
| 代表値 | 中央値 | データを並べたときの中央の値 |
| 代表値 | 最頻値 | 最も頻繁に出現する値 |
| 代表値 | 四分位数 | データを4等分する位置の値 |
| 散布度 | 分散 | データの平均からのばらつき |
| 散布度 | 標準偏差 | 分散の平方根(直感的なばらつき) |
| 散布度 | 範囲 | 最大値 - 最小値 |
| 散布度 | 四分位範囲 | Q3 - Q1 |
Pandasでの基本統計量の算出
import pandas as pd
import numpy as np
# サンプルデータの作成
np.random.seed(42)
df = pd.DataFrame({
"age": [25, 30, 35, 28, 42, 38, 45, 33, 29, 50],
"salary": [300, 450, 500, 380, 700, 600, 800, 480, 350, 900],
"score": [72, 85, 90, 68, 95, 88, 92, 78, 80, 98]
})
# describe()で基本統計量を一括表示
print(df.describe())
出力例:
| age | salary | score | |
|---|---|---|---|
| count | 10.0 | 10.0 | 10.0 |
| mean | 35.5 | 546.0 | 84.6 |
| std | 8.18 | 194.5 | 9.96 |
| min | 25.0 | 300.0 | 68.0 |
| 25% | 29.25 | 397.5 | 77.0 |
| 50% | 34.0 | 490.0 | 86.5 |
| 75% | 41.0 | 675.0 | 92.5 |
| max | 50.0 | 900.0 | 98.0 |
個別の統計量の算出
# 各統計量を個別に算出
print(f"合計: {df['salary'].sum()}")
print(f"平均値: {df['salary'].mean()}")
print(f"中央値: {df['salary'].median()}")
print(f"最頻値: {df['salary'].mode()[0]}")
print(f"最大値: {df['salary'].max()}")
print(f"最小値: {df['salary'].min()}")
print(f"範囲: {df['salary'].max() - df['salary'].min()}")
print(f"分散: {df['salary'].var()}")
print(f"標準偏差: {df['salary'].std()}")
print(f"四分位範囲(IQR): {df['salary'].quantile(0.75) - df['salary'].quantile(0.25)}")
3-2. 平均・中央値・最頻値
代表値はデータの「中心的な値」を一つの数で要約するものです。それぞれの特性を理解し、適切に使い分けることが重要です。
算術平均(Mean)
全データの合計をデータ数で割った値です。最も一般的に使われる代表値ですが、外れ値の影響を受けやすい特徴があります。
$$\bar{x} = \frac{1}{n}\sum_{i=1}^{n} x_i = \frac{x_1 + x_2 + x_3 + \cdots + x_n}{n}$$
import numpy as np
data = [300, 350, 400, 420, 450, 480, 500, 550, 600, 3000]
# 算術平均
mean_val = np.mean(data)
print(f"算術平均: {mean_val}") # 705.0(外れ値3000の影響で高くなる)
加重平均(Weighted Mean)
各データに重み(ウェイト)を付けて計算する平均です。データの重要度が異なる場合に使用します。
# 科目ごとの点数と単位数(重み)
scores = [80, 70, 90] # 数学、英語、物理
weights = [4, 2, 3] # 単位数
# 加重平均
weighted_mean = np.average(scores, weights=weights)
print(f"加重平均: {weighted_mean:.1f}") # 81.1
中央値(Median)
データを小さい順に並べたとき、ちょうど中央に位置する値です。外れ値の影響を受けにくいため、年収データなど偏りのあるデータに適しています。
data = [300, 350, 400, 420, 450, 480, 500, 550, 600, 3000]
median_val = np.median(data)
print(f"中央値: {median_val}") # 465.0(外れ値の影響を受けない)
最頻値(Mode)
データの中で最も頻繁に出現する値です。カテゴリカルデータにも適用できる唯一の代表値です。
from scipy import stats
data = [1, 2, 2, 3, 3, 3, 4, 4, 5]
mode_val = stats.mode(data, keepdims=True)
print(f"最頻値: {mode_val.mode[0]}") # 3(3回出現)
代表値の使い分け
| 代表値 | 適している場面 | 注意点 |
|---|---|---|
| 平均値 | データが左右対称に分布している場合 | 外れ値の影響を受けやすい |
| 中央値 | 外れ値がある場合、年収などの偏った分布 | データの合計情報が失われる |
| 最頻値 | カテゴリデータ、離散データ | 連続データでは求めにくい |
実践例:平均値と中央値の違いを実感する
import pandas as pd
import numpy as np
# 年収データ(右に裾が長い分布)
salaries = [300, 320, 350, 380, 400, 420, 450, 500, 550, 600,
650, 700, 800, 1000, 1500, 2000, 5000]
print(f"平均年収: {np.mean(salaries):.0f}万円") # 約878万円
print(f"中央年収: {np.median(salaries):.0f}万円") # 550万円
# → 年収のような偏った分布では中央値の方が「実感」に近い
3-3. 分散・標準偏差
データのばらつきを数値化する指標が分散と標準偏差です。代表値だけではデータの特徴を十分に把握できないため、散布度とあわせて確認することが重要です。
偏差
各データと平均値の差を偏差と呼びます。偏差の合計は必ず0になります。
$$偏差 = x_i - \bar{x}$$
$$\sum_{i=1}^{n}(x_i - \bar{x}) = 0$$
偏差平方和
偏差の合計が0になるため、ばらつきを表すには偏差を2乗して合計します。これを偏差平方和と呼びます。
$$偏差平方和 = \sum_{i=1}^{n}(x_i - \bar{x})^2$$
分散(Variance)
偏差平方和をデータ数nで割ったものが分散です。nが大きくなると偏差平方和も大きくなるため、nで割って平均化します。
$$\sigma^2 = \frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^2$$
Pandasのvar()やNumPyのnp.var()はデフォルトで不偏分散(n-1で割る)を計算します。母分散を求める場合はddof=0を指定します。
標準偏差(Standard Deviation)
分散の平方根を取ったものが標準偏差です。分散は単位が2乗になるため、元のデータと同じ単位で比較できるよう平方根を取ります。
$$\sigma = \sqrt{\frac{1}{n}\sum_{i=1}^{n}(x_i - \bar{x})^2}$$
標準偏差は、日常的な感覚でデータのばらつきを把握するのに便利です。
- 気温の標準偏差が大きい → 「その週の気温の変動が大きい」
- 気温の標準偏差が小さい → 「気温が安定している」
Pythonでの計算
import numpy as np
import pandas as pd
data = np.array([65, 70, 75, 80, 85, 90, 95, 100, 60, 72])
# 基本的な計算
mean = np.mean(data)
print(f"平均: {mean}")
# 偏差
deviations = data - mean
print(f"偏差: {deviations}")
print(f"偏差の合計: {deviations.sum():.10f}") # ほぼ0
# 偏差平方和
ss = np.sum(deviations**2)
print(f"偏差平方和: {ss}")
# 母分散(nで割る)
variance_pop = np.var(data, ddof=0)
print(f"母分散: {variance_pop:.2f}")
# 不偏分散(n-1で割る)- サンプルデータの場合
variance_sample = np.var(data, ddof=1)
print(f"不偏分散: {variance_sample:.2f}")
# 標準偏差
std_pop = np.std(data, ddof=0)
print(f"母標準偏差: {std_pop:.2f}")
std_sample = np.std(data, ddof=1)
print(f"標本標準偏差: {std_sample:.2f}")
母分散と不偏分散の違い
| 項目 | 母分散 | 不偏分散 |
|---|---|---|
| 割る数 | n | n-1 |
| 用途 | 母集団全体のデータがある場合 | 標本データから母集団の分散を推定する場合 |
| NumPy | np.var(data, ddof=0) |
np.var(data, ddof=1) |
| Pandas | df.var(ddof=0) |
df.var()(デフォルト) |
変動係数(CV: Coefficient of Variation)
異なる単位や異なるスケールのデータのばらつきを比較するときに使用します。
$$CV = \frac{\sigma}{\bar{x}} \times 100 , (%)$$
# 体重と身長のばらつきを比較
weight = np.array([55, 60, 65, 70, 75])
height = np.array([155, 160, 165, 170, 175])
cv_weight = (np.std(weight) / np.mean(weight)) * 100
cv_height = (np.std(height) / np.mean(height)) * 100
print(f"体重のCV: {cv_weight:.2f}%")
print(f"身長のCV: {cv_height:.2f}%")
# → CVが大きい方がばらつきが大きい
3-4. ヒストグラム
データの大まかな分布を知るために、データをある幅ごとに区切り(階級)、その中に含まれるデータの個数を数える方法があります。ヒストグラムはこの度数分布をグラフで表現したものです。
度数と度数分布表
度数
基本統計量を求める対象に文字列(カテゴリ)が含まれている場合、その出現頻度を算出します。この出現頻度を度数と呼びます。
度数分布表
観測されたデータの値をいくつかの区間(階級)に分け、各階級の度数を表の形式で表したものを度数分布表と呼びます。
| 階級 | 度数 | 相対度数 | 累積相対度数 |
|---|---|---|---|
| 20-24 | 3 | 0.05 | 0.05 |
| 25-29 | 5 | 0.08 | 0.13 |
| 30-34 | 7 | 0.11 | 0.24 |
| 35-39 | 10 | 0.16 | 0.40 |
| 40-44 | 13 | 0.20 | 0.60 |
| 45-49 | 11 | 0.17 | 0.77 |
| 50-54 | 8 | 0.13 | 0.90 |
| 55-59 | 5 | 0.08 | 0.98 |
| 60-64 | 2 | 0.03 | 1.00 |
ヒストグラムとは
度数分布表をグラフにしたものがヒストグラムです。量的データの可視化に用い、数値データの値域や分布の様子を確認することができます。
- 横軸:階級幅の値(ビン)
- 縦軸:各階級の度数
Pythonでのヒストグラム作成
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib # 日本語表示対応
# サンプルデータの生成(正規分布に従うテストスコア)
np.random.seed(42)
scores = np.random.normal(loc=65, scale=15, size=200)
# 基本的なヒストグラム
plt.figure(figsize=(10, 6))
plt.hist(scores, bins=15, edgecolor='black', alpha=0.7, color='steelblue')
plt.xlabel("テストスコア", fontsize=12)
plt.ylabel("度数(人数)", fontsize=12)
plt.title("テストスコアの分布", fontsize=14)
plt.axvline(np.mean(scores), color='red', linestyle='--', label=f'平均: {np.mean(scores):.1f}')
plt.axvline(np.median(scores), color='green', linestyle='--', label=f'中央値: {np.median(scores):.1f}')
plt.legend(fontsize=11)
plt.grid(axis='y', alpha=0.3)
plt.show()
階級幅(ビン数)の決め方
度数分布表やヒストグラムを作成するには、各階級の幅を決める必要があります。適切な階級数を決定するためにスタージェスの公式が使われます。
$$k = \log_2 n + 1$$
- n:サンプルサイズ
- k:階級数
階級幅はデータの範囲(最大値 - 最小値)をkで割って求めます。
import numpy as np
n = len(scores)
# スタージェスの公式
k = int(np.log2(n) + 1)
bin_width = (scores.max() - scores.min()) / k
print(f"データ数: {n}")
print(f"推奨階級数: {k}")
print(f"階級幅: {bin_width:.1f}")
# スタージェスの公式によるビン数でヒストグラム作成
plt.figure(figsize=(10, 6))
plt.hist(scores, bins=k, edgecolor='black', alpha=0.7, color='steelblue')
plt.xlabel("テストスコア", fontsize=12)
plt.ylabel("度数", fontsize=12)
plt.title(f"ヒストグラム(スタージェスの公式: bins={k})", fontsize=14)
plt.show()
ヒストグラムの応用
# 複数のデータを重ねて比較
np.random.seed(42)
class_a = np.random.normal(loc=70, scale=10, size=100)
class_b = np.random.normal(loc=60, scale=15, size=100)
plt.figure(figsize=(10, 6))
plt.hist(class_a, bins=15, alpha=0.6, label='クラスA', color='steelblue', edgecolor='black')
plt.hist(class_b, bins=15, alpha=0.6, label='クラスB', color='coral', edgecolor='black')
plt.xlabel("テストスコア", fontsize=12)
plt.ylabel("度数", fontsize=12)
plt.title("クラス別スコア分布の比較", fontsize=14)
plt.legend(fontsize=11)
plt.show()
Seabornでのヒストグラム(カーネル密度推定付き)
import seaborn as sns
plt.figure(figsize=(10, 6))
sns.histplot(scores, bins=15, kde=True, color='steelblue', edgecolor='black')
plt.xlabel("テストスコア", fontsize=12)
plt.ylabel("度数", fontsize=12)
plt.title("ヒストグラム + カーネル密度推定(KDE)", fontsize=14)
plt.show()
3-5. 箱ひげ図
**箱ひげ図(ボックスプロット)**は、ばらつきのあるデータをわかりやすく表現するためのグラフで、量的データを質的データ(カテゴリ)とともに可視化できます。データの分布、中央値、四分位数、外れ値を一目で把握できるのが特徴です。
箱ひげ図の構成要素
外れ値 ●
|
--------|-------- 上限(Q3 + 1.5×IQR)
| |
| ひげ |
| |
+================+ 第3四分位数(Q3: 75%点)
| |
| 箱 |
| |
+-------|--------+ 中央値(Q2: 50%点)
| |
| 箱 |
| |
+================+ 第1四分位数(Q1: 25%点)
| |
| ひげ |
| |
--------|-------- 下限(Q1 - 1.5×IQR)
|
外れ値 ●
| 要素 | 説明 |
|---|---|
| 箱(Box) | Q1からQ3の範囲。全データの50%が含まれる |
| 中央線 | 中央値(Q2) |
| ひげ(Whisker) | Q1 - 1.5×IQR 〜 Q3 + 1.5×IQR の範囲 |
| 外れ値 | ひげの外側にあるデータ点 |
| IQR | 四分位範囲(Q3 - Q1) |
Pythonでの箱ひげ図作成
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
# サンプルデータ
np.random.seed(42)
data_a = np.random.normal(loc=50, scale=10, size=100)
data_b = np.random.normal(loc=60, scale=15, size=100)
data_c = np.random.normal(loc=55, scale=8, size=100)
# 基本的な箱ひげ図
plt.figure(figsize=(8, 6))
plt.boxplot([data_a, data_b, data_c],
labels=['グループA', 'グループB', 'グループC'],
patch_artist=True,
boxprops=dict(facecolor='lightblue'))
plt.ylabel("値", fontsize=12)
plt.title("グループ別の箱ひげ図", fontsize=14)
plt.grid(axis='y', alpha=0.3)
plt.show()
Seabornでの箱ひげ図
import seaborn as sns
import pandas as pd
# DataFrameでの箱ひげ図
df = pd.DataFrame({
"score": np.concatenate([data_a, data_b, data_c]),
"group": ["A"]*100 + ["B"]*100 + ["C"]*100
})
plt.figure(figsize=(8, 6))
sns.boxplot(x="group", y="score", data=df, palette="Set2")
plt.xlabel("グループ", fontsize=12)
plt.ylabel("スコア", fontsize=12)
plt.title("グループ別スコアの箱ひげ図", fontsize=14)
plt.show()
箱ひげ図から読み取れる情報
- 箱の位置が高い/低い → そのグループの値が全体的に大きい/小さい
- 箱が大きい → データのばらつきが大きい
- 中央線が箱の中央にない → 分布に歪みがある
- 外れ値が多い → 極端な値が多く含まれている
- ひげが長い → データの広がりが大きい
3-6. 散布図
**散布図(Scatter Plot)**は、2つの変数の関係を視覚的に確認するためのグラフです。各データ点を平面上にプロットすることで、変数間の相関関係や傾向を把握できます。
散布図から読み取れるパターン
| パターン | 特徴 | 例 |
|---|---|---|
| 正の相関 | 一方が増えると他方も増える | 勉強時間とテスト点数 |
| 負の相関 | 一方が増えると他方が減る | 気温とコート売上 |
| 無相関 | 明確な傾向がない | 身長と好きな色 |
| 非線形 | 曲線的な関係がある | 経験年数と年収(頭打ち) |
Pythonでの散布図作成
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
# サンプルデータ(勉強時間とテストスコア)
np.random.seed(42)
study_hours = np.random.uniform(1, 10, 50)
test_scores = 30 + 6 * study_hours + np.random.normal(0, 5, 50)
# 基本的な散布図
plt.figure(figsize=(8, 6))
plt.scatter(study_hours, test_scores, alpha=0.7, color='steelblue', edgecolors='black')
plt.xlabel("勉強時間(時間)", fontsize=12)
plt.ylabel("テストスコア", fontsize=12)
plt.title("勉強時間とテストスコアの関係", fontsize=14)
plt.grid(alpha=0.3)
plt.show()
回帰直線を追加した散布図
from numpy.polynomial import polynomial as P
plt.figure(figsize=(8, 6))
plt.scatter(study_hours, test_scores, alpha=0.7, color='steelblue', edgecolors='black')
# 回帰直線
z = np.polyfit(study_hours, test_scores, 1)
p = np.poly1d(z)
x_line = np.linspace(study_hours.min(), study_hours.max(), 100)
plt.plot(x_line, p(x_line), color='red', linestyle='--', linewidth=2,
label=f'回帰直線: y = {z[0]:.2f}x + {z[1]:.2f}')
plt.xlabel("勉強時間(時間)", fontsize=12)
plt.ylabel("テストスコア", fontsize=12)
plt.title("勉強時間とテストスコアの関係(回帰直線付き)", fontsize=14)
plt.legend(fontsize=11)
plt.grid(alpha=0.3)
plt.show()
Seabornでの散布図
import seaborn as sns
import pandas as pd
df = pd.DataFrame({
"勉強時間": study_hours,
"テストスコア": test_scores,
"性別": np.random.choice(["男性", "女性"], 50)
})
# カテゴリ別の散布図
plt.figure(figsize=(8, 6))
sns.scatterplot(x="勉強時間", y="テストスコア", hue="性別", data=df, s=80)
plt.title("性別ごとの勉強時間とスコアの関係", fontsize=14)
plt.show()
# 回帰直線付き散布図(lmplot)
sns.lmplot(x="勉強時間", y="テストスコア", hue="性別", data=df,
height=6, aspect=1.2)
plt.title("回帰直線付き散布図")
plt.show()
ペアプロット(全変数の組み合わせ)
複数の変数間の関係を一度に確認したい場合は、ペアプロットが便利です。
import seaborn as sns
# Irisデータセットを使った例
iris = sns.load_dataset("iris")
sns.pairplot(iris, hue="species", diag_kind="hist")
plt.suptitle("Irisデータセットのペアプロット", y=1.02, fontsize=14)
plt.show()
3-7. 正規分布
**正規分布(Normal Distribution)**は、統計学で最も重要な確率分布の一つです。自然界や社会現象の多くのデータが正規分布に従うことが知られており、統計的推定や検定の理論的基盤となっています。
正規分布の確率密度関数
平均を $\mu$、分散を $\sigma^2$ とした場合、正規分布 $N(\mu, \sigma^2)$ の確率密度関数は以下で表されます。
$$f(x) = \frac{1}{\sqrt{2\pi}\sigma} \exp\left(-\frac{(x-\mu)^2}{2\sigma^2}\right)$$
正規分布の主な特徴
- 左右に裾野を持つ(理論上は $-\infty$ から $+\infty$ まで)
- 曲線は左右対称(平均を中心に対称)
- ピークは平均の位置
-
標準偏差が曲線の形を特徴付ける
- 標準偏差が小さい → 背が高く細い分布
- 標準偏差が大きい → 背が低く太い分布
- 曲線の面積(AUC)は1
- 平均値 = 中央値 = 最頻値
標準正規分布と68-95-99.7ルール
平均0、標準偏差1の正規分布を標準正規分布 $N(0, 1)$ と呼びます。
| 範囲 | 含まれるデータの割合 |
|---|---|
| $\mu \pm 1\sigma$ | 約68.3% |
| $\mu \pm 2\sigma$ | 約95.4% |
| $\mu \pm 3\sigma$ | 約99.7% |
この1, 2, 3といった値を**z-score(Zスコア)**と呼びます。
正規分布の可視化
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
import japanize_matplotlib
# 正規分布の描画
x = np.linspace(-4, 4, 1000)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 左:平均が異なる正規分布
for mu in [-1, 0, 1]:
y = stats.norm.pdf(x, loc=mu, scale=1)
axes[0].plot(x, y, label=f'μ={mu}, σ=1')
axes[0].set_title("平均が異なる正規分布", fontsize=13)
axes[0].set_xlabel("x")
axes[0].set_ylabel("確率密度")
axes[0].legend()
axes[0].grid(alpha=0.3)
# 右:標準偏差が異なる正規分布
for sigma in [0.5, 1, 2]:
y = stats.norm.pdf(x, loc=0, scale=sigma)
axes[1].plot(x, y, label=f'μ=0, σ={sigma}')
axes[1].set_title("標準偏差が異なる正規分布", fontsize=13)
axes[1].set_xlabel("x")
axes[1].set_ylabel("確率密度")
axes[1].legend()
axes[1].grid(alpha=0.3)
plt.tight_layout()
plt.show()
68-95-99.7ルールの可視化
x = np.linspace(-4, 4, 1000)
y = stats.norm.pdf(x, 0, 1)
plt.figure(figsize=(10, 6))
plt.plot(x, y, 'k-', linewidth=2)
# 各範囲を塗りつぶし
plt.fill_between(x, y, where=(x >= -3) & (x <= 3), alpha=0.1, color='blue', label='±3σ (99.7%)')
plt.fill_between(x, y, where=(x >= -2) & (x <= 2), alpha=0.2, color='blue', label='±2σ (95.4%)')
plt.fill_between(x, y, where=(x >= -1) & (x <= 1), alpha=0.3, color='blue', label='±1σ (68.3%)')
plt.xlabel("Zスコア", fontsize=12)
plt.ylabel("確率密度", fontsize=12)
plt.title("標準正規分布と68-95-99.7ルール", fontsize=14)
plt.legend(fontsize=11)
plt.grid(alpha=0.3)
plt.show()
データの標準化(正規分布の標準化)
データの標準化とは、平均値を0、標準偏差を1に変換する処理です。異なるスケールのデータを比較可能にします。
$$z = \frac{x - \mu}{\sigma}$$
標準化したデータは標準正規分布に従うため、$-1.96 \leq z \leq 1.96$ の区間に全体の95%のデータが含まれます。
import numpy as np
from scipy import stats
# テストスコアの標準化
scores = np.array([55, 60, 65, 70, 75, 80, 85, 90, 95])
mean = np.mean(scores)
std = np.std(scores)
# 標準化(Zスコアの計算)
z_scores = (scores - mean) / std
print(f"元のデータ: {scores}")
print(f"平均: {mean}, 標準偏差: {std:.2f}")
print(f"Zスコア: {np.round(z_scores, 2)}")
# 特定のスコアの偏差値を計算
# 偏差値 = 50 + 10 × Zスコア
deviation_values = 50 + 10 * z_scores
print(f"偏差値: {np.round(deviation_values, 1)}")
歪度と尖度
データの形状を詳しく理解するための指標として歪度と尖度があります。
歪度(Skewness)
データの分布がどれだけ左右に傾いているかを示す指標です。
| 歪度の値 | 分布の形状 |
|---|---|
| 歪度 = 0 | 左右対称 |
| 歪度 > 0(正) | 右に長い裾(右裾が重い) |
| 歪度 < 0(負) | 左に長い裾(左裾が重い) |
尖度(Kurtosis)
データの分布がどれだけ鋭く尖っているかを示す指標です。
| 尖度の値 | 分布の形状 |
|---|---|
| 尖度 = 0 | 正規分布と同程度 |
| 尖度 > 0(正) | 鋭く尖った分布(データが平均に集中) |
| 尖度 < 0(負) | 平坦な分布(データが広く散らばっている) |
from scipy import stats
import numpy as np
# さまざまな分布のデータ
np.random.seed(42)
normal_data = np.random.normal(0, 1, 1000) # 正規分布
right_skewed = np.random.exponential(2, 1000) # 右に歪んだ分布
left_skewed = -np.random.exponential(2, 1000) # 左に歪んだ分布
# 歪度の計算
print(f"正規分布の歪度: {stats.skew(normal_data):.4f}")
print(f"右に歪んだ分布の歪度: {stats.skew(right_skewed):.4f}")
print(f"左に歪んだ分布の歪度: {stats.skew(left_skewed):.4f}")
# 尖度の計算
print(f"\n正規分布の尖度: {stats.kurtosis(normal_data):.4f}")
print(f"右に歪んだ分布の尖度: {stats.kurtosis(right_skewed):.4f}")
正規性の確認(シャピロ・ウィルク検定)
データが正規分布に従うかどうかを統計的に検定することもできます。
from scipy import stats
# シャピロ・ウィルク検定
stat, p_value = stats.shapiro(normal_data[:50]) # サンプル数5000以下
print(f"統計量: {stat:.4f}")
print(f"p値: {p_value:.4f}")
if p_value > 0.05:
print("→ 正規分布に従う(帰無仮説を棄却できない)")
else:
print("→ 正規分布に従わない(帰無仮説を棄却)")
補足:実践的なデータ把握の進め方
データ把握の手順まとめ
実務では以下の順番でデータの把握を進めると効率的です。
1. df.shape → データの行数・列数を確認
2. df.dtypes → 各列のデータ型を確認
3. df.describe() → 基本統計量を一括確認
4. df.isnull().sum() → 欠損値の確認
5. ヒストグラム → 各変数の分布を確認
6. 箱ひげ図 → 外れ値とカテゴリ別の分布を確認
7. 散布図/ペアプロット → 変数間の関係を確認
8. 相関行列 → 数値的に変数間の関係を確認
Pandasプロファイリング(自動EDA)
大量の変数を手動で確認するのは手間がかかるため、自動で探索的データ分析(EDA)を行うライブラリも活用できます。
# ydata-profilingのインストール
# pip install ydata-profiling
from ydata_profiling import ProfileReport
import pandas as pd
df = pd.read_csv("data.csv")
# プロファイルレポートの生成
profile = ProfileReport(df, title="データ分析レポート", explorative=True)
profile.to_file("report.html") # HTMLレポートとして出力
相関行列のヒートマップ
複数の数値変数間の相関を一目で把握するために、相関行列をヒートマップで可視化します。
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import japanize_matplotlib
# サンプルデータ
np.random.seed(42)
df = pd.DataFrame({
"年齢": np.random.randint(20, 60, 100),
"年収": np.random.randint(300, 1200, 100),
"勤続年数": np.random.randint(1, 30, 100),
"満足度": np.random.randint(1, 10, 100)
})
# 相関行列
corr_matrix = df.corr()
# ヒートマップ
plt.figure(figsize=(8, 6))
sns.heatmap(corr_matrix, annot=True, cmap="coolwarm", fmt=".2f",
vmin=-1, vmax=1, square=True, linewidths=0.5)
plt.title("相関行列のヒートマップ", fontsize=14)
plt.show()
まとめ
本記事では、データの把握として以下の内容を解説しました。
| 項目 | ポイント |
|---|---|
| 基本統計量 | 代表値(平均・中央値・最頻値)と散布度(分散・標準偏差) |
| 平均・中央値・最頻値 | データの偏りに応じて適切な代表値を選択する |
| 分散・標準偏差 | データのばらつきを定量化する指標 |
| ヒストグラム | 量的データの分布を可視化、スタージェスの公式で階級数を決定 |
| 箱ひげ図 | 四分位数と外れ値を一目で把握、グループ比較に有効 |
| 散布図 | 2変数間の関係を可視化、相関パターンを確認 |
| 正規分布 | 統計学の基盤となる確率分布、68-95-99.7ルール |
データ把握のチェックリスト
- 基本統計量(describe())を確認したか
- 各変数のヒストグラムで分布を確認したか
- 外れ値を箱ひげ図で可視化したか
- 変数間の関係を散布図で確認したか
- データが正規分布に従うか確認したか
- 歪度・尖度でデータの形状を把握したか
- カテゴリ別の分布の違いを確認したか
シリーズ記事一覧
- 【第1回】Pythonで学ぶデータ分析実践 〜データ分析とは〜
- 【第2回】データの前処理(欠損値・外れ値・ダミー変数・データスケーリングなど)
- 【第3回】データの把握(基本統計量・ヒストグラム・箱ひげ図・散布図など)
- 【第4回】相関分析(共分散・相関係数・相関比・連関係数など)
- 【第5回】統計的推定(母集団と標本・点推定・区間推定・信頼区間)
- 【第6回】統計的検定(仮説検定・p値・t検定・カイ二乗検定など)
- 【第7回】分散分析(一元分散分析・二元分散分析・多重比較)
- 【第8回】回帰分析(単回帰・重回帰・一般化線形モデル・モデル評価)
- 【第9回】時系列データ分析(トレンド・季節性・ARIMA・SARIMAなど)
- 【第10回】クラス分類(決定木・ランダムフォレスト・ロジスティック回帰など)
- 【第11回】クラスタリング(k-means・階層クラスタリング・エルボー法など)
- 【第12回】次元削減(主成分分析・寄与率・可視化)
- 【発展編】分析精度の向上(特徴量エンジニアリング・ハイパーパラメータ調整・アンサンブル学習など)
- 【発展編】ニューラルネットワーク入門(パーセプトロン・誤差逆伝播法・TensorFlow・Kerasなど)
次回の第4回では、変数間の関係をさらに深掘りし、共分散や相関係数、相関比、連関係数を使った相関分析について解説します。相関と因果関係の違いについても触れていきます。