本文へスキップ
からだにいいもの

Rのトピックスを中心に『まだ、まだ、知らない、役に立つ情報?』を発信します。

Rで解析:計数データの過分散に対応する「hermite」パッケージ

発生件数のような計数データを扱うとき、どの分布を当てはめるかが解析の出発点になります。しかし、事象が1件ずつだけでなく複数まとまっても発生する場合、ポアソン分布では分散を説明しきれず、過分散への対処に手間がかかります。

「hermite」パッケージは、一般化エルミート分布にもとづく確率計算と回帰分析を簡単におこなえるパッケージです。確率質量関数、累積分布関数、分位点関数、乱数生成の4つのコマンドが収録されており、分布の形状の確認からシミュレーションまでを一貫して扱えます。

また、説明変数を含む回帰モデルの最尤推定や、ポアソン分布を帰無仮説とする尤度比検定も可能です。本パッケージの利用で、まとまって発生する計数データの特性を反映した解析ができるのではないかと考えます。

パッケージバージョンは1.2.1。Windows 11 x64 (build 26200)のR version 4.6.1で確認しています。

パッケージのインストール

下記コマンドを実行してください。依存パッケージとしてmaxLikが同時にインストールされます。

# パッケージのインストール
install.packages("hermite")

# パッケージの読み込み
library("hermite")
スポンサーリンク

コマンド例

詳細はコメント、パッケージのヘルプを確認してください。

本記事では、架空の動物病院における30日分の1日あたりのペット数を作成し、記事全体を通して使用します。病院には1頭で来院するケースと、多頭飼いのため2頭同時に来院するケースが混在していることを考えました。1件ずつの事象と2件まとまった事象が重なる構造は、次数m = 2の一般化エルミート分布そのものです。

# 架空の動物病院における30日分の1日あたり来院ペット数を作成
raiin <- c(1, 3, 0, 7, 2, 9, 4, 0, 5, 11,
           2, 1, 6, 13, 3, 0, 8, 2, 5, 15,
           4, 1, 7, 0, 3, 10, 6, 2, 9, 4)

# 各日が休診日の翌日かどうかを示すフラグを作成
yokujitsu <- c(0, 0, 0, 1, 0, 1, 0, 0, 0, 1,
               0, 0, 1, 1, 0, 0, 1, 0, 0, 1,
               0, 0, 1, 0, 0, 1, 1, 0, 1, 0)

# 平均と分散を比較して過分散の有無を確認
c(平均 = mean(raiin), 分散 = var(raiin), 分散指標 = var(raiin) / mean(raiin))
    平均      分散  分散指標 
4.766667 16.322989  3.424403 

分散が平均の約3.4倍となっており、ポアソン分布が前提とする「分散=平均」から大きく外れています。以降、このデータに一般化エルミート分布を当てはめます。

一般化エルミート分布の確率質量関数を計算:dhermiteコマンド

オプション意味初期値
x確率を求める非負整数のベクトルなし
aエルミート分布の第一パラメーターなし
bエルミート分布の第二パラメーターなし
m一般化エルミート分布の次数、2が標準的なエルミート分布に対応2

指定した値がちょうど発生する確率を返します。a、b、mのいずれかが負の値、またはmが2未満や非整数の場合は警告とともにNaNが返ります。

# 単独来院が平均2件、ペア来院が平均1.4件のときの確率質量関数を計算
dhermite(0:8, a = 2, b = 1.4, m = 2)
[1] 0.03337327 0.06674654 0.11346912 0.13794285 0.14839981 0.13660792 0.11478922 0.08744009 0.06203625

# 3頭ちょうど来院する確率のみを取得
dhermite(3, a = 2, b = 1.4, m = 2)
[1] 0.1379428

mを変更すると、まとまって発生する件数が変わります。m = 3では3件単位の来院を仮定した分布になります。

# 3頭まとまって来院すると仮定した場合の確率質量関数を計算
round(dhermite(0:8, a = 2, b = 1.4, m = 3), 4)
[1] 0.0334 0.0667 0.0667 0.0912 0.1157 0.1023 0.0980 0.0974 0.0781

一般化エルミート分布の累積分布関数を計算:phermiteコマンド

オプション意味初期値
q累積確率を求める非負整数のベクトルなし
aエルミート分布の第一パラメーターなし
bエルミート分布の第二パラメーターなし
m一般化エルミート分布の次数、2が標準的なエルミート分布に対応2
lower.tailTRUEでq以下となる確率、FALSEでqを超える確率を返すTRUE
# 来院数が0頭から8頭以下となる累積確率を計算
round(phermite(0:8, a = 2, b = 1.4, m = 2), 4)
[1] 0.0334 0.1001 0.2136 0.3515 0.4999 0.6365 0.7513 0.8388 0.9008

