Note of Pediatric Surgery

腸内細菌、R、ときどき小児外科

ELISAの測定結果をRで計算する 2. 4パラメーターロジスティック回帰で吸光度から濃度を求める

0. はじめに

それでは、4パラメーターロジスティック曲線を描いて、回帰式からELISAの値を算出していきます。前回記事の続きです。

pediatricsurgery.hatenadiary.jp

この記事のコンテンツは

  1. 必要なパッケージのインストール
  2. ELISAデータをGitHubから入手する
  3. バックグラウンドの値を差し引く
  4. スタンダードの散布図を描く
  5. 4パラメーターロジスティック回帰の回帰式を求めて予測値を求める

となります。尚、本記事は前ブログの記事を大幅加筆修正したものです。前ブログでの誤りなども記載しておりますので、ご参考いただければと思います。

1. 必要なパッケージのインストール

Rで4パラメーターロジスティック曲線を描く方法はいくつかあるようですが、{nplr}と{drc}を使用します

まずdrcをダウンロードしてインストールしておきます。ついでに{ggplot2}と{dplyr}や{tidyr}を使うため、これらが1つの包括された{tidyverse}も立ち上げておきましょう。{ggplot2}と{dplyr}とは何ぞ?という人はこちらもどうぞ。データ解析には必須のパッケージです。

http://stat.biopapyrus.net/ggplot/

qiita.com

# install.packages("drc", dependencies = TRUE)
library(drc)
# install.packages("nplr")
library(nplr)
# install.packages("tidyverse")
library(tidyverse)

2. ELISAデータをGitHubから入手する

ここでELISAのデータを使用したいのですが、ちょうどいいデータがなかったので、コチラのデータを使用して下さい。練習で使った96wellのデータを少しいじったものをGitHubにアップロードしました。GitHubからRに直接ダウンロードするには一手間必要です。ちょっと面倒くさいけど、下記の様な手順を踏む必要があるようです。

# install.packages("RCurl", dependencies = TRUE)
library("RCurl")
getURL("https://raw.githubusercontent.com/Razumall/NOPS/master/Data/ELISA.csv") %>%
  read.csv(text = ., header = TRUE) %>%
  tbl_df() -> df

と入力すると

X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12
1.1197 1.0665 0.0920 0.0932 0.1470 0.1299 0.1355 0.1485 0.2746 0.2875 0.0572 0.0576
0.6696 0.6609 0.0910 0.0891 0.1700 0.1606 0.1160 0.1205 0.3440 0.3598 0.0579 0.0580
0.3759 0.3399 0.0880 0.0852 0.1272 0.1170 0.0887 0.0892 0.3433 0.3631 0.0575 0.0576
0.1898 0.1869 0.0696 0.0678 0.1175 0.1165 0.0667 0.0662 0.4164 0.4589 0.0691 0.0565
0.1158 0.1110 0.0554 0.0545 0.2451 0.2428 0.1200 0.1226 0.2679 0.2908 0.0567 0.0626
0.0812 0.0817 0.1431 0.1382 0.0903 0.0879 0.1168 0.1276 0.3529 0.3694 0.0525 0.0561
0.0682 0.0665 0.1015 0.1008 0.1631 0.1700 0.2103 0.2110 0.0507 0.0504 0.0565 0.0568
0.0529 0.0529 0.0836 0.0780 0.0693 0.0723 0.1936 0.1885 0.0504 0.0511 0.0572 0.0574

こんな感じのデータが出力されると思います。8 * 12 = 96 wellになっていて、2列ずつのduplicateになっており、左の2列がstandardになっており、1000, 500, 250, 125, 62.5, 31.25, 15.625, 0 ( blank ) となっています。残りの6列は測定したいサンプルの吸光度を示します。

3. バックグラウンドの値を差し引く

空のwellでも吸光度は0にならないので、何も入れなかったwellの吸光度をバックグラウンドとして全体の値から差し引く必要があります。1列目と2列目の8行目がバックグラウンドにしてあります。

df %>%
  mutate_all(funs(. - as.numeric((df[8,1]+df[8,2])/2 ))) -> df2 # バックグラウンドの平均をとって全体から差し引く
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12
1.0668 1.0136 0.0391 0.0403 0.0941 0.0770 0.0826 0.0956 0.2217 0.2346 0.0043 0.0047
0.6167 0.6080 0.0381 0.0362 0.1171 0.1077 0.0631 0.0676 0.2911 0.3069 0.0050 0.0051
0.3230 0.2870 0.0351 0.0323 0.0743 0.0641 0.0358 0.0363 0.2904 0.3102 0.0046 0.0047
0.1369 0.1340 0.0167 0.0149 0.0646 0.0636 0.0138 0.0133 0.3635 0.4060 0.0162 0.0036
0.0629 0.0581 0.0025 0.0016 0.1922 0.1899 0.0671 0.0697 0.2150 0.2379 0.0038 0.0097
0.0283 0.0288 0.0902 0.0853 0.0374 0.0350 0.0639 0.0747 0.3000 0.3165 -0.0004 0.0032
0.0153 0.0136 0.0486 0.0479 0.1102 0.1171 0.1574 0.1581 -0.0022 -0.0025 0.0036 0.0039
0.0000 0.0000 0.0307 0.0251 0.0164 0.0194 0.1407 0.1356 -0.0025 -0.0018 0.0043 0.0045

