【R】度数分布表とヒストグラムを作る

本記事のリンクには広告が含まれています。

本記事は、統計学のテキストの内容をRでコード実装するシリーズです。

この記事で扱うデータおよび度数分布表の考え方は、小島寛之著『完全独習 統計学入門』(ダイヤモンド社)を参考にしています。

今回は、上記テキストで示されている度数分布表とヒストグラムの作成手順を、Rで実装することを目的に整理しました。

目次

実行環境

実行環境は以下の通りです。

項目バージョン
OSWindows 11 Home 64bit (バージョン 24H2)
R4.5.1
RStudio2025.09.0

参考文献

参考図書はこちらです。

\楽天ポイント4倍セール!/
楽天市場
\商品券4%還元!/
Yahooショッピング
山田剛史, 杉澤武俊, 村井潤一郎
\楽天ポイント4倍セール!/
楽天市場
\商品券4%還元!/
Yahooショッピング

また、以下の記事も参考にさせていただきました。

データの準備

本書 P.16に掲載されている身長データ(図表1-1)を使用します。

# 身長データ(単位:cm)
height_cm <- c(
  151, 154, 158, 162, 
  154, 152, 151, 167, 
  160, 161, 155, 159, 
  160, 160, 155, 153, 
  163, 160, 165, 146, 
  156, 153, 165, 156, 
  158, 155, 154, 160, 
  156, 163, 148, 151, 
  154, 160, 169, 151, 
  160, 159, 158, 157, 
  154, 164, 146, 151, 
  162, 158, 166, 156, 
  156, 150, 161, 166, 
  162, 155, 143, 159, 
  157, 157, 156, 157, 
  162, 161, 156, 156, 
  162, 168, 149, 159, 
  169, 162, 162, 156, 
  150, 153, 159, 156, 
  162, 154, 164, 161
)

データ数nを確認します。

# データ数の確認
n <- length(height_cm)
n
## [1] 80

length() 関数は、ベクトルに含まれる要素の個数を返します。ここでは 80 人分の身長データを扱っていることが分かります。

度数分布表の作成

度数分布表を作成する流れは以下の通りです。

  1. データの範囲(最小値・最大値)の確認
  2. 階級幅と境界の設定
  3. 階級ラベルの作成
  4. 階級値の決定
  5. 階級の割り当て
  6. 度数の集計
  7. 度数分布表として整形
  8. 相対度数・累積度数の追加

1. データの範囲(最小値・最大値)の確認

まずは、データの範囲を把握します。

# 最小値・最大値の確認
min_height_cm <- min(height_cm)
max_height_cm <- max(height_cm)

min_height_cm
max_height_cm
## [1] 143
## [1] 169

min() は最小値、max() は最大値を返す関数です。今回のデータでは、最小値 143 cm、最大値 169 cmでした。

2. 階級幅と境界の設定

階級(class)の対象範囲は、最小値から最大値まで包含するように、140から170と設定することにします。

階級幅(class interval)は、分析の目的によって決め方が変わります。今回は学習用として、身長データとして直感的に理解しやすい 5 cm 刻みを採用します。

# 階級の対象範囲
start_val <- 140 #開始値(最小値より少し小さいキリの良い数)
end_val <- 170 #終了値(最大値より少し大きいキリの良い数)

# 階級幅の設定
class_interval <- 5

次に、階級の境界となる数値(breaks)を設定します。

# 整数データを整数区間に割り当てやすくするためのずらし
offset <- 0.5

# 階級境界を作成
class_breaks <- seq(
  from = start_val + offset,
  to   = end_val + offset,
  by   = class_interval
)

class_breaks
## [1] 140.5 145.5 150.5 155.5 160.5 165.5 170.5

なぜ0.5ずらすのか?

度数分布表を「141–145」「146–150」のような 整数の区間で作りたい場合、境界を「150」ジャストに設定してしまうと、「150という値は下の階級に入るのか?上の階級に入るのか?」という曖昧さが生じます。

境界を 150.5 のように設定すれば、以下のように迷うことなく自動的に振り分けることができます(連続性の補正)。

  • 150 は 150.5 より小さい → 下の階級
  • 151 は 150.5 より大きい → 上の階級

今回は整数データなので、ずらさなくても正しい階級に割り当てられますが、整数区間ラベルを作るために0.5ずらしています

seq() 関数とは

seq() 関数は 、一定の間隔で並んだ数値の列を作る関数です。

  • from:開始値
  • to:終了値
  • by:増分

