t_kahi’s blog

KNIMEやCellProfiler、創薬に関する記事と,日々のメモです

【KNIME】KNIMEでBootstrap:母集団の統計量(中央値)のばらつきを考える

こんばんは,@PKです.

先日KNIMEで使用できるサンプリングノードに関する紹介をしました.

www.t-kahi.com

今日はこの中でも,「Bootstrap sampling」を利用したBootstrap法を使って,母集団の統計量(今回は中央値)のばらつきを出すKNIME Worklowを紹介します.

Bootstrap法について

前回ブートストラップサンプリングというのは入力されたデータから重複を許してデータを抽出する手法ということを紹介しました.

Bootstrap法とは,このサンプリング手法を利用した分析手法です.

データサイエンスの分野では、1つの標本から復元抽出を繰り返して大量の標本を生成し、それらの標本から推定値 を計算し、母集団の性質やモデルの推測の誤差などを分析する方法をブートストラップ法と呼ぶ。   Rとブートストラップ

例えば,Bootstrap法はRandom Forestという機械学習手法で利用されています.
Random Forest による分類
アンサンブル学習の勉強をした話(雑記?) - Data Science by R and Python
(Random Forestノードもあるので改めてどこかで紹介します…!)

Bootstrap法については多くの記事があるので,以下の記事を読んで勉強しました.
ブートストラップ法の直感的理解 - 講義のページへようこそ
Bayesian Bootstrapを試してみる - Deep Karmaning
Rとブートストラップ
An Introduction to the Bootstrap Method – Towards Data Science
特にこの記事を参考にしてKNIME Workflowを作成しました.
色んな統計量のバラツキを求めたい!再標本化 (Bootstrap)のススメ - Qiita

Bootstrap法を使うと,サンプリングしたデータを何度もリサンプリングして統計値を取得することができます.この操作を非常に多く繰り返すことで,様々な統計量(平均値,中央値,etc)のばらつきを知ることができます.

KNIME Workflowの概要

今回ご紹介するKNIME Workflowを以下に示します.
f:id:t_kahi:20190703231300p:plain
今回は,先ほどご紹介したように,Bootstrap法を使ってサンプルデータの統計量(中央値)のばらつきを考えてみます.
まずサンプルデータを作成した後に,サンプルデータの中央値のばらつきを計算するために,「Bootstrap Sampling」と「R Snippet」のそれぞれを使ったブートストラップ法をご紹介します.

最後にこのWorkflow全体の流れについて動画で紹介しています.

サンプルデータの作成

まず「R Source」でサンプルデータを作成します.
適当に作成したサンプルデータから何点かサンプリングを行います.

#R Source
library(ggplot2)

x <- data.frame(round(rgamma(10000,shape = 2,rate = 0.2),1))
names(x) <- "data"
knime.out <- x

median_x <- median(x$data)
ggplot(x, aes(x =data)) +
  geom_histogram()+
  annotate("text", x=40, y=200, label="Median=") +
  annotate("text", x=47, y=200, label=median_x)

f:id:t_kahi:20190703231640p:plain 出力結果を上図に示しています. 作成したガンマ分布(shape=2,rate=0.2)がプロットされています. こちらから少しサンプリングしたデータを使います.
「Row Sampling」で100行だけ抽出し,その結果を「R View」で可視化します.

# R View
library(ggplot2)
x <- median(knime.in$data)
ggplot(knime.in, aes(x =data)) +
  geom_histogram()+
  annotate("text", x=30, y=5, label="Median=") +
  annotate("text", x=33, y=5, label=x)

f:id:t_kahi:20190703232029p:plain
上図のように分布したデータから,ブートストラップ法で中央値のデータをサンプリングしていきます.

今回は二種類のノードを使ってそれぞれでブートストラップ法を行います.

「Bootstrap Sampling」を使ってブートストラップ

まずは前回ご紹介した「Bootstrap Sampling」を使って,データを取得していきます. こちらのWorkflowの概要は以下のようになっております.
f:id:t_kahi:20190703234034p:plain

ブートストラップ法では何度もサンプリングを行うので「Counting Loop Start」でn=1000で設定しておきます.
その後,「Bootstrap Sampling」で"Sample size in % = 100"と設定し,実行します.
サンプリングしたデータの中央値を「Math Formula」で取得して「Group By」でまとめた結果を以下に示しています.
f:id:t_kahi:20190704000513p:plain

中央値を取得することができたので,あとは1000回ループを繰り返して中央値のデータを1000個取得します.
f:id:t_kahi:20190704082433p:plain

ブートストラップ法を使って大量にデータを取得することで,取得した統計量(中央値)の分布は母集団の統計量の分布と一致しているだろう,と考えることができます.
得られた結果を「R View 」で可視化し,標準偏差を求めます.

