🔖

【Plot】[ComplexHeatmap] アノテーション付き棒グラフを作成

2024/07/02に公開

ggplotで棒グラフを書くときに、y軸に1つの変数を指定するとして、それ以外に変数を増やそうと思うと、棒グラフの塗りつぶしや枠線の色、透明度を変えるということをしても3変数ぐらいしか増やせない。増やせたとしても正直見やすいplotではないだろう。

例えばmtcarsのデータを例に、mpg列をy軸、cyl列で塗りつぶし、disp列で枠線、hp列で透明度を変えるようにした棒グラフを描いてみる。

library(dplyr)
library(ggplot2)

data("mtcars")

df <- mtcars
ggplot(data = df, aes(x = rownames(df), y = mpg, fill = cyl, col =disp, alpha = hp)) +
  geom_col() +
  theme(axis.text.x = element_text(angle = 45, hjust=1))

見た目綺麗でも値の違いを分かりやすく表示できているとは言えない。

ggplotで4変数(y軸、塗りつぶし、枠線、透明度)を表現


pheatmapやComplexHeatmapのようにヒートマップの上部や左右に別の変数を表示できればもっと分かりやすいplotが得られそうである。本記事ではComplexHeatmapを使って棒グラフの上にアノテーション情報を付与する例を紹介する。
ComplexHeatmapではヒートマップのアノテーションに棒グラフや折れ線グラフなどを入れることができる。このアノテーション用に作る棒グラフをメインのplotになるように工夫していく。

# BiocManager::install("ComplexHeatmap")

library(ComplexHeatmap)
library(circlize) # グラデーション色を作るのに使用するだけ


デモデータ

mtcarsを使用する。デモ用に名義尺度の列も欲しいのでmtcarsに生産国の情報を加えておく。

df <- mtcars

df$country <- ifelse(grepl("Mazda|Toyota|Honda|Datsun", rownames(df)),
                     yes = "Japan", no = 
              ifelse(grepl("Hornet|Valiant|Duster|Cadillac|Camaro|Chrysler", rownames(df)),
                     yes = "America", no =
              ifelse(grepl("Merc|Porsche|Volvo|Fiat", rownames(df)),
                     yes = "Europe", no = "Other")))

plot完成例

ComplexHeatmapを使えば例えばこんなアノテーション付き棒グラフを描くことができる。

スクリプト
library(ComplexHeatmap)
library(circlize)

df <- df[order(df$mpg,decreasing = T),]
main_mat <- df[,"mpg",drop=F]

anno1 <- as.matrix(df[,c("cyl"),drop=FALSE])
anno2 <- as.matrix(df[,c("country"),drop=FALSE])

# cyl列用の色機能
cyl_color2 <- colorRamp2(
  breaks = c(min(df$cyl),median(df$cyl),max(df$cyl)),
  colors = c("lightgreen","green", "darkgreen")
)

# マーク表示用のベクトル
pch_anno2 <- rep(NA, times = nrow(anno2))
pch_anno2[anno2 == "Japan"] <- 1

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno_simple(
    x = anno1,
    col = cyl_color2,
    gp = gpar(col="black")
  ),
  disp = df$disp,
  hp =df$hp,
  drat = df$drat,
  wt = df$wt,
  qsec = df$qsec,
  vs = df$vs,
  country = anno_simple(
    x = anno2,
    pch = pch_anno2,
    col = c(Japan="blue", America="green", Europe="red",Other= "gray"),
    gp = gpar(col="black")
  ),
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch"),
    bar_width = 1,
    gp = gpar(fill = "lightyellow", # 塗りつぶし
              col = "black" # 枠線の色
    )
  )
)

# 空のmatrix作成
dammy_mat <- matrix(data = 0, nrow = 1, ncol = nrow(df))
colnames(dammy_mat) <- rownames(df)

# ヒートマップオブジェクト作成
ht <- 
  Heatmap(dammy_mat, 
          col = "white",
          cluster_columns = F, # <-- サンプル順が変わらないようにクラスタリングをオフ
          cluster_rows = F,
          height = 0, # <-- plotの高さを0にして非表示
          top_annotation = ha, # <-- アノテーションオブジェクト
          show_heatmap_legend = FALSE
  )

draw(ht, 
     annotation_legend_list = packLegend(
       lgd_countory,
       lgd_countory_pch,
       lgd_cyl
     ),
     annotation_legend_side = "left")


【 解説 】

以下にstep by stepで上記plotの一端を解説する。

メインの棒グラフ

メインとなるplotを作成する。ComplexHeatmapのアノテーションオブジェクトとしてのplot作成を行う。

まず棒グラフに使用するmatrixを用意する。このmatrixは行名にサンプル、列に変数となるようにしておく。(行名が入っていなくても描けるが、後々順番合わせがややこしくなるので識別可能にしておく。)
ここでは「mpg」列の値で棒グラフを描いてみる。(2変数以上のmatrixにしても良い。)
1列だけ抜き出すとベクトルに変換されてしまうので、drop=FALSEをindexに入れて次元落ちを防いでおく。