今回のコードは、「140.5 から 170.5 までを 5 ずつ増やしながら並べる」という指示になります。

3. 階級ラベルの作成

class_breaks をもとに階級ラベルを作成します。

コードを書く前に、まずは「最終的にどのような階級を作りたいのか」を整理しておくと、実装が分かりやすくなります。今回作成する階級ラベルは、以下の通りです。

  • 141–145
  • 146–150
  • 151–155
  • 156–160
  • 161–165
  • 166–170
# 下端の計算
lower_bounds <- floor(class_breaks[-length(class_breaks)]) + 1

# 上端の計算
upper_bounds <- floor(class_breaks[-1])

# 文字列として結合
class_labels <- paste(
  lower_bounds,
  upper_bounds,
  sep = "-"
)

class_labels
## [1] "141-145" "146-150" "151-155" "156-160" "161-165" "166-170"

floor()関数は、小数点以下を切り捨てる関数です。

class_breaks[-1] は、「1 番目の要素を除いた残りすべて」を意味します。同様に、class_breaks[-length(class_breaks)] は、「最後の要素を除いた残りすべて」を意味します。

R ではインデックス指定において、

  • 正の数:その位置の要素を取り出す
  • 負の数:その位置の要素を除外する

というルールがあるため、このように書くことができます。

paste() 関数 は、複数の値を文字列として結合する関数です。sep = "-" を指定することで、下端値と上端値の間をハイフンで結んだ階級ラベルを作成しています。

4. 階級値の決定

階級値(class mark)は、各階級の代表値として用いる「区間の中央の値」のことです。一般的に、以下のように求められます。

=+2階級値=\frac{下端+上端}{2}
# 階級値の決定
class_marks  <- (lower_bounds + upper_bounds) / 2

class_marks
## [1] 143 148 153 158 163 168

5. 階級の割り当て

次に、各データがどの階級に属するかを割り当てます。

# 身長データを階級ごとに分類
height_class <- cut(
  height_cm,             # 対象データ
  breaks = class_breaks, # さっき作った境界(140.5, 145.5...)
  labels = class_labels, # 階級ラベル(141-145...)
  include.lowest = TRUE # 最初の区間だけ下端も含める
  right  = TRUE,          # (a, b] つまり右側を含む設定(今回は.5なので厳密には不要だが明示)
)

# 先頭データを確認
head(height_class)
## [1] 151-155 151-155 156-160 161-165 151-155 151-155
## Levels: 141-145 146-150 151-155 156-160 161-165 166-170

cut() 関数は、連続した数値データを指定した区間に分割し、各値がどの区間に属するかを示す因子(factor)として返します。cut()関数 の戻り値は数値ではなく「区間ラベル(カテゴリ)」であることが重要です。

right = TRUE は区間を (a, b](右端を含む)として扱う指定です。今回は境界が 145.5 のような小数で、データは整数のため境界値に一致しにくく、結果が変わらないことが多いですが、階級の定義として重要なので明示しています。
include.lowest = TRUEは最初の区間だけ下端も含める指定で、最小値が境界に一致したときの取りこぼしを防ぎます。

6. 度数の集計

階級ごとの度数(Frequency)を数えます。

# 各階級の度数を数える
class_freq <- table(height_class)

class_freq
sum(class_freq) # 度数の合計
## 141-145 146-150 151-155 156-160 161-165 166-170 
##       1       6      19      30      18       6 

## [1] 80

table() 関数は、同じ値の出現回数を集計する関数です。

度数の合計が、データ数(length(height_cm))と一致していれば、全データが必ずどこかの階級に割り当てられていることが確認できます。

7. 度数分布表として整形

table() の戻り値は名前付きベクトルです。これは簡単な確認には便利ですが、後続の処理(他の列との結合、可視化、集計など)を考えると、データフレーム形式の方が扱いやすいため、ここで変換しておきます。

# データフレームに変換
freq_df <- data.frame(
  class = class_labels,            # 階級
  class_marks = class_marks,       # 階級値
  freq = as.integer(class_freq)    # 度数
)

freq_df
##     class class_marks freq
## 1 141-145         143    1
## 2 146-150         148    6
## 3 151-155         153   19
## 4 156-160         158   30
## 5 161-165         163   18
## 6 166-170         168    6

table() 関数が返す値は、見た目は数値に見えますが、内部的には特殊な属性を持つオブジェクトです。そのため、as.integer(class_freq)によって明示的に整数型に変換しています。

