Подтвердить что ты не робот

Объединение нескольких комплексных графиков в виде панелей на одной фигуре

Введение by @backlin

Несколько простых графиков можно объединить в виде панелей на одной фигуре с помощью layout или par(mfrow=...). Однако более сложные сюжеты, как правило, настраивают собственную панель, внутренне отключая их от использования в качестве панелей. Есть ли способ создать вложенный макет и инкапсулировать сложный график в одну панель?

Я чувствую, что пакет grid может выполнить это, например. путем построения панелей в отдельных видовых экранах, но не смогли выяснить, как это сделать. Вот пример игрушки, чтобы продемонстрировать проблему:

my.plot <- function(){
    a <- matrix(rnorm(100), 10, 10)
    plot.new()
    par(mfrow=c(2,2))
    plot(1:10, runif(10))
    plot(hclust(dist(a)))
    barplot(apply(a, 2, mean))
    image(a)
}
layout(matrix(1:4, 2, 2))
for(i in 1:4) my.plot()
# How to avoid reseting the outer layout when calling `my.plot`?

Оригинальный вопрос от @alittleboy

Я использую функцию heatmap.2 в пакете gplots для создания тепловых карт. Вот пример кода для одной карты тепла:

library(gplots)
row.scaled.expr <- matrix(sample(1:10000),nrow=1000,ncol=10)
heatmap.2(row.scaled.expr, dendrogram ='row',
          Colv=FALSE, col=greenred(800), 
          key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
          trace='none', colsep=1:10,
          sepcolor='white', sepwidth=0.05,
          scale="none",cexRow=0.2,cexCol=2,
          labCol = colnames(row.scaled.expr),                 
          hclustfun=function(c){hclust(c, method='mcquitty')},
          lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
)

Однако, поскольку я хочу сравнить несколько тепловых карт в одном графике, я использую par(mfrow=c(2,2)), а затем вызываю heatmap.2 четыре раза, т.е.

row.scaled.expr <- matrix(sample(1:10000),nrow=1000,ncol=10)
arr <- array(data=row.scaled.expr, dim=c(dim(row.scaled.expr),4))
par(mfrow=c(2,2))
for (i in 1:4)
heatmap.2(arr[ , ,i], dendrogram ='row',
          Colv=FALSE, col=greenred(800), 
          key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
          trace='none', colsep=1:10,
          sepcolor='white', sepwidth=0.05,
          scale="none",cexRow=0.2,cexCol=2,
          labCol = colnames(arr[ , ,i]),                 
          hclustfun=function(c){hclust(c, method='mcquitty')},
          lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
)

Однако результат - это не четыре тепломассы на одном участке, а четыре отдельных тепловых карты. Другими словами, если я использую pdf() для вывода результата, файл состоит из четырех страниц вместо одного. Нужно ли мне менять какие-либо параметры? Большое вам спасибо!

4b9b3361

Ответ 1

Хорошо. Я полагаю, что этот вопрос сидит без ответа в течение достаточно долгого времени, когда должен быть написан длинный ответ.

Ответ на самые сложные проблемы с графикой - это (как предлагает @backlin) необработанное использование пакета "grid" . Многие готовые графические пакеты переопределяют все текущие видовые экраны и параметры графического устройства, поэтому, если вы хотите, чтобы что-то было сделано очень определенным образом, вы должны сами создать его.

Я рекомендую собрать книгу Пола Мурелла "R Graphics" и перейду к главе о пакете "сетка". Это сумасшедшая полезная книга, и копия сидит на моем столе все время.

Для вашей тепловой карты я написал быстрый учебник, который поможет вам начать быстро.

Функции для

  • grid.newpage() Это инициализирует устройство построения графика. Используйте его без параметров.
  • grid.rect() Это рисует прямоугольник. Ваша тепловая карта - это просто гигантский набор цветных прямоугольников, поэтому это будет основная часть вашей графики. Он работает так: grid.rect(x=x_Position, y=y_Position, width=width_Value, height=height_Value, gp=gpar(col=section_Color, fill=section_Color), just=c("left", "bottom"), default.units="native") Аргумент "just" указывает, какая точка прямоугольника будет сидеть на ваших указанных (x, y) координатах.
  • grid.text() Это рисует текст. Он работает так: grid.text("Label Text", x_Value, y_Value, gp=gpar(col=color_Value, cex=font_Size), just=c("right","center"), rot=rot_Degrees, default.units="native")
  • grid.lines() Это рисует линию. Он работает так: grid.lines(c(x_Start,x_End), c(y_Start, y_End), gp=gpar(col=color_Value), default.units="native")
  • dataViewport() Это определяет атрибуты окна построения, которое "сетка" означает "окно просмотра". Используйте его так: pushViewport(dataViewport(xData=x_Data, yData=y_Data, xscale=c(x_Min, x_Max), yscale=c(y_Min, y_Max), x=x_Value, y=y_Value, width=width_Value, height=height_Value, just=c("left","center"))) Здесь нужно запомнить кое-что... см. Более подробное объяснение видовых экранов.
  • pushViewport() Используется для инициализации вейвпорта. Вы оберните это вокруг определения вида просмотра, чтобы фактически выполнить окно просмотра, например: pushViewport(dataViewport([stuff in here]))
  • popViewport() Это завершает просмотр и перемещает вас на один уровень в иерархии видовых экранов. См. Более подробное объяснение видовых экранов.

Видимости в двух словах

Видовые экраны - это временные пространства рисования, которые определяют, где и как будут создаваться объекты сетки. Все, что находится в окне просмотра, отображается относительно окна просмотра. Если окно просмотра повернуто, все внутри будет повернуто. Видовые экраны могут быть вложенными, могут перекрываться и почти бесконечно гибкими, за одним исключением: они всегда представляют собой прямоугольник.

