R - 如何将屏幕空间分配给复杂的ggplot图像 [英] R - how to allocate screen space to complex ggplot images

查看:106
本文介绍了R - 如何将屏幕空间分配给复杂的ggplot图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图编写一个脚本,在单个图像中生成四个不同的图。具体而言,我希望尽可能地重新创建此图形:



我目前的脚本产生了四个类似于这些的地块,但我无法弄清楚如何相应地分配屏幕的不动产。我想:


  1. 修改图的高度和宽度,使四个图的宽度均匀一致,其中一个比另一个高很多在它们之间具有统一的高度

  2. 通过坐标定义图例的位置,以便我可以有效地使用屏幕空间修改图像的整体形状明确地根据需要(也许我需要它接近于方形)

生成一些数据到图表

  pt_id = c(1:279)#DEFINE PATIENT IDs 
smoke = rbinom(279,1,0.5)#DEFINE SMOKING STATUS
hpv = rbinom(279,1,0.3)#DEFINE HPV STATUS
data = data.frame(pt_id,smoke,hpv)#PRODUCE DATA FRAME

添加解剖学网站数据

  data $ site = sample (1:4,279,replace = T)
data $ site [data $ site == 1] =Hypopharynx
data $ site [data $ site == 2] =Larynx
data $ site [data $ site == 3] =口腔
dat $ site $ [data $ site == 4] =Oropharynx
data $ site_known = 1#
$ b

添加突变频率数据

  data $ freq = sample(1:1000,279,replace = F)

DEFINE BARPLOT

  require(ggplot2)
require(gridExtra)
bar = ggplot(data,aes(x = pt_id,y = freq))+ geom_bar(stat =identity) +主题(axis.title.x = element_blank(),axis.ticks.x = element_blank(),axis.text.x = element_blank())+ ylab(突变数目)
#DEFINE BINARY PLOTS
smoke_status = ggplot(data,aes(x = pt_id,y = smoke,fill =red))+ geom_bar(stat =identity)+ theme(legend.position =none,axis.title .x = element_blank(),axis.ticks.x = element_blank(),axis.text.x = element_blank())+ ylab(Smoking Status)
hpv_status = ggplot(data,aes(x = pt_id ,y = hpv,fill =red))+ geom_bar(stat =identity)+ theme(legend.position =none,axis.title.x = elem (数据,aes(x = pt_id,y = 1)),生产四个图表合并成一个图表, / b>

  grid.arrange(bar,smoke_status,hpv_status,site_status,nrow = 4)

我怀疑完成这些任务所需的功能已经包含在ggplot2和gridExtra中,但我一直未能弄清楚。此外,如果我的任何代码过于冗长,或者有更简单,更优雅的方式来完成我已经完成的工作 - 请随时对此进行评论。

解决方案

以下是获取您所描述布局的步骤: > 1)将图例提取为单独的grob(图形对象)。然后我们可以将图例与图解分开布置。

2)左对齐四个图的边,以便左边和x坐标正确排列。要做到这一点的代码来自这个SO回答。这个答案有一个函数来对齐任意数量的情节,但是当我也想改变分配给每个情节的比例空间时,我无法让它工作,所以我最终做了漫长的路分别调整每个图。

3)使用 grid.arrange 布置图和图例, arrangeGrob heights 参数为每个绘图分配总垂直空间的不同比例。我们还使用宽度参数为一个宽列中的图和另一个窄列中的图例分配水平空间。



4)绘制成任意大小的设备。这是您获得特定形状或长宽比的方式。

 库(gridExtra)
库(网格)

#从ggplot图中提取图例的函数作为单独的grob
#源:https://stackoverflow.com/a/12539820/496488
get_leg =函数(a.gplot){
tmp< - ggplot_gtable(ggplot_build(a .gplot))
leg< - which(sapply(tmp $ grobs,function(x)x $ name)==guide-box)
legend< - tmp $ grobs [[leg ]]
legend
}

#将图例作为单独的grob获取
leg = get_leg(site_status)

