LoginSignup
4
6

More than 3 years have passed since last update.

多峰性をSilvermanの検定で確かめる

Last updated at Posted at 2019-12-07

多峰性とは

ヒストグラムを作ったときに、よく山が複数できることがあります。これを多峰性(multimodal)と呼びます。
以下に、3つの正規分布を結合させて3つの山を持つデータをつくってみます。

# 正規分布を3つ結合させる。
x <- c(rnorm(100, mean = 4, sd = 1),
       rnorm(100, mean = 8, sd = 1),
       rnorm(100, mean = 12, sd = 1))

# 階級幅1でヒストグラムを描く。
hist(x, breaks=seq(0,16,1))

hist1.png

実際のデータでこの多峰性が現れた場合、データに複数の集団が含まれている可能性があります。しかし、ヒストグラムは階級幅の大きさによって形が変わってしまうため、本当の山(mode)の数はわかりません。例えば、先ほどのデータに対して階級幅を変えてヒストグラムを作ってみると、山の数が2個に見えるようになります。

# 階級幅2.1でヒストグラムを描く。
hist(x, breaks=seq(0,16,2.1))

hist2.png
この山の数を統計的な検定で決定したいと調べてみたところ、以下の資料に出会いました。

ノンパラメトリックな多峰性検定-Silvermanの検定-とその古生物学への導入

資料では、多峰性検定の一つであるSilvermanの検定をとてもわかりやすく説明しており、とても勉強になりました。Rのパッケージも紹介していたのですが、見つけることができなかったので、以下のパッケージを試してみました。

jenzoprさんのsilvermantestパッケージ(GitHub)

silvermantestパッケージの紹介

まず、導入してみます。

library(silvermantest)

silverman.testで検定を行うことができます。mode数が1のときは、キャリブレーションが必要となるので、adjust=TRUEとします。以下に、先ほどの3つの正規分布の結合データを検定してみました。


# mode数1で検定
silverman.test(x, 1, adjust = TRUE)

# mode数2で検定
silverman.test(x, 2)

# mode数3で検定
silverman.test(x, 3)

結果は以下のとおりです。mode数3でp値が0.05を超えました。mode数を3と考えて良さそうです。

Silvermantest: Testing the null hypothesis that the number of modes is <=  1 
The resulting p-value is  -4.5e-05 

Silvermantest: Testing the null hypothesis that the number of modes is <=  2 
The resulting p-value is  0.001001001 

Silvermantest: Testing the null hypothesis that the number of modes is <=  3 
The resulting p-value is  0.9479479 

Silvermantest: Testing the null hypothesis that the number of modes is <=  4 
The resulting p-value is  0.7767768 

上記の操作をまとめて行えるのが、silverman.plotです。mode数ごとに検定を行って、p値をグラフにしてくれます。グラフの赤い線が0.05のラインです。p値が0.05を初めて超えたmode数を適切と考えてよいようです。

silverman.plot(x)

plot1.png

さらにdensities.plotで各mode数でのカーネル密度推定量のグラフを描くことができます。赤い線の部分が極大値を表しています。

densities.plot(x)

plot2.png

densities.plotのコードを加工して、ヒストグラムとカーネル密度推定量を重ねる関数を作ってみました。

# ヒストグラムとカーネル密度推定量を重ねる関数
hist_density = function(x, mode){

  # 極大値のインデックスを返す関数
  mode_ind = function(x, b) {
    y <- density(x, bw = b)$y
    d1 <- diff(y)
    signs <- diff(d1 / abs(d1))
    ind <- which(signs == -2)
    return(ind)
  }

  # 臨界バンド幅でのカーネル密度推定を行う。
  h0 <- h.crit(x, mode)
  d <- density(x, bw = h0)

  # グラフのx軸y軸の幅をを求める。
  y_max <- max(d$y)
  x_max <- max(abs(d$x))
  x_min <- min(abs(d$x))

  # カーネル密度推定量をプロット
  plot(d,
       main = paste("modes:", mode),
       xlim = c(x_min, x_max),
       ylim = c(0, y_max+0.1)
  )

  # 極大値のインデックスを求める。
  ind <- mode_ind(x, h0)

  x_values <- d$x
  y_values <- d$y

  # 極大値を赤くプロット。
  for (j in 1:mode){
    lines(c(x_values[ind[j]], x_values[ind[j]]),
          c(y_values[ind[j]] - 0.025, y_values[ind[j]] + 0.025),
          col = "red"
    )
  }

  # グラフを上書き。
  par(new=T)

  # ヒストグラムを描く
  hist(x,
       xlim = c(x_min, x_max),
       ylim = c(0, y_max+0.1),
       prob=T,
       ann=F)  

}

mode数3で実行してみます。

hist_density(x,3)

hist3.png

Rの多峰性に関するパッケージ

 今回はSilvermanの検定とパッケージについて紹介しましたが、他にも多峰性に関する検定はたくさんあります。また、CRANには多峰性に関するパッケージであるmultimodeがあり、多くの検定だけでなく、mode tree や mode forest 、SiZer map などを扱うことができます。

4
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6