# 8頭を超える確率を計算
phermite(8, a = 2, b = 1.4, m = 2, lower.tail = FALSE)
[1] 0.09919495

指定した確率に対応する分位点を計算:qhermiteコマンド

オプション意味初期値
p分位点を求める確率のベクトルなし
aエルミート分布の第一パラメーターなし
bエルミート分布の第二パラメーターなし
m一般化エルミート分布の次数、2が標準的なエルミート分布に対応2
lower.tailTRUEで累積確率がp以上となる最小の整数、FALSEで1-pに対応する値を返すTRUE

累積確率が指定した値以上となる最小の整数を返します。準備すべき診察枠の見積もりなどに利用できます。

# 中央値から99パーセント点までの分位点を計算
qhermite(c(0.5, 0.8, 0.9, 0.95, 0.99), a = 2, b = 1.4, m = 2)
[1]  5  7  8 10 12

# 上側5パーセント点を計算
qhermite(0.05, a = 2, b = 1.4, m = 2, lower.tail = FALSE)
[1] 10

一般化エルミート分布に従う乱数を生成:rhermiteコマンド

オプション意味初期値
n生成する観測数なし
aエルミート分布の第一パラメーターなし
bエルミート分布の第二パラメーターなし
m一般化エルミート分布の次数、2が標準的なエルミート分布に対応2

内部では平均aのポアソン乱数と、平均bのポアソン乱数をm倍したものの和が返されます。

# 乱数の再現性を確保するためにシード値を固定
set.seed(1234)

# 300日分の来院数を生成
rnd <- rhermite(300, a = 2, b = 1.4, m = 2)

# 先頭20件を確認
head(rnd, 20)
[1]  2 10  4  4 12  4  0 11  4  6  9  6  3  6  1  3  1  3  3  7

# 理論値の平均4.8および分散7.6と比較
c(平均 = mean(rnd), 分散 = var(rnd))
    平均     分散 
5.103333 8.447480

エルミート回帰モデルの最尤推定:glm.hermiteコマンド

オプション意味初期値
formula目的変数と説明変数を記述したモデル式なし
dataモデルに含まれる変数を格納したデータフレームなし
linkリンク関数の指定、”log”または”identity”“log”
startパラメーターの初期値ベクトルNULL
m次数mの値、NULLの場合は関数内で推定NULL

戻り値はcoefs(係数)、loglik(対数尤度)、vcov(分散共分散行列)、hess(ヘッセ行列)、fitted.values(当てはめ値)、w(尤度比検定統計量)、pval(そのp値)を含むリストです。wとpvalは、ポアソン分布を帰無仮説とする尤度比検定の結果です。

glm.hermiteコマンド実行時に

警告メッセージ:
model.matrix.default(mt, mf, contrasts) で:
non-list contrasts argument ignored

が表示されますが問題ありません。

# 説明変数を含まないモデルで来院数の分布を推定
mod0 <- glm.hermite(raiin ~ 1, link = "log", m = 2)

# 切片、分散指標、次数の推定値を確認
mod0$coefs
(Intercept) dispersion.index            order 
1.561707         1.811578         2.000000 

# ポアソン分布に対する尤度比検定の結果を確認
c(検定統計量 = mod0$w, p値 = mod0$pval)
  検定統計量          p値 
2.805052e+01 5.909459e-08 

分散指標が1.81と1を上回り、p値も十分に小さいため、ポアソン分布よりも一般化エルミート分布のほうが当てはまりがよいと判断できます。なお、本コマンドは実行のたびに「non-list contrasts argument ignored」という警告を表示しますが、パッケージ内部の処理によるもので推定結果には影響しません。

説明変数を加える場合は、dataオプションにデータフレームを渡します。結果はsummaryコマンドで整形して確認できます。

# 来院数と休診日翌日フラグをデータフレームにまとめる
byoin <- data.frame(raiin = raiin, yokujitsu = yokujitsu)

# 休診日翌日フラグを説明変数としてエルミート回帰を実行
mod1 <- glm.hermite(raiin ~ yokujitsu, data = byoin, link = "log", m = 2)
警告メッセージ:
  model.matrix.default(mt, mf, contrasts) で:
  non-list contrasts argument ignored

# 推定結果を整形して表示
summary(mod1)
Call:
  glm.hermite(formula = raiin ~ yokujitsu, data = byoin, link = "log", 
              m = 2)

Deviance Residuals:
  Min        1Q    Median        3Q       Max 
-8.201381 -8.201381 -2.199341 -1.199341 -1.199341 

Coefficients:
  Estimate Std. Error   z value      p-value