4. スタンダードの散布図を描く

僕は最初の2列 ( X1とX2 ) にduplicateでスタンダードを置くようにしています。4パラメーターロジスティック曲線をまず書くのに必要なのはスタンダードの値なので、それを抜き出してデータフレームにしておきます。

df2 %>%
  dplyr::select(X1, X2) %>%
  mutate(Concentration = c(1000*0.5^(0:6), 0)) %>%
  gather(Column, Absorbance, -Concentration) %>%
  filter(Concentration!=0) %>% # バックグラウンドは除去する
  dplyr::select(-Column) -> Standard
  
# kable(Standard, align="c")
Concentration Absorbance
1000.000 1.0668
500.000 0.6167
250.000 0.3230
125.000 0.1369
62.500 0.0629
31.250 0.0283
15.625 0.0153
1000.000 1.0136
500.000 0.6080
250.000 0.2870
125.000 0.1340
62.500 0.0581
31.250 0.0288
15.625 0.0136

上記のコードを実行するとStandardには上記の様なデータが格納されていると思います。これを{ggplot2}で散布図にしてみましょう。

ggplot(Standard, aes(x=Concentration, y=Absorbance))+
  geom_point()+
  scale_x_log10() # X軸をlog scaleにする

するとこんな感じになると思います。確かにシグモイド曲線になっているのがわかると思います。

5. 4パラメーターロジスティック回帰の回帰式を求めて予測値を求める

5-1. {nplr}: おすすめ

{nplr}のvignetteを参考にしてください。nplr()という関数を使用します。

モデルを作成する

# install.packages("nplr")
library(nplr)
nplr <- nplr( x = Standard$Concentration,
        y = Standard$Absorbance )
> nplr
Instance of class nplr 

Call:
nplr(x = Standard$Concentration, y = Standard$Absorbance)

4-P logistic model
Bottom asymptote: 0.006489387 
Top asymptote: 1.936195 
Inflexion point at (x, y): 2.953461 0.9713423 
Goodness of fit: 0.9987979 
Weighted Goodness of fit: 0.9999795 
Standard error: 0.01331459 0.006026113 

実行するとという結果が返って来ます。どうやら自動でパラメーターをいくつに設定するかを計算してくれるようですね。きちんと、ELISA kitのマニュアルに書かれている4-P logistic modelを選択してくれています。しかもGoodness of fit: 0.9987979であり、モデルの適合度も優れていそうです。

回帰曲線をプロットする

plot( nplr,
      xlab="Concenttration",
      ylab="Absorbance")

こんな感じで回帰曲線がプロットされます。

測定値を得る

df2 %>%
  dplyr::select(-c(X1, X2)) %>% # Standardの列を除外する
  gather(Column, Absorbance) %>%
  mutate(Sample = c(rep(1:8, 2), rep(9:16, 2), rep(17:24, 2), rep(25:32, 2), rep(33:40, 2)) ) %>% # 各wellに入れたサンプルの番号をつける
  mutate(Sample = as.factor(Sample)) %>%
  group_by(Sample) %>%
  summarise(Mean = mean(Absorbance), SD = sd(Absorbance)) -> Measured

# kable(Measured, align="c")
Sample Mean SD
1 0.03970 0.0008485
2 0.03715 0.0013435
3 0.03370 0.0019799
4 0.01580 0.0012728
5 0.00205 0.0006364
6 0.08775 0.0034648
7 0.04825 0.0004950
8 0.02790 0.0039598
9 0.08555 0.0120915
10 0.11240 0.0066468
11 0.06920 0.0072125
12 0.06410 0.0007071
13 0.19105 0.0016263
14 0.03620 0.0016971
15 0.11365 0.0048790
16 0.01790 0.0021213
17 0.08910 0.0091924
18 0.06535 0.0031820
19 0.03605 0.0003536
20 0.01355 0.0003536
21 0.06840 0.0018385
22 0.06930 0.0076368
23 0.15775 0.0004950
24 0.13815 0.0036062
25 0.22815 0.0091217
26 0.29900 0.0111723
27 0.30030 0.0140007
28 0.38475 0.0300520
29 0.22645 0.0161927
30 0.30825 0.0116673
31 -0.00235 0.0002121
32 -0.00215 0.0004950
33 0.00450 0.0002828
34 0.00505 0.0000707
35 0.00465 0.0000707
36 0.00990 0.0089095
37 0.00675 0.0041719
38 0.00140 0.0025456
39 0.00375 0.0002121
40 0.00440 0.0001414