8. 相対度数・累積度数の計算

  • 相対度数 (relative freqquency):各階級の度数が、全体に占める割合(度数 / 全体データ数)
  • 累積度数 (cumulative frequency):その階級以下に含まれる度数を、上から順に合計したもの
# 相対度数と累積度数の計算
relative_freq   <- freq_df$freq / sum(freq_df$freq)
cumulative_freq <- cumsum(freq_df$freq)

relative_freq
cumulative_freq
sum(relative_freq) #相対度数の合計
## [1] 0.0125 0.0750 0.2375 0.3750 0.2250 0.0750
## [1]  1  7 26 56 74 80
## [1] 1

cumsum()累積和(cumulative sum) を計算する関数です。第1階級から順に足し上げることで累積度数になります。

相対度数の合計は、理論上 1 になります。多少のズレが出る場合は、丸め誤差によるものです。

最後に、計算結果を度数分布表に追加します。

# 度数分布表に追加
freq_df$relative_freq <- relative_freq
freq_df$cumulative_freq <- cumulative_freq

freq_df
##     class class_marks freq relative_freq cumulative_freq
## 1 141-145         143    1        0.0125               1
## 2 146-150         148    6        0.0750               7
## 3 151-155         153   19        0.2375              26
## 4 156-160         158   30        0.3750              56
## 5 161-165         163   18        0.2250              74
## 6 166-170         168    6        0.0750              80

これで、度数分布表が完成しました。

ヒストグラムの作成

度数分布表で使用したclass_breaksをそのままhist()関数に渡すことで、表と整合性の取れたグラフを描画します。

# ヒストグラムの作成
hist(
  height_cm,
  breaks = class_breaks,      # 作成した境界を使用
  include.lowest = TRUE,
  right  = TRUE,              # cut()の挙動に合わせる((a, b]区間)
  xaxt = "n"                 # デフォルトのx軸を消す
)

axis(
  side = 1,
  at = class_marks,           # 目盛の位置(棒の中心)
  labels = class_marks        # 表示ラベル
)

hist() 関数は、内部で階級境界(breaks)を解釈し、各階級の度数を数え上げたうえで描画します。

hist() の戻り値($counts, $mids, $breaks)を確認すると、度数分布表で計算した度数や階級値と対応していることが分かります。

身長データのヒストグラム

練習問題

本書 P.23に掲載されている体重データ(図表1-4)から、上記と同じ方法で度数分布表とヒストグラムを作成してみます。

# 体重データ(単位:kg)
weight_kg <- c(
  48, 54, 47, 50, 53, 43, 45, 43, 
  44, 47, 58, 46, 46, 63, 49, 50, 
  48, 43, 46, 45, 50, 53, 51, 58, 
  52, 53, 47, 49, 45, 42, 51, 49, 
  58, 54, 45, 53, 50, 69, 44, 50, 
  58, 64, 40, 57, 51, 69, 58, 47, 
  62, 47, 40, 60, 48, 47, 53, 47, 
  52, 61, 55, 55, 48, 48, 46, 52, 
  45, 38, 62, 47, 55, 50, 46, 47, 
  55, 48, 50, 50, 54, 55, 48, 50
)

length(weight_kg)
## [1] 80

結果は、以下のようになりました。

## 体重データの度数分布表
##   class class_marks freq relative_freq cumulative_freq
## 1 36-40          38    3        0.0375               3
## 2 41-45          43   11        0.1375              14
## 3 46-50          48   33        0.4125              47
## 4 51-55          53   19        0.2375              66
## 5 56-60          58    7        0.0875              73
## 6 61-65          63    5        0.0625              78
## 7 66-70          68    2        0.0250              80
体重データのヒストグラム

まとめ

今回ご紹介したRで度数分布表を作成する手順をまとめると、以下のようになります。

  1. データの確認min()max()で最小・最大値を確認し、階級の対象範囲を決める
  2. 階級境界の設定: seq()関数で区切り(境界値)を作る
  3. ラベルと階級値の作成paste()で「141-145」といった表示用ラベルを作り、各区間の中央値を階級値として算出する
  4. データの分類cut()関数を使い、各データを設定した階級に振り分ける
  5. 度数の集計table()関数で各階級のデータ数を数え上げる
  6. 表の整形と列の追加data.frame()で表形式にまとめ、相対度数累積度数を追加する

1から度数分布表を作成するのと比べて、ヒストグラムは比較的簡単に作成できるのが意外でした。

ただ、度数分布表の作成の過程でhist()関数の処理の流れが分かり、理解が深まりました。

よかったらシェアお願いします!
  • URLをコピーしました!
  • URLをコピーしました!
目次