カテゴリーで分けられるデータの視覚化(棒グラフおよびエラーバー)

棒グラフ

棒グラフは、種や遺伝子などのカテゴリーで分類されるデータの視覚化に利用される。例えば、処理前と処理後の遺伝子の発現量を比較しながら視覚化したいときに棒グラフを用いる。生命科学の分野において、棒グラフはエラーバーとともに用いられる場合が多い。これは生命科学の実験において、複製実験を行うのが一般的であることに由来する。複製実験で得られたデータから、平均値と標準偏差が求まる。その平均値を棒グラフの高さに、標準偏差をエラーバーとして描かれることも多い。

R では、barplot 関数を利用して棒グラフを描く。オプションなどを与えることで、積み上げ棒グラフなどを描くことができる。また、arrows 関数を利用して、棒グラフの上にエラーバーを重ね書きすることもできる。

棒グラフ

ベクトルデータ

次のサンプルコードは、ある状態下で測定された 5 つの遺伝子(a, b, c, d, e)の発現量(logFPKM)を棒グラフで描く例。まず、遺伝子の発現量をベクトル x に代入し、次に names 関数で、ベクトル x の各要素に名前をつける。

x <- c(21.3, 22.5, 34.1, 12.9, 23.1)
names(x) <- c("a", "b", "c", "d", "e")

barplot(x, xlab = "gene", ylab = "logFPKM")
barplot 関数で描いた棒グラフ

棒グラフを描くとき、項目の名前が長いと、横軸に書き出される際に、左右のラベルと重なり見えにくくなる。対策方法として、横軸のラベルを縦方向にしたりする。

barplot(x, las = 2, xlab = "gene", ylab = "logFPKM")
barplot で描いた棒グラフの横軸ラベルを縦方向にする

比較データ

次のサンプルコードは、処理前と処理後に測定された 5 つの遺伝子(a, b, c, d, e)の発現量(logFPKM)を棒グラフで描く例。行列を用意し、1 列目を処理前、2 列目を処理後として、また各行を遺伝子として、発現量行列を用意する。次に、行列の行と列に名前をつけて、barplot 関数で棒グラフ描く。複数項目あるので棒グラフを横に並べて描くために beside = TRUE を利用し、また、項目名も表示させるために legend = TRUE を利用する。

x <- cbind(c(21.3, 22.5, 34.1, 12.9, 23.1),
           c(25.9, 26.1, 40.2, 13.1, 22.8))
colnames(x) <- c("control", "treatment")
rownames(x) <- c("a", "b", "c", "d", "e")
x
##   control treatment
## a    21.3      25.9
## b    22.5      26.1
## c    34.1      40.2
## d    12.9      13.1
## e    23.1      22.8

barplot(x, beside = TRUE, legend = TRUE, xlab = "gene", ylab = "logFPKM")
barplot 関数に行列を与えて描いた横並べ棒グラフ

行列 x の行と列を入れ替えて(t 関数)描いた場合は、次のようになる。

barplot(t(x), beside = TRUE, legend = TRUE, xlab = "gene", ylab = "logFPKM")
行列の行と列を入れ替えてから与えると、異なる横並べの棒グラフが描かれる

エラーバー(誤差範囲)の付け方

複製実験などが行われたときに、それらの平均値を棒グラフの高さ、標準偏差をエラーバーとして視覚化できる。棒グラフにエラーバーを付け加える場合、まず、データの平均値と標準偏差を計算してから、barplot 関数で平均値を棒グラフとして描き、その上に、arrows 関数で平均値 ± 標準偏差をエラーバーとして描く。

次のサンプルコードは、3 回の複製実験で測定した 5 つの遺伝子の発現量を棒グラフとして描いている。棒グラフを描いた後にエラーバーを描くと、縦軸の制限上、エラーバーの上半分が描かれない場合がある。これを避けるために、barplot で棒グラフを描く際に、縦軸を平均値 + 標準偏差の最大値となるように設定する(ylim = c(0, max(xm + xs)))。

x <- rbind(c(20.3, 22.5, 32.1, 12.9, 23.1),
           c(21.4, 22.1, 34.2, 13.1, 22.8),
           c(20.9, 23.3, 33.6, 12.3, 24.8))
colnames(x) <- c("a", "b", "c", "d", "e")
rownames(x) <- c("rep1", "rep2", "rep3")
x
##         a    b    c    d    e
## rep1 20.3 22.5 32.1 12.9 23.1
## rep2 21.4 22.1 34.2 13.1 22.8
## rep3 20.9 23.3 33.6 12.3 24.8


xm <- apply(x, 2, mean)
xs <- apply(x, 2, sd)

# bar chart
b <- barplot(xm, xlab = "gene", ylab = "logFPKM", ylim = c(0, max(xm + xs)))

# error bar
arrows(b, xm - xs, b, xm + xs, code = 3, lwd = 1, angle = 90, length = 0.1)
棒グラフに標準偏差をエラーバーとして描き入れる例