(Intercept)      0.7881579  0.1677142 4.6994103 2.609138e-06
yokujitsu        1.4311958  0.1994811 7.1745942 7.252198e-13
dispersion.index 1.1717825  0.4338111 0.1815149 3.350373e-01
order            2.0000000         NA        NA           NA
(Likelihood ratio test against Poisson is reported by *z value* for *dispersion.index*)

AIC:  130.7737 

休診日翌日の係数が1.43と正の値になっており、対数リンクのもとで来院数が約4.2倍に増えると読み取れます。

実行例

パッケージの機能を組み合わせた、実践的な使い方の例です。

推定結果からパラメーターを復元して実測度数と比較する

glm.hermiteが返すのは平均の対数と分散指標であり、dhermiteが必要とするaとbではありません。平均をμ、分散指標をdとすると、m = 2のときb = μ(d – 1) / 2、a = μ – 2bで復元できます。復元したパラメーターを使い、実測の度数分布、一般化エルミート分布、ポアソン分布の3者を並べて比較します。

# 推定された切片を指数変換して平均を取得
heikin <- exp(unname(mod0$coefs[1]))

# 推定された分散指標を取得
shisu <- unname(mod0$coefs[2])

# 分散指標と平均から第二パラメーターbを復元
b_hat <- heikin * (shisu - 1) / 2

# 平均とbから第一パラメーターaを復元
a_hat <- heikin - 2 * b_hat

# 復元したパラメーターを小数第4位に丸めて確認
round(c(a = a_hat, b = b_hat), 4)
     a      b 
0.8982 1.9344 

# 実測度数と2つの理論分布を並べた比較表を作成
hikaku <- data.frame(
  来院数 = 0:10,
  実測割合 = as.numeric(table(factor(raiin, levels = 0:10))) / length(raiin),
  エルミート = dhermite(0:10, a = a_hat, b = b_hat, m = 2),
  ポアソン = dpois(0:10, lambda = heikin)
)

# 小数第4位に丸めて表示
round(hikaku, 4)
    来院数 実測割合 エルミート ポアソン
1       0   0.1333     0.0589   0.0085
2       1   0.1000     0.0529   0.0405
3       2   0.1333     0.1376   0.0966
4       3   0.1000     0.1094   0.1536
5       4   0.1000     0.1576   0.1830
6       5   0.0667     0.1130   0.1745
7       6   0.0667     0.1186   0.1386
8       7   0.0667     0.0776   0.0944
9       8   0.0333     0.0661   0.0563
10      9   0.0667     0.0400   0.0298
11     10   0.0333     0.0291   0.0142

0頭の日と9頭以上の日のいずれについても、ポアソン分布は確率を過小評価しています。一般化エルミート分布は両端の確率を高く見積もっており、実測の裾の重さをよく捉えています。

分位点から必要な診察枠を見積もる

復元したパラメーターをqhermiteに渡すと、指定した割合の日をカバーできる来院数がわかります。診察枠や在庫の準備数を決める場面で利用できます。

# カバーしたい日の割合を設定
wariai <- c(0.5, 0.8, 0.9, 0.95, 0.99)

# 各割合に対応する来院数を計算
waku <- qhermite(wariai, a = a_hat, b = b_hat, m = 2)

# 割合と必要な診察枠を対応させた表を作成
data.frame(カバー率 = wariai, 必要枠 = waku)
   カバー率 必要枠
1     0.50      4
2     0.80      7
3     0.90      9
4     0.95     10
5     0.99     13

# 12頭を超える日が発生する確率を計算
round(phermite(12, a = a_hat, b = b_hat, m = 2, lower.tail = FALSE), 4)
[1] 0.0123

95パーセントの日をカバーするには10頭分、99パーセントでは13頭分の枠が必要という見積もりが得られます。

乱数生成によるシミュレーションで見積もりを検証する

rhermiteで大量の仮想データを生成し、分位点から得た見積もりが妥当かを確認します。

# 乱数の再現性を確保するためにシード値を固定
set.seed(20260804)

# 復元したパラメーターで10000日分の来院数を生成
sim <- rhermite(10000, a = a_hat, b = b_hat, m = 2)

# 生成データの平均と分散を確認
c(平均 = mean(sim), 分散 = var(sim))
   平均     分散 
4.760300 8.886933

# 12頭を超える日の実際の割合を確認
mean(sim > 12)
[1] 0.0132

シミュレーションによる平均4.76は、実測の平均4.77とほぼ一致しています。12頭を超える日の割合は約1.3パーセントで、phermiteから求めた理論値の約1.2パーセントとよく対応しています。


この記事が誰かの役に立ちますように。

スポンサーリンク
価格および配送状況は変更される場合があります。購入時は商品ページをご確認ください。
当サイトに表示されている商品情報はAmazonから提供されたものであり、更新または削除される場合があります。
karada-goodはAmazonアソシエイトとして、適格販売により収入を得ています。