分布とヒストグラム|記述統計と標本
このページはプロモーション(広告)を含みます。
記述統計と標本(初級・読む25分+演習35分)
この授業のゴール
- データを階級に分けて度数・相対度数・累積相対度数の表を作れる
- 階級幅が不揃いのとき、度数ではなく密度で棒の高さを決める理由を説明し、計算できる
- ヒストグラムから山の数と裾の向き(対称・右に裾が長い)を読み取り、平均と中央値の位置関係と結びつけられる
- 階級の境界(左閉右開か右閉か)と階級幅の選び方で見え方が変わることを確かめられる
仕事でどう使うか
平均や合計だけでは見えない情報が、分布(値がどのあたりにどれだけ散らばっているか)の形に入っています。分布を見る最初の道具がヒストグラムです。
| 路線 | この授業の使いどころ |
|---|---|
| DS(データサイエンティスト) | 分析の最初にヒストグラムを描き、山の数・裾の長さ・異常な塊を確認する。平均に頼る前の定番手順です |
| ML(機械学習エンジニア) | 特徴量の分布が極端に偏っていれば、対数変換や分位点での区切りを検討する。訓練データと本番データで分布が違わないかも見る |
| DX(DX推進・AI企画) | 処理時間や待ち時間のヒストグラムから、「平均は短いが、遅いケースが長く伸びる」業務の問題を見つける。KPIを平均だけにしない根拠になります |
| RS(研究者) | 論文の図の読み書き。階級幅と境界の設定を明記しないと、再現できない図になります |
実務のダッシュボードで「平均3.2日」とだけ表示されているとき、分布の形を確認できるかどうかが、判断の質を分けます。
前提
次の言葉を使います。
- 平均・中央値の手計算 → 平均・合計・中央値を手計算
- 棒グラフと軸の読み方 → 軸とグラフを読む
- 数値の尺度(比尺度・間隔尺度) → 変数の尺度
コードは numpy(数値計算ライブラリ)の np.histogram と、matplotlib(グラフ描画ライブラリ)を使います。
身近な状況:20件の注文金額、平均は5.165千円
架空のネットショップで、ある日の注文20件の金額(千円)が次のとおりでした。
[1.2, 1.8, 2.1, 2.4, 2.5, 2.9, 3.0, 3.1, 3.4, 3.6, 3.9, 4.2, 4.5, 5.1, 5.6, 6.3, 7.8, 9.5, 12.4, 18.0]
合計は103.3千円、平均は 103.3÷20=5.165千円です。担当者は「注文の金額はだいたい5千円」と説明しました。しかし、並べてみると5千円付近の注文は数件で、20件中11件は4千円未満です。12.4千円と18.0千円の2件が平均を押し上げています。
数字を眺めても形は見えません。値を範囲ごとに仕分けして、件数を棒の高さで描くと、分布の形が見えてきます。これがヒストグラムです。この授業では、この20件を材料にして、表の作り方、読み方、落とし穴を順に確認します。
直感的な説明:箱に仕分けて、積み上げる
郵便物を「0〜2千円の箱」「2〜4千円の箱」…と金額ごとの箱に入れて、箱ごとに何通入ったかを数えると想像してください。箱の高さを棒にして横に並べたものがヒストグラムです。
- 高い棒=その範囲の値が多い。
- 低い棒や隙間=その範囲の値が少ない、またはない。
- 山が1つ(単峰)か2つ以上(多峰)か。2つあれば、性質の違う集団が混ざっている可能性があります。
- 山の片側に長く伸びる部分(裾)があるか。右に長ければ「少数の大きな値がある」ということです。
棒グラフとの違い:棒グラフは「商品A・商品B」のような別々のカテゴリの比較で、棒の間に隙間があります。ヒストグラムは連続した数値を区切った範囲なので、棒を隙間なく並べます。横軸には『順序と大きさ』があります。
正確な定義:階級・度数・相対度数・累積・密度
用語を表で確定します。
| 用語 | 意味 |
|---|---|
| 階級(bin) | 値を区切った範囲。ここでは [0,2)、[2,4)、… のように書く |
| 階級幅 | 階級の幅 $w$(単位は元のデータと同じ。ここでは千円) |
| 度数 | 階級に入ったデータの件数 $f$ |
| 相対度数 | 度数 ÷ 全件数 $n$。割合(%)で表す |
| 累積相対度数 | 小さい階級から順に相対度数を足した値。「この値以下の割合」を表す |
| 密度 | 相対度数 ÷ 階級幅。単位幅あたりの割合 |
区間の書き方:[a, b) は「$a$ 以上 $b$ 未満」($a$ を含み、$b$ を含まない)の意味です。この約束を左閉右開と呼びます。同じ値が隣り合う2つの階級に入って二重に数えられたり、どちらにも入らず漏れたりしないよう、境界の約束を必ず決めます。
階級が $K$ 個あるとき、$k$ 番目の階級の度数を $f_k$、階級幅を $w_k$、全件数を $n$ とすると、密度は次の式です。
$$d_k=\frac{f_k}{n\,w_k}$$
記号の意味:$f_k$ は件数(単位:件)、$n$ は全件数(件)、$w_k$ は階級幅(千円)。$d_k$ の単位は「1千円あたりの割合」です。たとえば [2,4) は $f=9$、$n=20$、$w=2$ なので $d=9\div(20\times2)=0.225$、つまり1千円あたり22.5%です。棒の面積(高さ×幅)が相対度数になるように高さを決めるのが、密度の意味です。階級幅がすべて等しければ、高さを度数にしても形は同じです。
平均値と中央値を使った形の読み方:右に裾が長い分布では、大きな値が平均を引き上げるため、平均が中央値より大きくなります。左に裾が長ければ逆です。ヒストグラムの形と、平均・中央値の位置関係は対応します。
段階付き例題:20件の度数分布表をつくる
ステップ1:階級を決める。 最小は1.2、最大は18.0。幅2千円、0から始めて [0,2)、[2,4)、…、[18,20) の10階級にします。
ステップ2:度数を数える。 各値を左閉右開の階級に入れます。
- [0,2):1.2, 1.8 → 2件
- [2,4):2.1, 2.4, 2.5, 2.9, 3.0, 3.1, 3.4, 3.6, 3.9 → 9件
- [4,6):4.2, 4.5, 5.1, 5.6 → 4件
- [6,8):6.3, 7.8 → 2件
- [8,10):9.5 → 1件
- [10,12):0件
- [12,14):12.4 → 1件
- [14,16)、[16,18):0件
- [18,20):18.0 → 1件(18.0は左閉なので、[16,18) ではなくここに入る)
合計 2+9+4+2+1+0+1+0+0+1=20件で、全件数と一致します。合計が $n$ に一致するかを必ず確認します。
ステップ3:相対度数と累積を出す。
| 階級 | 度数 | 相対度数 | 累積相対度数 |
|---|---|---|---|
| [0,2) | 2 | 10% | 10% |
| [2,4) | 9 | 45% | 55% |
| [4,6) | 4 | 20% | 75% |
| [6,8) | 2 | 10% | 85% |
| [8,10) | 1 | 5% | 90% |
| [10,12) | 0 | 0% | 90% |
| [12,14) | 1 | 5% | 95% |
| [14,16)〜[16,18) | 0 | 0% | 95% |
| [18,20) | 1 | 5% | 100% |
ステップ4:コードで確かめ、形を読む。 np.histogram(データ, bins=境界のリスト) は、階級ごとの度数と境界を返す関数です。戻り値は(度数の配列、境界の配列)の2つです。
import numpy as np
import matplotlib.pyplot as plt
x = [1.2, 1.8, 2.1, 2.4, 2.5, 2.9, 3.0, 3.1, 3.4, 3.6, 3.9, 4.2, 4.5, 5.1, 5.6, 6.3, 7.8, 9.5, 12.4, 18.0]
edges = list(range(0, 22, 2)) # 0,2,4,...,20
counts, _ = np.histogram(x, bins=edges)
print("度数:", counts.tolist(), " 合計:", counts.sum())
print("平均:", round(np.mean(x), 3), " 中央値:", np.median(x))
plt.hist(x, bins=edges, edgecolor="white")
plt.xlabel("注文金額(千円)")
plt.ylabel("件数")
出力は、度数 [2, 9, 4, 2, 1, 0, 1, 0, 0, 1]、合計20、平均5.165、中央値3.75 です。
ステップ5:結果を読む。 山は [2,4) に1つ(単峰)で、右に長い裾があります。平均5.165は中央値3.75より大きく、右の裾(12.4と18.0)が引き上げています。「典型的な注文は3〜4千円で、まれに大口がある」と説明するほうが、「だいたい5千円」より正確です。
操作して理解する:階級幅を変えると見え方が変わる
同じ20件を、階級幅1・2・5・10で分けてみます。np.arange(0, 20 + w, w) は 0 から 20 まで幅 $w$ で並べた境界を作ります。
import numpy as np
x = [1.2, 1.8, 2.1, 2.4, 2.5, 2.9, 3.0, 3.1, 3.4, 3.6, 3.9, 4.2, 4.5, 5.1, 5.6, 6.3, 7.8, 9.5, 12.4, 18.0]
for w in (1, 2, 5, 10):
edges = np.arange(0, 20 + w, w)
counts, _ = np.histogram(x, bins=edges)
print("幅", w, ":", counts.tolist())
出力は次のとおりです。
| 幅 | 度数 |
|---|---|
| 1 | 0, 2, 4, 5, 2, 2, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 |
| 2 | 2, 9, 4, 2, 1, 0, 1, 0, 0, 1 |
| 5 | 13, 5, 1, 1 |
| 10 | 18, 2 |
- 幅1だと棒が20本になり、1件か2件ごとのでこぼこが目立ちます。細かすぎる例です。
- 幅10だと2本だけで、「大半が10未満」としか言えません。粗すぎる例です。
- 幅2か5が、形を読むのに適しています。
階級幅が不揃いの場合の密度も確かめましょう。階級を [0,2)、[2,4)、[4,10)、[10,20) とします。度数は 2、9、7(4.2, 4.5, 5.1, 5.6, 6.3, 7.8, 9.5)、2(12.4, 18.0)です。
import numpy as np
x = [1.2, 1.8, 2.1, 2.4, 2.5, 2.9, 3.0, 3.1, 3.4, 3.6, 3.9, 4.2, 4.5, 5.1, 5.6, 6.3, 7.8, 9.5, 12.4, 18.0]
edges = [0, 2, 4, 10, 20]
counts, _ = np.histogram(x, bins=edges)
density, _ = np.histogram(x, bins=edges, density=True)
print("度数:", counts.tolist())
print("密度:", [round(float(d), 4) for d in density])
print("面積の合計:", round(sum(d * w for d, w in zip(density, np.diff(edges))), 6))
出力は、度数 [2, 9, 7, 2]、密度 [0.05, 0.225, 0.0583, 0.01]、面積の合計 1.0 です。density=True を付けると、$d_k=f_k/(n\,w_k)$ が自動で計算されます。幅6の階級 [4,10) は度数7でも、密度は 7÷(20×6)≒0.058 で、幅2の階級 [2,4)(0.225)よりはるかに低い、とわかります。
発展:階級数の決め方、累積分布、対数軸
階級幅の決め方には目安があります。
- スタージェスの規則:階級数の目安を $1+\log_2 n$ とする($n=20$ なら約5.3個)。
- フリードマン・ディアコニスの規則:階級幅を $2\times\mathrm{IQR}\times n^{-1/3}$ とする。IQR(四分位範囲)は 代表値と散らばり で学びます。外れ値に強い決め方です。
- どの規則も出発点にすぎません。幅を変えて形が安定して見えるか確認するのが基本です。
ヒストグラムは階級の切り方に左右されます。それを避ける方法として、次があります。
- 経験累積分布関数(ECDF):値を小さい順に並べ、「この値以下の割合」を階段状に描く。階級を使わず、全データの情報を使います。
- カーネル密度推定(KDE):各データ点に小さな山を置いて足し合わせ、なめらかな密度曲線を描く。ただし帯域幅という調整値があり、これも見え方を変えます。
- 対数軸:売上や再生回数のように桁がまたがる右裾のデータは、横軸を対数にすると形が見やすくなります。ただし対数軸のヒストグラムは、元の単位での幅が不揃いになる点に注意します。
ML では、右に裾が長い特徴量に対数変換 $\log(1+x)$ を適用し、分布を左右対称に近づけることがよく行われます(特徴量・前処理・表データ(有料版))。
よくある失敗
失敗1:階級幅が不揃いなのに度数をそのまま棒の高さにする
誤った例:階級 [0,2)、[2,4)、[4,10)、[10,20) の度数 2、9、7、2 をそのまま棒の高さにする。
誤りの理由:[4,10) は幅6で、幅2の階級の3倍の広さがあります。その広い階級に7件入っただけなのに、高さ7の棒は「山」のように見えます。実際の1千円あたりの密度は 0.058 で、[2,4) の 0.225 の約4分の1です。
直し方:棒の高さを密度(度数 ÷ (n×幅))にして、面積が割合を表すようにする。np.histogram(..., density=True) や plt.hist(..., density=True) を使う。階級幅をそろえられるなら、そろえるのが最も簡単です。
失敗2:階級の境界の約束が違い、件数が合わない
同じ4件 [2, 4, 4, 6] を、境界 [0, 2, 4, 6] で数えます。
import numpy as np
import pandas as pd
d = [2, 4, 4, 6]
print("np.histogram :", np.histogram(d, bins=[0, 2, 4, 6])[0].tolist())
print("pd.cut 右閉 :", pd.cut(d, bins=[0, 2, 4, 6]).value_counts().sort_index().tolist())
print("pd.cut 左閉 :", pd.cut(d, bins=[0, 2, 4, 6], right=False).value_counts().sort_index().tolist())
出力は [0, 1, 3]、[1, 2, 1]、[0, 1, 2] です。3つとも違います。
np.histogramは [0,2)、[2,4)、[4,6](最後だけ右端を含む)。pd.cutの初期設定は右閉 (0,2]、(2,4]、(4,6] です。- 左閉にした
right=Falseでは、6がどの階級にも入らず欠落し、合計が3件になります。
直し方:使う道具の境界の約束を確認し、度数の合計が全件数 $n$ に一致するかを毎回検算する。報告には「左閉右開」などの約束を書く。
失敗3:階級を粗くして、2つの山を1つにしてしまう
次の14件は、新規客と常連客が混ざった滞在時間(分)だと仮定します。
import numpy as np
b = [1, 2, 2, 3, 3, 3, 4, 11, 12, 12, 13, 13, 13, 14]
print("幅5 :", np.histogram(b, bins=[0, 5, 10, 15])[0].tolist())
print("幅15:", np.histogram(b, bins=[0, 15])[0].tolist())
print("平均:", round(np.mean(b), 2), " 中央値:", np.median(b))
幅5では [7, 0, 7] と、山が2つで間に谷があります。幅15だと [14] の1本だけで、分布の形は消えます。平均は7.57、中央値は7.5ですが、この値の近く(5〜10分)にはデータが1件もありません。
誤りの理由:平均や中央値が『典型的な値』とは限りません。山が2つあるデータでは、平均は2つの山の間の、データのない場所に来ます。
直し方:階級幅を何通りか試して形を確認する。2つの山があれば、集団を分けて(層別、層別と集計の罠)集計する。
まとめ
- ヒストグラムは、値を階級に分けて度数を棒で描き、分布の形(山の数・裾の向き)を見る道具。
- 階級は左閉右開など境界の約束を決め、度数の合計が全件数に一致するか検算する。
- 階級幅が不揃いなら、高さは密度(度数÷(n×幅))にして、面積が割合を表すようにする。
- 右に裾が長い分布では平均が中央値より大きくなる。二峰の分布では平均が谷に来ることもある。
- 階級幅を何通りか試して、形が安定して見えるか確認する。
用語
- ヒストグラム(histogram)
数値を階級に分け、階級ごとの度数を隙間なく並べた棒で表す図。
- 度数・相対度数(frequency / relative frequency)
階級に入ったデータの件数と、その全体に占める割合。
- 累積相対度数(cumulative relative frequency)
小さい階級から順に相対度数を足した値。『この値より小さい割合』を表す。
- 密度(density)
相対度数を階級幅で割った値。階級幅が違っても面積が割合を表すように高さを決める。
- 裾(tail)
分布の山から離れた端の部分。右に長ければ『右に裾が長い』という。
よくある質問
ヒストグラムと棒グラフはどう違いますか。
棒グラフは別々のカテゴリの比較で、棒の順序に意味がないことが多い。ヒストグラムは連続する数値を区切った範囲の度数で、棒は隙間なく並び、横軸の大小に意味があります。カテゴリ別の売上の棒グラフをヒストグラムと呼ばないようにします。
階級数や階級幅は、どう決めればよいですか。
規則(スタージェス、フリードマン・ディアコニス)は出発点で、実際には幅を何通りか変えて形が安定しているかを見て決めます。報告には、階級幅と境界の約束を書き、再現できるようにします。
相対度数と密度はどう違いますか。
相対度数は階級に入った割合そのものです。密度はそれを階級幅で割った『単位幅あたりの割合』で、幅が違う階級を公平に比べるために使います。幅が同じなら、形は相対度数でも密度でも同じです。
平均と中央値が離れていたら、何を疑えばよいですか。
分布が左右非対称(裾が長い)か、外れ値があるか、複数の集団が混ざっていないかを疑います。ヒストグラムを描き、外れ値かどうかは 外れ値と頑健性 の方法で調べます。
階級値を使った平均の推定は、正確ですか。
元データが手元にないときの近似です。各階級の値が階級の中央に集まっていると仮定しているため、実際の平均とずれます。元データがあれば、元データから平均を計算します。
ヒストグラムは仕事でどう使われますか。
待ち時間や処理時間の分析、特徴量の偏りの確認、品質管理でのばらつきの確認などです。レポートでは、平均とともに『分布の形』『山の数』『裾の長さ』を一言添えると、判断の質が上がります。
確認問題
問1 階級幅が不揃いのヒストグラムで、棒の高さを度数にすると何が問題か。
- ① 問題はない。度数は常に正しい高さである
- ② 広い階級の棒が実際より目立ち、分布の形が歪んで見える
- ③ 全体の件数が増えてしまう
- ④ 平均値が計算できなくなる
解答と解説
正解:②
広い階級には、それだけ多くの値が入りやすくなります。密度(度数÷(n×幅))を高さにして面積が割合を表すようにすれば、形が正しく見えます。
問2 右に裾が長い(少数の大きな値がある)分布について、平均と中央値の関係として最も適切なのはどれか。
- ① 平均は中央値より大きくなりやすい
- ② 平均は中央値より小さくなりやすい
- ③ 平均と中央値は必ず一致する
- ④ 平均と中央値の関係は分布の形と無関係
解答と解説
正解:①
大きな値は平均を引き上げますが、中央値(順位の真ん中)にはあまり影響しません。
問3 [4,6) の階級の定義が「左閉右開」のとき、値 6.0 はどの階級に入るか。
- ① [4,6)
- ② 両方に入る
- ③ [6,8)
- ④ どちらにも入らない
解答と解説
正解:③
[4,6) は『4以上6未満』なので6.0は含みません。6.0は『6以上8未満』の [6,8) に入ります。
問4 度数分布表を作ったら、度数の合計が全件数より1少なかった。最も疑うべき原因はどれか。
- ① 平均の計算ミス
- ② データが多すぎる
- ③ ヒストグラムの色の設定
- ④ 階級の境界の約束により、境界上の値や範囲外の値が数えられていない
解答と解説
正解:④
pd.cut の right=False で最大値が範囲外になる例のように、境界の扱いや階級の範囲で値が欠落することがあります。合計の検算で発見できます。
問5 滞在時間のヒストグラムで、2〜4分と12〜14分に山があり、その間はほぼ空だった。平均は約7.6分だった。この平均の使い方として適切なのはどれか。
- ① 『平均7.6分』を典型的な滞在時間として報告する
- ② 山が2つあることを示し、集団を分けて集計する方針を検討する
- ③ 平均が出たので分布を見る必要はない
- ④ データを1つの山になるまで削除する
解答と解説
正解:②
二峰のデータでは平均は谷(データのほとんどない場所)に来て、典型的な値になりません。集団の違い(新規と常連など)を調べます。
問6 20件のデータを階級幅1、2、10で分けた。形を読み取るのに最も不向きなのはどれか。
- ① 幅2:9件の山とその右の裾が見える
- ② 幅1:1〜2件ごとの凸凹が目立つが、全体の傾向は読み取れる
- ③ 幅10:2本の棒だけで、山の位置や裾の形がほぼ見えない
- ④ どの幅でも情報量は変わらない
解答と解説
正解:③
階級が粗すぎると形の情報が失われます。細かすぎると偶然の凸凹が目立ちます。何通りか試して判断します。
次に学ぶこと
次は 代表値と散らばり で、分布を数で要約する代表値(平均・中央値・最頻値)と散らばり(分散・標準偏差・四分位範囲)を学びます。戻るなら 変数の尺度 で尺度を確認します。
この授業の確認問題だけで習得とはしません。ST01 の総合テストと、別データの自力課題で判定します。外れ値の見つけ方は 外れ値と頑健性、集団を分けて見る方法は 層別と集計の罠 です。
キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。
アプリを無料で開く有料版と無料版の違い