【问题标题】:R adding a datatable to a ggplot graph using viewPorts : Scaling the GrobR 使用 viewPorts 将数据表添加到 ggplot 图:缩放 Grob
【发布时间】:2026-01-22 19:40:01
【问题描述】:

我正在尝试将数据表添加到用 ggplot 制作的图表中(类似于 excel 功能,但可以灵活地更改其轴)

我已经尝试了几次,但一直遇到缩放问题,所以尝试 1) 是

library(grid)
library(gridExtra)
library(ggplot2)
xta=data.frame(f=rnorm(37,mean=400,sd=50))
xta$n=0
for(i in 1:37){xta$n[i]<-paste(sample(letters,4),collapse='')}
xta$c=0
for(i in 1:37){xta$c[i]<-sample((1:6),1)}
rect=data.frame(xmi=seq(0.5,36.5,1),xma=seq(1.5,37.5,1),ymi=0,yma=10)
xta=cbind(xta,rect)
a = ggplot(data=xta,aes(x=n,y=f,fill=c)) + geom_bar(stat='identity')
b = ggplot(data=xta,aes(x=n,y=5,label=round(f,1))) + geom_text(size=4) + geom_rect(aes(xmin=xmi,xmax=xma,ymin=ymi,ymax=yma),alpha=0,color='black')
z = theme(axis.text=element_blank(),panel.background=element_rect(fill='white'),axis.ticks=element_blank(),axis.title=element_blank())
b=b+z
la=grid.layout(nrow=2,ncol=1,heights=c(0.15,2),default.units=c('null','null'))
grid.show.layout(la)
grid.newpage()
pushViewport(viewport(layout=la))
print(a,vp=viewport(layout.pos.row=2,layout.pos.col=1))
print(b,vp=viewport(layout.pos.row=1,layout.pos.col=1))

产生了

第二次尝试 2) 是

xta1=data.frame(t(round(xta$f,1)))
xtb=tableGrob(xta1,show.rownames=F,show.colnames=F,show.vlines=T,gpar.corefill=gpar(fill='white',col='black'),gp=gpar(fontsize=12),vp=viewport(layout.pos.row=1,layout.pos.col=1))
grid.newpage()
la=grid.layout(nrow=2,ncol=1,heights=c(0.15,2),default.units=c('null','null'))
grid.show.layout(la)
grid.newpage()
pushViewport(viewport(layout=la))
print(a,vp=viewport(layout.pos.row=2,layout.pos.col=1))
grid.draw(xtb)

生产的

最后 3) 是

grid.newpage()
print(a + annotation_custom(grob=xtb,xmin=0,xmax=37,ymin=450,ymax=460))

产生了

如果我可以将 tableGrob 缩放到与绘图相同的大小,其中选项 2 将是最好的,但我不知道该怎么做。关于如何更进一步的任何指示? - 谢谢