#添加主题元素(plot.margin = unit(c(0,0,-0.5,0),lines))

#左对齐四个图
#改编自:https://stackoverflow.com/a/13295880/496488
gA< - ggplotGrob(bar + thm)
gB< - ggplotGrob(smoke_status + thm)
gC&l t; - ggplotGrob(hpv_status + thm)
gD< - ggplotGrob(site_status + theme(plot.margin = unit(c(0,0,0,0),lines))+
(fill = FALSE))

maxWidth = grid :: unit.pmax(gA $ widths [2:5],gB $ widths [2:5],gC $ widths [2:5] ,gD $ widths [2:5])
gA $ widths [2:5]< - as.list(maxWidth)
gB $ widths [2:5]< - as.list最大宽度)
gC $ widths [2:5]< - as.list(maxWidth)
gD $ widths [2:5]< - as.list(maxWidth)

#布置图和图例
p = grid.arrange(arrangeGrob(gA,gB,gC,gD,heights = c(0.5,0.15,0.15,0.21)),
leg,ncol = 2 ,widths = c(0.8,0.2))

然后,您可以确定形状或宽高比通过设置输出设备的参数进行最终绘图。 (当您创建底层图时,您可能需要调整字体大小,以便按照您希望的方式获得最终布局。)下面粘贴的图是直接从RStudio图窗口保存的png。以下是如何将图表保存为PDF文件(但您可以使用许多其他设备(例如,png,jpeg等)以不同格式保存):

  pdf(myPlot.pdf,width = 10,height = 5)
p
dev.off()

< img src =https://i.stack.imgur.com/e1yem.pngalt =在这里输入图片描述>



你也是询问更有效的代码。你可以做的一件事就是创建一个你多次使用的绘图元素列表,然后将列表对象的名称添加到每个绘图。例如:

  my_gg = list(geom_bar(stat =identity,fill =red),
theme(legend.position =none,
axis.title.x = element_blank(),
axis.ticks.x = element_blank(),
axis.text.x = element_blank( )),
plot.margin = unit(c(0,0,-0.5,0),lines))

smoke_status = ggplot(data,aes(x = pt_id, y =吸烟))+
实验室(y =吸烟状态)+
my_gg


I am trying to write a script that produces four different plots in a single image. Specifically, I want to recreate this graphic as closely as possible:

My current script produces four plots similar to these but I cannot figure out how to allocate screen real-estate accordingly. I want to:

  1. modify the height and width of the plots so that all four have uniform width, one is substantially taller than the others which have uniform height among them
  2. define the position of the legends by coordinates so that I can use screen space effectively
  3. modify the overall shape of my image explicitly as needed (maybe I will need it closer to square-shaped at some point)

GENERATE SOME DATA TO PLOT

pt_id = c(1:279) # DEFINE PATIENT IDs
smoke = rbinom(279,1,0.5) # DEFINE SMOKING STATUS
hpv = rbinom(279,1,0.3) # DEFINE HPV STATUS
data = data.frame(pt_id, smoke, hpv) # PRODUCE DATA FRAME

ADD ANATOMICAL SITE DATA

data$site = sample(1:4, 279, replace = T)
data$site[data$site == 1] = "Hypopharynx"
data$site[data$site == 2] = "Larynx"
data$site[data$site == 3] = "Oral Cavity"
data$site[data$site == 4] = "Oropharynx"
data$site_known = 1  # HACK TO FACILITATE PRODUCING BARPLOTS

ADD MUTATION FREQUENCY DATA

data$freq = sample(1:1000, 279, replace = F)

DEFINE BARPLOT