ここで大事なのはサンプルの入れ間違いなどなどによって、duplicateの値が大きく異なっている場合、もしくはサンプルの入れ忘れが発生していることがあります。Meanがマイナスになっているものは少なくともサンプルの入れ忘れでしょうし、SDが大きいものはちょっと再測定を検討した方がいいかもしれません。ここではマイナスになっているものだけ除去しておきます。

Measured %>%
  filter(Mean > 0) -> Measured2

Concentrationを予測する

{nplr}のgetEstimates()を使用します。

getEstimates(nplr, Measured2$Mean) %>%
  tbl_df() %>%
  dplyr::rename(Absorbance=y, Concentration=x) -> Estimates_nplr
  
# kable(Estimates_nplr %>% head(), align="c")
Absorbance x.025 Concentration x.975
0.0397000 20.134198 43.21722 63.56545
0.0371500 17.233111 40.66222 62.53931
0.0337000 14.231966 37.12865 58.59670
0.0158000 2.736750 16.49146 42.45722
0.0151566 2.513367 15.62500 40.78934
0.0877500 67.788432 86.20568 103.56121

とすると95%信頼区間を含めた吸光度からの濃度の予測値を立ててくれます。これで欲しい値を手に入れることができました。めでたしめでたし。

予測値のプロットを描いて希釈濃度が妥当か検討する

ggplot(Estimates_nplr, aes(x=Concentration, y=Absorbance))+
  geom_point()+
  scale_x_log10()+
  geom_hline(aes(yintercept=0.1), color="gray", linetype="dashed")

とするとこんな感じの曲線が得られます。実際に吸光度から濃度を計算する際に重要なのは、信頼性の高い予測値はシグモイド曲線が直線になっている範囲でしか得られないとされているようですので水平線を引いた、Absorbance < 0.1のサンプルは希釈濃度を調整して再測定した方がよいのかもしれません。

5-2. {drc}

前ブログで記載していた{drc}のdrm()に関しても記載しておきます。Rで用量反応曲線を参考にさせていただきました。drm()で使用できるモデルの一覧が上記にまとまっていましたので引用させていただきます。

drc パッケージ内の関数とモデルの対応を一部下記に示します。

関数 モデル
G.4() 4 パラメータゴンペルツ
L.4() 4 パラメータロジスティック
LL.4() 4 パラメータ log ロジスティック
W1.4() 4 パラメータワイブル 1
W2.4() 4 パラメータワイブル 2
BC.4() 4 パラメータ Brain-Cousens

モデルを作成する

drc <- drm( Concentration ~ Absorbance,
            data = Standard,
            fct = L.4() ) # L.4: 4パラメータロジスティック

これを実行すると各パラメーターが算出されます。

> drc

A 'drc' model.

Call:
drm(formula = Concentration ~ Absorbance, data = Standard, fct = L.4())

Coefficients:
b:(Intercept)  c:(Intercept)  d:(Intercept)  e:(Intercept)  
       -1.257       -558.225       3577.330          1.445 

回帰曲線をプロットする

plot(drc,
  type="all",
  col="gray",
  pch=19)

{nplr}の結果と比べてX軸とY軸が逆になっていることに注意してください。

Concentrationを予測する

一般的な回帰式で使うpredict()を使用します。

Measured2 %>%
  mutate(Concentration = predict(drc, newdata = .[,2] )) %>% # predict()でConcentrationを予測
  dplyr::rename(Absorbance = Mean) -> Estimates_drc
  
# kable(Estimates_drc %>% head(), align="c")
Sample Absorbance SD Concentration
1 0.03970 0.0008485 45.23250
2 0.03715 0.0013435 43.58224
3 0.03370 0.0019799 41.35549
4 0.01580 0.0012728 29.91191
5 0.00205 0.0006364 21.24566
6 0.08775 0.0034648 77.03440

とすると、吸光度から濃度が予測されます。

予測値のプロットを描いて希釈濃度が妥当か検討する

ggplot(Estimates_drc, aes(x=Concentration, y=Absorbance))+
  geom_point()+
  scale_x_log10()+
  geom_hline(aes(yintercept=0.1), color="gray", linetype="dashed")

同じ計算をしているはずなのに、{nplr}の結果と微妙に違うのは…ちょっとわかりません。

番外編: 前ブログ記事の訂正

尚、前ブログの記事で

余談ですが、実際はfctの設定は " L.4 " が4パラメーターロジスティックのoptionなのですが、ここでは " LL.4 " の4パラメータlogロジスティックを使っています。" log " がつくのとつかないのでどういう違いがあるのかわかりませんでしたが、

  • 参考にしたサイトでもLL.4を使用している
  • Softmax Proで自動で計算してくれる、係数に近かったのはlogありの方
  • 実際に吸光度から濃度計算した値もlogありの方が信用できる

というところから fct=LL.4のoptionを選択しました。あとは散布図を描けばOKです。

と記載しており、これは誤りだと訂正させてください。 ( 当たり前ですよね、よく調べず、しかも長期間放置してすみませんでした ) 何人かの方からコメントをいただいていましたが、ほとんど前ブログを放置していたので、気づきませんでした。お詫び申し上げます。