Кое-что, из-за чего на самом деле запутано много людей, является система координат. Каждый видовой экран, включая начальный экран просмотра grid.newpage(), идет от 0 до 1 на оси x и y. Происхождение (0,0) - это дальний левый нижний угол, а макс (1,1) - дальний верхний правый угол. Это системная единица "npc", и все, что не имеет набора единиц, которые, скорее всего, будут втянуты в соответствии с этой системой. Это означает две вещи для вас:

  • Используйте систему "npc" при указании размеров и местоположений видовых экранов. Предположим, что ваши видовые экраны должны использовать координаты "npc", и вы сэкономите много хлопот. Это означает, что если я хочу сделать два графика рядом друг с другом, определения для двух видовых экранов выглядят примерно так:
    • viewport(x=0, y=0, width=0.5, height=1, just=c("left","lower")) и
    • viewport(x=0.5, y=0, width=0.5, height=1, just=c("left","lower"))
  • Если ваш viewport имеет другую систему координат (например, окно просмотра для построения графика), вам нужно будет указать аргумент "default.units" для каждого объекта сетки, который вы рисуете. Например, если вы попытались построить точку в точке (2,4), вы бы никогда не увидели точку, потому что она была бы далеко за пределами экрана. Указание default.units="native" будет указывать на эту точку, чтобы использовать собственную систему координат видового экрана и правильно нарисовать точку.

Видовые экраны можно перемещать и записывать напрямую, но, если вы не делаете что-то очень автоматизированное, легче указать видовое окно, нарисовать внутри него, а затем "поп" (финализировать) окно просмотра. Это возвращает вас к родительскому видовому экрану, и вы можете начать работу в следующем окне просмотра. Поппинг каждого окна просмотра является беспорядочным и подходит для большинства целей (и облегчает отладку!).

Функция "dataViewport" важна при построении графика. Это специальный вид окна просмотра, который обрабатывает все координаты и шкалы для вас, если вы расскажете, какие данные вы используете. Это тот, который я использую для любой области печати. Когда я впервые начал использовать пакет "grid" , я скорректировал все мои значения в соответствии с системой координат "npc", но это было ошибкой! Функция "dataViewport" делает все просто, пока вы не помните использовать "родные" единицы для каждого элемента чертежа.

Отказ

Визуализация данных - моя сильная сторона, и я не прочь потратить полдня на создание хорошего визуального. Пакет "grid" позволяет мне создавать довольно сложные визуальные эффекты быстрее, чем все, что я нашел. я script мои визуальные функции как функции, поэтому я могу быстро загрузить различные данные. Я не мог быть счастливее.

Однако, если вам не нравятся script вещи, "сетка" будет вашим врагом. Кроме того, если вы считаете, что на полдня слишком много времени для визуального, то "сетка" не поможет вам слишком много. Известный пакет "ggplot2" (в), на который большинство людей соглашается, и я от всей души рекомендую его, хотя я лично не считаю его полезным.

Если кто-то хочет помочь в изучении "сетки" графики, я более чем готов помогать преподавать. Он полностью изменил мою способность создавать быстрые, интеллектуальные и красивые визуальные данные.

Ответ 2

Пакет gridGraphics может помочь,

введите описание изображения здесь

library(gridGraphics)
library(grid)

grab_grob <- function(){
  grid.echo()
  grid.grab()
}

arr <- replicate(4, matrix(sample(1:100),nrow=10,ncol=10), simplify = FALSE)

library(gplots)
gl <- lapply(1:4, function(i){
  heatmap.2(arr[[i]], dendrogram ='row',
            Colv=FALSE, col=greenred(800), 
            key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
            trace='none', colsep=1:10,
            sepcolor='white', sepwidth=0.05,
            scale="none",cexRow=0.2,cexCol=2,
            labCol = colnames(arr[[i]]),                 
            hclustfun=function(c){hclust(c, method='mcquitty')},
            lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
  )
  grab_grob()
})

grid.newpage()
library(gridExtra)
grid.arrange(grobs=gl, ncol=2, clip=TRUE)

Ответ 3

Так же, как сказал Динре, "сетка" pacakge может обрабатывать все сложные сюжеты. По оригинальному вопросу от @alittleboy, я думаю, что пакет "ComplexHeatmap" (который также является базой на сетке) от Bionconductor может быть хорошим решением (http://www.bioconductor.org/packages/release/bioc/vignettes/ComplexHeatmap/inst/doc/ComplexHeatmap.html)

Ответ 4

Я боролся с подобной проблемой и придумал решение, которое очень просто, но требует установки imagemagick. Идея состоит в том, чтобы отображать тепловые карты для разделения файлов, а затем объединять их с командой монтажа:

library(gplots)
row.scaled.expr <- matrix(sample(1:10000),nrow=1000,ncol=10)
arr <- array(data=row.scaled.expr, dim=c(dim(row.scaled.expr),4))
par(mfrow=c(2,2))
for (i in 1:4) {
    ifile <- paste0(i,'_heatmap.pdf')
    pdf(ifile)
    heatmap.2(arr[ , ,i], dendrogram ='row',
                        Colv=FALSE, col=greenred(800), 
                        key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
                        trace='none', colsep=1:10,
                        sepcolor='white', sepwidth=0.05,
                        scale="none",cexRow=0.2,cexCol=2,
                        labCol = colnames(arr[ , ,i]),                 
                        hclustfun=function(c){hclust(c, method='mcquitty')},
                        lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
    )
    dev.off()
}
system('montage -geometry 100% -tile 2x2 ./*_heatmap.pdf outfile.pdf')