積み上げ棒グラフ

積み上げ棒グラフは、入力データとして行列を barplot に与えれば描かれる。

x <- cbind(
  c(1.0, 1.0, 1.2, 1.1),
  c(1.7, 1.5, 1.5, 1.8),
  c(2.0, 2.1, 2.2, 2.6),
  c(2.7, 3.1, 3.2, 3.1),
  c(4.2, 4.0, 4.6, 3.6)
)
colnames(x) <- c("a", "b", "c", "d", "e")
rownames(x) <- c("2016", "2017", "2018", "2019")
x
##        a   b   c   d   e
## 2016 1.0 1.7 2.0 2.7 4.2
## 2017 1.0 1.5 2.1 3.1 4.0
## 2018 1.2 1.5 2.2 3.2 4.6
## 2019 1.1 1.8 2.6 3.1 3.6

barplot(t(x), legend = TRUE)
barplot 関数に行列を与えると積み上げ棒グラフが描かれる。

棒グラフに有意差を示すアスタリスクをつける方法

グラフを描く操作がやや複雑なために、これらの操作をまず biobarplot 関数として作る。biobarplot 関数で、まずデータの平均値と標準偏差を計算するとともに、t 検定を行う。次に、barplot で棒グラフを描き、arrows 関数でエラーバーを描く。続いて、lines 関数で比較する 2 つの棒グラフを結ぶ線を描く。最後に、t 検定の結果から、有意差があるならば、text 関数でアスタリスクをかき入れる。

biobarplot <- function(x, xlab = "", ylab = "", col = NA) {
    sample.labels <- names(x)
    condition.labels <- colnames(x[[1]])

    if (is.na(col)) {
        col <- c("#666666", "#CCCCCC")
    }
    
    # prepare variables to save mean, sd, and p-values
    dfm <- dfs <- matrix(0, ncol = length(condition.labels), nrow = length(sample.labels))
    colnames(dfm) <- colnames(dfs) <- condition.labels
    rownames(dfm) <- rownames(dfs) <- sample.labels
    pvalues <- rep(NA, length(sample.labels))
    
    # calculate mean, sd, and p-values
    for (i in seq(x)) {
        dfm[i, ] <- apply(x[[i]], 2, mean, na.rm = TRUE)
        dfs[i, ] <- apply(x[[i]], 2, sd, na.rm = TRUE)
        x1 <- x[[i]][, 1]
        x2 <- x[[i]][, 2]
        pvalues[i] <- t.test(x1[!is.na(x1)], x2[!is.na(x2)])$p.value
    }
    
    # change data structure
    dfm <- t(dfm)
    dfs <- t(dfs)
    
    # calculate the y-coordinates for plotting *
    maxy <- max(dfm + dfs) * 1.1
    stepy <- max(dfm + dfs) * 0.1

    # bar chart
    bb <- barplot(dfm, beside = TRUE, ylim = c(0, maxy + 2 * stepy), col = col, border = col, xlab = xlab, ylab = ylab)
    
    # error bar
    arrows(bb, dfm - dfs, bb, dfm + dfs, code = 3, lwd = 1, angle = 90, length = 0.25 / length(sample.labels))
     
    # write *
    for (i in 1:length(pvalues)) {
        xi <- bb[, i]
        yi <- dfm[, i] + stepy * 1.5
        maxyi <- max(yi) + stepy
        if (pvalues[i] < 0.05) {
            lines(c(xi[1], xi[1], xi[2], xi[2]), c(yi[1], maxyi, maxyi, yi[2]))
            if (pvalues[i] < 0.01) {
                text((xi[1] + xi[2]) / 2, maxyi + stepy / 4, "**")
            } else if (pvalues[i] < 0.05) {
                text((xi[1] + xi[2]) / 2, maxyi + stepy / 4, "*")
            }
        }
    }
    
    # graph legend
    legend("topleft", legend = condition.labels, fill = col, col = col, border = col, box.lwd = 0, box.lty = 0)
}

3 種の細胞に対して、処理前と処理後をそれぞれ 3 回の複製実験により濃度を測定したデータを例にして、棒グラフを描く。

cell.a <- data.frame(Control = c(1.1, 1.2, 0.9),
                     Treated = c(1.5, 1.6, 1.3))
cell.b <- data.frame(Control = c(1.0, 0.8, 0.9),
                     Treated = c(1.8, 1.9, NA))
cell.c <- data.frame(Control = c(1.4, 1.5, 1.2, NA),
                     Treated = c(2.1, 2.0, 1.8, 1.9))

data <- list(`Cell A` = cell.a, `Cell B` = cell.b, `Cell C` = cell.c)

biobarplot(data, xlab = "Cell type", ylab = "Value")
R の barplot 関数を応用して、二つの棒グラフを並べて、両者の間に有意差があるならばアスタリスクをかき入れる例。