main_mat <- df[,"mpg",drop=F]

※ 2列以上のmatrixにしても良いが、plotは積み上げ棒グラフになる。

HeatmapAnnotation()機能は複数のアノテーションを1まとめにするために使用する。個々のアノテーションは任意の名前 = anno_〇〇()という形で作っていく。

棒グラフアノテーションはanno_barplot()で作成できる。まずはシンプルに何もオプションを付けずに描いてみる。※ 2変数以上の場合はデフォルトで積み上げ棒グラフになる。beside=TRUEを入れると変数ごとに棒がわかれる。

ha <- HeatmapAnnotation(
  mpg = anno_barplot(
    x = main_mat
  )
)

このHeatmapAnnotationオブジェクトをdraw()機能に渡して可視化する。

draw(ha)

Viewerの下部にplotが表示される。ヒートマップのアノテーション用のplotなのでこのままでは中央部に大きく表示されない。そこで事前に棒グラフのサイズを指定しておく。

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch")
  )
)

# 前のplotの更新を終了して、新規plotを作成させるために必要
dev.off()
# 可視化
draw(ha)

アノテーションの追加

この棒グラフの上部に他の変数の情報を加えていく。1変数列のシンプルなアノテーションはHeatmapAnnotation()機能内で任意の名前 = matrixとするか、任意の名前 = anno_simple(matrix)機能で作成する。

anno_simple()に渡すmatrixも行にサンプル、列に変数が来るようにして、行の順番も入れ替わることがないように注意しておく。

ここでは「cyl」列と「country」列のアノテーションを作ってみる。渡すmatrixも行にサンプル、列に変数が来るようにして、行の順番も入れ替わることがないように注意しておく。

# アノテーション用のmatrixを用意
anno1 <- as.matrix(df[,c("cyl"),drop=FALSE])
anno2 <- as.matrix(df[,c("country"),drop=FALSE])

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno1,
  country = anno2,
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch")
  )
)

# 可視化
dev.off()
draw(ha)


ダミーマトリックスにアノテーションを付けてplot

anno_barplot()の棒グラフをメインにすると言ってもアノテーションオブジェクトとして扱われるままではplotの軸ラベル表示やplot内のlegend配置などでコントロールが難しくなってくる。(アノテーションオブジェクトをdraw()するだけではサンプル名が表示されない。)

そこでHeatmap()機能でアノテーションオブジェクトを表示させる。
この機能はヒートマップを描くためのものなので、ヒートマップ用のmatrixが必須である。今回は何も表示されたくないので、ダミーマトリックスを用意し、ヒートマップの表示サイズも0にすることで非表示化させる。

dammy_mat <- matrix(data = 0, nrow = 1, ncol = nrow(mtcars))
colnames(dammy_mat) <- rownames(mtcars)

# ヒートマップオブジェクト作成
ht <- 
  Heatmap(dammy_mat, 
          col = "white",
          cluster_columns = F, # <-- サンプル順が変わらないようにクラスタリングをオフ
          cluster_rows = F,
          height = 0, # <-- plotの高さを0にして非表示
          top_annotation = ha # <-- アノテーションオブジェクト
    )

# 可視化
dev.off()
draw(ht)

これでサンプル名が表示され、棒グラフのy軸ラベルも表示された。

anno_simple()でアノテーションを作るとオプション類も併せて指定できるのは良いが、凡例表示が自動で行われなくなる。Legendオブジェクトをカスタムで作って表示させることで対応する。

アノテーションの色割り当て

ComplexHeatmapではアノテーションの色パレットをランダムに割り当てるためplotするたびに色が変わる。再現性が必要であれば自身で色を割り当てる必要がある。この時、離散値であれは色ベクトル、連続値であれば色割り当て機能が必要となる。

HeatmapAnnotation()機能の中のcol=引数にリスト化した情報を渡す。このリストの名前とアノテーション名を対応させる。

color_list <- list(
  アノテーション名1 = 色情報1,
  アノテーション名2 = 色情報2
  )

ha <- HeatmapAnnotation(
  アノテーション名1 = アノテーション情報1,
  アノテーション名2 = アノテーション情報2,
  col = color_list
  )

離散値/名義尺度

ユニークな要素の数だけ色を用意し、名前属性に水準を入れた色ベクトルを指定する。

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno1,
  country = anno2,
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch")
  ),
  col = list(
    country = c(Japan="blue", America="green", Europe="red",Other= "gray"))
)

# 可視化
dev.off()
draw(ha)


連続値

連続値の場合は、値を引数に取り色を返す関数を指定する。circlizeパッケージのcolorRamp2()機能で色作成関数が簡単に用意できる。
breaks=で色を割り当てる範囲、colors=で割り当てる色を指定する。2色以上は指定が必要となる。

2色のグラデーション作成機能
cyl_color1 <- colorRamp2(
  breaks = c(min(df$cyl),max(df$cyl)),
  colors = c("lightgreen", "darkgreen")
  )