require(ggplot2)
require(gridExtra)
bar = ggplot(data, aes(x = pt_id, y = freq)) + geom_bar(stat = "identity") +     theme(axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank()) + ylab("Number of Mutations")
# DEFINE BINARY PLOTS
smoke_status = ggplot(data, aes(x=pt_id, y=smoke, fill = "red")) + geom_bar(stat="identity") + theme(legend.position = "none", axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank()) + ylab("Smoking Status")
hpv_status = ggplot(data, aes(x=pt_id, y = hpv, fill = "red")) + geom_bar(stat="identity") + theme(legend.position = "none", axis.title.x = element_blank(), axis.ticks.x = element_blank(), axis.text.x = element_blank()) + ylab("HPV Status")
site_status = ggplot(data, aes(x=pt_id, y=site_known, fill = site)) +     geom_bar(stat="identity")

PRODUCE FOUR GRAPHS TOGETHER

grid.arrange(bar, smoke_status, hpv_status, site_status, nrow = 4)

I suspect that the functions needed to accomplish these tasks are already included in ggplot2 and gridExtra but I have not been able to figure out how. Also, if any of my code is excessively verbose or there is a simpler, more-elegant way to do what I have already done - please feel free to comment on that as well.

解决方案

Here are the steps to get the layout you describe:

1) Extract the legend as a separate grob ("graphical object"). We can then lay out the legend separately from the plots.

2) Left-align the edges of the four plots so that the left edges and the x-scales line up properly. The code to do that comes from this SO answer. That answer has a function to align an arbitrary number of plots, but I wasn't able to get that to work when I also wanted to change the proportional space allotted to each plot, so I ended up doing it the "long way" by adjusting each plot separately.

3) Lay out the plots and the legend using grid.arrange and arrangeGrob. The heights argument allocates different proportions of the total vertical space to each plot. We also use the widths argument to allocate horizontal space to the plots in one wide column and the legend in another narrow column.

4) Plot to a device in whatever size you desire. This is how you get a particular shape or aspect ratio.

library(gridExtra)
library(grid)

# Function to extract the legend from a ggplot graph as a separate grob
# Source: https://stackoverflow.com/a/12539820/496488
get_leg = function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  legend
}

# Get legend as a separate grob
leg = get_leg(site_status)

# Add a theme element to change the plot margins to remove white space between the plots
thm = theme(plot.margin=unit(c(0,0,-0.5,0),"lines"))

# Left-align the four plots 
# Adapted from: https://stackoverflow.com/a/13295880/496488
gA <- ggplotGrob(bar + thm)
gB <- ggplotGrob(smoke_status + thm)
gC <- ggplotGrob(hpv_status + thm)
gD <- ggplotGrob(site_status + theme(plot.margin=unit(c(0,0,0,0), "lines")) + 
                  guides(fill=FALSE))

maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5], gC$widths[2:5], gD$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)
gC$widths[2:5] <- as.list(maxWidth)
gD$widths[2:5] <- as.list(maxWidth)

# Lay out plots and legend
p = grid.arrange(arrangeGrob(gA,gB,gC,gD, heights=c(0.5,0.15,0.15,0.21)),
                 leg, ncol=2, widths=c(0.8,0.2))

You can then determine the shape or aspect ratio of the final plot by setting the parameters of the output device. (You may have to adjust font sizes when you create the underlying plots in order to get the final layout to look the way you want it.) The plot pasted in below is a png saved directly from the RStudio graph window. Here's how you would save the plot as PDF file (but there are many other "devices" you can use (e.g., png, jpeg, etc.) to save in different formats):

pdf("myPlot.pdf", width=10, height=5)
p
dev.off()

You also asked about more efficient code. One thing you can do is create a list of plot elements that you use multiple times and then just add the name of the list object to each plot. For example:

my_gg = list(geom_bar(stat="identity", fill="red"),
             theme(legend.position = "none", 
                   axis.title.x = element_blank(), 
                   axis.ticks.x = element_blank(), 
                   axis.text.x = element_blank()),
                   plot.margin = unit(c(0,0,-0.5,0), "lines"))

smoke_status = ggplot(data, aes(x=pt_id, y=smoke)) + 
                  labs(y="Smoking Status") +
                  my_gg

这篇关于R - 如何将屏幕空间分配给复杂的ggplot图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