【问题讨论】:

    标签: r ggplot2 viewport gridextra r-grid


    【解决方案1】:

    你可以试试新版tableGrob;生成的 gtable 宽度/高度可以设置为特定大小(这里是等分布的 npc 单位)

    library(ggplot2)
    library(gridExtra)
    library(grid)
    tg <- tableGrob(head(iris), rows=NULL)
    tg$widths <- unit(rep(1/ncol(tg),ncol(tg)),"npc")
    tg$heights <- unit(rep(1/nrow(tg),nrow(tg)),"npc")
    
    qplot(colnames(iris), geom="bar")+ theme_bw() +
      scale_x_discrete(expand=c(0,0)) +
      scale_y_continuous(lim=c(0,2), expand=c(0,0)) +
      annotation_custom(ymin=1, ymax=2, xmin=-Inf, xmax=Inf, tg)
    

    【讨论】:

    • 感谢@baptiste 我还没有机会对此进行测试。周末会这样做,但这看起来是正确的答案。也感谢一个了不起的包
    • 一个简单的问题@baptiste 我每次都必须加载source_gist 吗?您预计实验性的 tableGrob 何时会进入主流代码?
    • 如果您觉得它有用,我的建议是复制代码并自己在本地加载(在包中,或在您的 .Rprofile 中)。我真的不能保证任何发布日期,因为我很少在这个包上工作,而且这个新版本是高度实验性的。如果它适合你,它应该很容易适应自定义颜色等。
    【解决方案2】:

    例如,您可以使用 ggplot 创建的表格并将它们与 blog 中的类似内容组合在一起。我在这里做了一个简化的工作示例:

    首先制作你的情节:

    library(ggplot2)
    library(reshape2)
    library(grid)
    
     df <- structure(list(City = structure(c(2L,
         3L, 1L), .Label = c("Minneapolis", "Phoenix",
         "Raleigh"), class = "factor"), January = c(52.1,
         40.5, 12.2), February = c(55.1, 42.2, 16.5),
         March = c(59.7, 49.2, 28.3), April = c(67.7,
             59.5, 45.1), May = c(76.3, 67.4, 57.1),
         June = c(84.6, 74.4, 66.9), July = c(91.2,
             77.5, 71.9), August = c(89.1, 76.5,
             70.2), September = c(83.8, 70.6, 60),
         October = c(72.2, 60.2, 50), November = c(59.8,
             50, 32.4), December = c(52.5, 41.2,
             18.6)), .Names = c("City", "January",
         "February", "March", "April", "May", "June",
         "July", "August", "September", "October",
         "November", "December"), class = "data.frame",
         row.names = c(NA, -3L))
    
    dfm <- melt(df, variable = "month")
    
     levels(dfm$month) <- month.abb
     p <- ggplot(dfm, aes(month, value, group = City,
         colour = City))
     p1 <- p + geom_line(size = 1) + theme(legend.position = "top") + xlab("")
    

    接下来在 ggplot 中生成数据表。使用与绘图相同的 x 轴:

    none <- element_blank()
    data_table <- ggplot(dfm, aes(x = month, y = factor(City),
         label = format(value, nsmall = 1), colour = City)) +
         geom_text(size = 3.5) +
      scale_y_discrete(labels = abbreviate)+ theme_bw()  +
         theme(panel.grid.major = none, legend.position = "none",
             panel.border = none, axis.text.x = none,
             axis.ticks = none) + theme(plot.margin = unit(c(-0.5,
         1, 0, 0.5), "lines")) + xlab(NULL) + ylab(NULL)
    

    将两者与视口结合起来:

    Layout <- grid.layout(nrow = 2, ncol = 1, heights = unit(c(2,
         0.25), c("null", "null")))
    grid.show.layout(Layout)
    vplayout <- function(...) {
         grid.newpage()
         pushViewport(viewport(layout = Layout))
     }
    
    subplot <- function(x, y) viewport(layout.pos.row = x,
         layout.pos.col = y)
    
    mmplot <- function(a, b) {
         vplayout()
         print(a, vp = subplot(1, 1))
         print(b, vp = subplot(2, 1))
     }
    
    mmplot(p1, data_table)
    

    请注意,仍然需要进行一些调整,例如情节图例的位置和表格中城市名称的缩写,但结果看起来不错:

    应用于您的示例:

    library(grid)
    library(gridExtra)
    library(ggplot2)
    xta=data.frame(f=rnorm(37,mean=400,sd=50))
    xta$n=0
    for(i in 1:37){xta$n[i]<-paste(sample(letters,4),collapse='')}
    xta$c=0
    for(i in 1:37){xta$c[i]<-sample((1:6),1)}
    rect=data.frame(xmi=seq(0.5,36.5,1),xma=seq(1.5,37.5,1),ymi=0,yma=10)
    xta=cbind(xta,rect)
    a = ggplot(data=xta,aes(x=n,y=f,fill=c)) + geom_bar(stat='identity')+ theme(legend.position = "top")+xlab("")
    
    none <- element_blank()
    z=ggplot(xta, aes(x = n, y = "fvalues",
         label = round(f,1)) )+
         geom_text(size = 3)+ theme_bw()  +
         theme(panel.grid.major = none, legend.position = "none",
             panel.border = none, axis.text.x = none,
             axis.ticks = none) + theme(plot.margin = unit(c(-0.5,
         1, 0, 0.5), "lines")) + xlab(NULL) + ylab(NULL)
    
    Layout <- grid.layout(nrow = 2, ncol = 1, heights = unit(c(2,
         0.25), c("null", "null")))
    grid.show.layout(Layout)
    vplayout <- function(...) {
         grid.newpage()
         pushViewport(viewport(layout = Layout))
     }
    
    subplot <- function(x, y) viewport(layout.pos.row = x,
         layout.pos.col = y)
    
    mmplot <- function(a, b) {
         vplayout()
         print(a, vp = subplot(1, 1))
         print(b, vp = subplot(2, 1))
     }
    
    mmplot(a, z)
    

    编辑:

    类似于丹尼斯他的解决方案,但不是条形图和+ coord_flip()。如果你不想翻转它,你可以删除后者,但它会增加可读性:

    ggplot(xta, aes(x=n,y=f,fill=c)) +
       geom_bar() +
       labs(color = "c") +
       geom_text(aes(y = max(f)+30, label = round(f, 1)), size = 3, color = "black") + coord_flip()
    

    【讨论】:

    • 谢谢@JT85 这很有用,我确实看到了那个博客。我的第一次尝试(不使用 tableGrob)本质上就是这个过程,但删除了函数调用并在数字周围设置了矩形。我知道如果我移动图例我可以让它工作,但我试图将 grob 调整为图形而不是更改图形以允许显示数据。不过,这几乎是我所需要的。
    • 我为答案添加了另一个解决方案。
    • 感谢@JT85。这绝对是一个解决方案,但如果可能的话,我想获得一个实际的数据表。如果您不介意,我会推迟将其标记为答案,因为它仍然看起来像注释,而不是值的不同表。我有明确的外观和感觉。
    【解决方案3】:

    恕我直言,这不是一个精心设计的图形。首先,我不明白为什么在值范围从 300 到 500 时需要零原点,这是一种变相的说法,我不喜欢条形图的比喻。您还尝试使用条形填充来表示 c 值的差异,其中只有六个。这是我认为解决问题的一种更简单的方法。鉴于您的 xta 数据,

    # Convert the categories to a factor
    xta$N <- factor(xta$n, levels = xta$n)
    
    # Simple approach:
    ggplot(xta, aes(x = f, y = N, color = factor(c))) +
       geom_point() +
       labs(color = "c") +
       geom_text(aes(x = 575, label = round(f, 1)), size = 4, color = "black")
    

    这对我来说不是一个有趣的图形。根据问题的上下文,可能会增加一点洞察力的方法是按递增顺序对响应进行排序,并添加尺寸美学以标出 c 级别之间的差异。 (您也可以使用不带颜色的大小。)最后,因为我们将因子水平放在垂直轴上以便它们的标签清晰可见,所以我们还可以通过稍微延长水平轴将 f 值插入为文本。

    ggplot(xta, aes(x = f, y = reorder(N, f), color = factor(c), size = c)) + 
       geom_point() +
       labs(color = "c") +
       geom_text(aes(x = 575, label = round(f, 1)), size = 4, color = "black")
    

    这段代码中有足够的提示可以让您将其引向不同的方向。我将把它留给你。

    【讨论】:

    • 感谢@Dennis 的输入。实际数据经过排序,数据集更适合使用条形图。我考虑了点图,但对于我的观众来说,我希望它只会混淆问题,因为他们不熟悉图形。我很感激你的意见。我将在同一个项目中使用点图作为单独的视觉辅助。