3色のグラデーション作成機能
cyl_color2 <- colorRamp2(
  breaks = c(min(df$cyl),median(df$cyl),max(df$cyl)),
  colors = c("lightgreen","green", "darkgreen")
)

作成した関数は値を渡すと色コードを返すことが確認できる。



離散値の時と同様に、col=引数にアノテーション名を対応させながら指定する。

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno1,
  country = anno2,
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch")
  ),
  col = list(
    cyl = cyl_color2)
)

# 可視化
dev.off()
draw(ha)


anno_〇〇()を使った場合

anno_barplot()などで作った棒グラフの色は、anno_barplot()内で指定する。gp=引数にgpar()機能で作ったグラフパラメーターを指定する。

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno1,
  country = anno2,
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch"),
    gp = gpar(fill = "lightyellow", # 塗りつぶし
              col = "black" # 枠線の色
              )
  )
)

# 可視化
dev.off()
draw(ha)


anno_simple()

HeatmapAnnotation(アノテーション名=matrix)だけで指定してきたアノテーションをHeatmapAnnotation(アノテーション名=anno_simple(matrix))と書くことができる。
この利点は枠線やマーク付けなど更なる修飾が行えることである。

例えば次のようなアノテーションを作れる。

pch_anno2 <- rep(NA, times = nrow(anno2))
pch_anno2[anno2 == "Japan"] <- 1

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno_simple(
    x = anno1,
    col = cyl_color2
    ),
  country = anno_simple(
    x = anno2,
    pch = pch_anno2,
    col = c(Japan="blue", America="green", Europe="red",Other= "gray"),
    gp = gpar(col="black")
    ),
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch"),
    gp = gpar(fill = "lightyellow", # 塗りつぶし
              col = "black" # 枠線の色
    )
    )
)

# 可視化
dev.off()
draw(ha)

マーク付けはpch=引数に記号番号のベクトルを指定する。ここではJapanの箇所に記号1を、それ以外にNAを入れて非表示とした。
枠線はgp=gpar(col=〇〇)で指定できる。塗りつぶし色はgp=gpar(fill=〇〇)で指定すると同一色での塗りつぶしになってしまう。値に応じて色が変わるようにするにはcol=引数に名前付き色ベクトルや色作成関数を指定する。

Legend

anno_simple()を使ってアノテーション表示を修飾すると、今度は凡例が表示されなくなる。

# アノテーションオブジェクト作成
ha <- HeatmapAnnotation(
  cyl = anno_simple(
    x = anno1,
    col = cyl_color2,
    gp = gpar(col="black")
    ),
  country = anno_simple(
    x = anno2,
    pch = pch_anno2,
    col = c(Japan="blue", America="green", Europe="red",Other= "gray"),
    gp = gpar(col="black")
    ),
  mpg = anno_barplot(
    x = main_mat,
    height = unit(5,"inch"),
    gp = gpar(fill = "lightyellow", # 塗りつぶし
              col = "black" # 枠線の色
    )
    )
)

# ヒートマップオブジェクト作成
ht <- 
  Heatmap(dammy_mat, 
          col = "white",
          cluster_columns = F, # <-- サンプル順が変わらないようにクラスタリングをオフ
          cluster_rows = F,
          height = 0, # <-- plotの高さを0にして非表示
          top_annotation = ha # <-- アノテーションオブジェクト
  )

# 可視化
dev.off()
draw(ht)



凡例を表示するには別途Legendオブジェクトが必要となる。Legend()機能を使用する。
引数には例えば、離散値の場合はどの水準にどの色を割り当てるか、連続値の場合は色作成機能と表示するtickやラベルなどを指定する。

例えば、次のように離散値の凡例を作成してみる。

lgd_countory <- Legend(at = c("Japan", "America", "Europe", "Other"), 
                       legend_gp = gpar(fill = c("blue", "green", "red","gray")),
                       title = "country")

Legendオブジェクトはdraw()機能に渡して可視化できる。

dev.off()              
draw(lgd_countory)



「country」列のマークのアノテーション、「cyl」列の連続値のアノテーションを同様に用意しておく。

lgd_countory_pch <- Legend(labels = c("Japan", ""),
                           pch = c(1,NA),
                           type = "points"
                           )
lgd_cyl <- Legend(
  col_fun = cyl_color2,
  at = c(min(df$cyl),median(df$cyl),max(df$cyl)),
  labels = c("Min","Median","Max"), # <-- 表示を値ではなく任意に変えたい場合
  title = "cyl"
)



作成したLegendオブジェクトはリストにまとめて、draw()機能のannotation_legend_list=引数に指定する。

dev.off()
draw(ht, 
     annotation_legend_list = list(
       lgd_countory,
       lgd_countory_pch,
       lgd_cyl
       ))

packLegend()という機能でLegendオブジェクトをまとめても良い。この方法ではLegendオブジェクトの配置に関するオプションを追加できる。

dev.off()
draw(ht, 
     annotation_legend_list = packLegend(
       lgd_countory,
       lgd_countory_pch,
       lgd_cyl, gap = unit(10,"mm")
       ))

Discussion