library(ggplot2)
x <- mean(knime.in$column_median)
x <- round(x, digits =3)
y <- sd(knime.in$column_median)
y <- round(y, digits =3)

ggplot(knime.in, aes(x =column_median)) +
  geom_histogram()+
  annotate("text", x=12, y=100, label="Mean=") +
  annotate("text", x=12.5, y=100, label=x)+
  annotate("text", x=12, y=90, label="SD=   ") +
  annotate("text", x=12.5, y=90, label=y)

f:id:t_kahi:20190704085823p:plain

得られた中央値は平均が8.872,標準偏差は0.671と取得することができました.
この操作によって,単純に統計量としての中央値を求めただけでなく,求めた統計量がどの程度信頼できるのか(つまり母集団のばらつき)を求めることができました.

「R Snippet」を使ってブートストラップ

KNIMEの専用ノードだけでできるのはわかりましたが,ループをたくさん回すと時間もかかるのでRを使ってみます.
ここでは,「R Snippet」でブートストラップを行い,データを「R View」で可視化します.
「R Snippet」では"simpleboot"パッケージを使用しました.
n=10000でリサンプリングをします.

# R Snippet
library(simpleboot)
data.b.mean <- one.boot(knime.in$data, median, 10000)
knime.out <- data.frame(data.b.mean$t)

後は「R VIew」で可視化します.

# R View
library(ggplot2)

x <- mean(knime.in$data.b.mean.t)
x <- round(x, digits =3)
y <- sd(knime.in$data.b.mean.t)
y <- round(y, digits =3)

ggplot(knime.in, aes(x =data.b.mean.t)) +
  geom_histogram()+
  annotate("text", x=12, y=400, label="Mean=") +
  annotate("text", x=12.5, y=400, label=x)+
  annotate("text", x=12, y=350, label="SD=   ") +
  annotate("text", x=12.5, y=350, label=y)

f:id:t_kahi:20190704092556p:plain

得られた中央値は平均が8.847,標準偏差は0.676でした.先ほどのn=1000とほぼ変わらない結果が取得できています.

もう少し考えてみる

1つの標本からブートストラップでリサンプリングした結果で大丈夫なのか?ということで,以下のようにサンプリングを1000回繰り返すことを試してみました.
f:id:t_kahi:20190704093857p:plain

Workflowは”「Bootstrap Sampling」を使ってブートストラップ”と変わらないので結果だけ示します.
f:id:t_kahi:20190704094019p:plain 確かに,ブートストラップの結果とほとんど一致しています.
今回は実験データではないので,適当に母集団を用意してそこから100個サンプリングをすることが簡単にできましたが,実際には都合上1回のデータしか取れない場合もあると思います.
このような場合にブートストラップ法はかなり便利のようです.
こちらにブートストラップについてさらに詳しく解説してありました.
http://ebsa.ism.ac.jp/ebooks/sites/default/files/ebook/1881/pdf/vol3_ch8.pdf

まとめ

今回は「Bootstrap Sampling」と「R Snippet」をそれぞれ使ってブートストラップ法をKNIME Workflowで実践しました.
n数を10000くらいに増やしたい場合はループ使っているとめちゃくちゃ時間使うので,普通に「R Snippet」で"simpleboot"使ったほうが圧倒的に早いと思います. (動画を見るとわかるのですが,n=1000でも結構時間がかかっています)

あと,ブートストラップサンプリングにはノンパラメトリックパラメトリックがあるのですが,simpleboot"はノンパラメトリックですが,「Bootstrap Sampling」がノンパラがどうかがわかりませんでした(おそらくそうだと思うのですが)

最後に,このWorkflow全体の動画を示します.
youtu.be

おまけ

「R Snippet」で"simpleboot"と"boot"パッケージを使うと,求めた統計量に対して,4種類手法で信頼区間を求めた結果も出すことができます.

# R Snippet
library(boot)
library(simpleboot)
library(dplyr)

b.median <- one.boot(knime.in$data, median, 2000)

boot.ci(b.median)

normal <- data.frame(boot.ci(b.median)$normal)
colnames(normal) <- c("conf", "V4","V5")

list <- c("conf", "V4","V5")
basic <- data.frame(boot.ci(b.median)$basic)
basic
basic.l <- select(basic, one_of(list))
percent <- data.frame(boot.ci(b.median)$percent)
percent.l <- select(percent, one_of(list))
bca <- data.frame(boot.ci(b.median)$bca)
bca.l <- select(bca, one_of(list))

results <- rbind(normal, basic.l, percent.l, bca.l)
results_new <- transform(results, methods = c("normal", "basic", "percent", "bca"))

knime.out <- results_new

結果をテーブル形式で表示で出力できるので,そのまま次のWorkflowにも流せますね.
f:id:t_kahi:20190704095343p:plain

カラム名"V4","V5"が信頼区間の上下で,methodsに手法をそれぞれ示しています.