微生物屋のノート

プログラミング・解析関連についてのノートです。

anova や glm を、for 関数で処理する方法

色々な処理区があり、各処理区の群集を特徴付ける指標がたくさん得られてきた。
そして、その処理区ごとの各指標の平均値の違いは、処理区ごとに説明できるのかを統計的に示す時に、分散分析を行うと思います。

Rプログラムの中では、anova 関数や aov 関数が分散分析を行う関数になり、この関数は、formula 型の Input ( y ~ x, data=df )を要求してきます。

この aov 関数と for 関数を使って、いろんな指標の分散分析を一括で処理する時、formula 関数が便利です。



次のような data frame (省略版) があるとします。
A~Gの処理区があって、3つの指標を測ったマトリックスになっています。各処理区にはに100個の値(100サンプル)が、ほんとはあります。

   treatment Richness  Abundance  Interaction
1          A 63.03062  99.183025 61.779550401
2          A 59.28171  92.629037 62.014830418
3          A 60.02215  99.844330 59.648745452
4          B 77.90848  46.798696 54.998186842
5          B 71.64935  41.451237 61.789154721
6          B 71.02360  52.430302 56.806729181
7          C 32.37181  79.320784 44.909000587
8          C 40.65879  65.529647 55.526555285
9          C 31.65834  65.384726 44.569884241
10         D 93.80565  62.985476 86.305609523
11         D 73.20372  62.672580 83.021430326
12         D 76.77968  69.194962 81.886503028
13         E 56.08483 -12.653348  4.713663180
14         E 61.87220  -5.530885  0.005947824
15         E 50.68268   5.953438 -2.610380320
16         F 72.80016   3.270578 57.132157097
17         F 76.54925   1.466419 85.587994020
18         F 70.88253   3.530791 79.221027188
19         G 97.02695  12.273005  7.767906549
20         G 86.16959  -1.815169  0.361944723
21         G 77.37816  26.637699  8.648186671

図にすると以下の通りです。


それでは、各指標、1列ごとにfor 関数を回して、分散分析を行います。
この時、paste 関数で式の形をした文字列を作り、それを formula 関数で type : formula にしてあげます。

result <- list()

for(i in colnames(summ)[-1]){
    
    formula.tmp <- paste(i, 'treatment', sep='~') # 中身としては"Interaction~treatment"という形
    formula.object <- formula(formula.tmp) # object の型をformulaにする

    result[[i]] <- aov(formula.object, data=summ) # aov 関数にかける
    
}

これで、result の中に各指標の結果を放り込むことができました。


おまけ

分散分析で有意差が出た後、Post-hoc検定、処理区間のペアワイズで何らかの検定を行うそうです。

処理区ごとに、有意差があると見せる時はgeomsignif package が便利そうです。
Rで解析:ggplot2のプロットに有意差バーを追加!「ggsignifr」パッケージ

しかし、処理区間で有意差が出まくると、線がたくさん引かれて微妙な気持ちになったので、別の見せ方のコードを提示しておきます。

線ではなく、有意差があった処理区の名前を表示するようにして見ました。
ここでのポイントは、geom_textの高さをvjust で揃えているところです。

実データではきれいにできたのですが、テストデータだと微妙でしたね...

*TukeyHSDで2群間比較を行なっていますが、どの方法でPost-hoc検定を行うかは使うデータによって判断してください

summ <- c()

for(l in LETTERS[1:7])	{
    df <- matrix(NA, nrow=3, ncol=3, dimnames=list(NULL, c('Richness', 'Abundance', 'Interaction') ))
    
    for(i in 1:ncol(df)){
        
        df[,i] <- rnorm( n= 3, mean=sample(1:100, 1), sd=sample(1:10,1))
        
    }			
    
    summ <- rbind(summ, cbind(treatment=l, as.data.frame(df)))
}


library(tidyr)
library(ggplot2)
library(RColorBrewer)

lf.sum <- gather(summ, key=index, value, -1)

head(lf.sum)

ggplot(lf.sum) +
    geom_boxplot(aes(x= treatment, y=value)) +
    facet_wrap(~key)

result <- list()
for(i in colnames(summ)[-1]){
    
    p.mat <- matrix(NA, ncol=7, nrow=7, dimnames=list(unique(summ$treatment), unique(summ$treatment)))
    
    formula.tmp <- paste(i, 'treatment', sep='~')
    formula.object <- formula(formula.tmp)
    result[[i]] <- aov(formula.object, data=summ)
    
    if( summary(result[[i]])[[1]][[5]][1] < 0.05 ){
        
        test <- TukeyHSD(aov(formula.object, data=summ))$treat
        
        for(l in 1:nrow(test)){
            a <- strsplit(rownames(test), '-')[[l]]
            p.mat[a, a] <- test[l,4]
        }
        diag(p.mat) <- NA
    }
    
    for(l in 1:nrow(p.mat)){
        
        sig <- names(which(p.mat[l,] < 0.05))
        
        lf.sum[which( lf.sum$treatment==rownames(p.mat)[l] & lf.sum$index==i ), 'pval'] <- paste(sig, collapse='\n')
        lf.sum[which( lf.sum$treatment==rownames(p.mat)[l] & lf.sum$index==i ), 'y'] <- 
            quantile(lf.sum[which( lf.sum$index==i ), 'value'], probs=seq(0,1,0.1))[11]
        
    }
}

gg <- ggplot(lf.sum)+
    geom_boxplot(outlier.size = 0,aes(x=treatment, y=value, color=treatment, fill=after_scale(alpha(color, 0.5) )), width=0.5)+
    geom_text( aes(x=treatment, y=y, label=pval),vjust="inward" )+
    facet_wrap(~index, scales='free', ncol=2) +
    scale_color_manual(values=brewer.pal(8,'Accent')[-c(6)])+
    scale_shape_manual(values=c(21:25,6:8))+
    theme_minimal(base_size=18) + theme(panel.border=element_rect(fill=NA, size=1),)+
    scale_x_discrete(guide = guide_axis(n.dodge = 2))