You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修复VisCollin包tableplot函数的grid视口调用,仅生成单图

问题

我有VisCollin包中的遗留函数tableplot,它用grid graphics生成表格的半图形展示,每个单元格用与值成比例的符号(带形状、填充色等属性)表示。函数通过grid视口(viewport)和布局绘制单元格标记,在R/RStudio控制台运行正常,但在.Rmd文档中,knitr会为表格的每个单元格生成一张图,必须用块选项fig.keep = "last"才能只保留最终图形。

简化后的函数代码如下:

tableplot.default <- function( ... ) {

    grid.newpage()

    #---Create Layout 1 and write main title.

    L1 <- grid.layout(2,1,heights=unit(c(3,1),c("lines","null")))
    pushViewport(viewport(layout=L1, width=0.95, height=0.98))

    pushViewport(viewport(layout.pos.row=1))            ## Push row 1 of Layout 1.
    grid.text(title, x=0.02, just=c("left", "bottom"))
    upViewport()

    #---Create Layout 2.

    L2 <- grid.layout(1,2,widths=unit(c(1,1),c("char","null")))
    pushViewport(viewport(layout.pos.row=2, layout=L2)) ## Push row 2 of Layout 1.

    #---Create Layout 3.

    L3 <- grid.layout(dim(values)[1],dim(values)[2],respect=T,just=c("left","top"))
    pushViewport(viewport(layout.pos.col=2))            ## Push col 2 of Layout 2;

    #---Push Layout 3, but with adjustments to accommodate possible partitions.

    pushViewport(viewport(layout=L3, x=0, y=1,
                    just=c(0,1),
                    width =unit(1,"npc")-unit(gap,"mm")*(length(v.parts)-1),
                    height=unit(1,"npc")-unit(gap,"mm")*(length(h.parts)-1)))

    #---Draw cellgrams.

    for (i in 1:dim(values)[1]){
        for (j in 1:dim(values)[2]){

            pushViewport(viewport(layout.pos.row=i, layout.pos.col=j))
            pushViewport(viewport(just=c(0,1), height=1, width=1,
                            x=unit(gap,"mm")*v.gaps[j],
                            y=unit(1,"npc")-unit(gap,"mm")*h.gaps[i]))

            cellgram(cell  = values[i,j,], ... )   # draw values in cell(i,j)
            if ((j==1) && (table.label == TRUE)) {
                if (side.rot==0) {grid.text(side.label[i], x=-0.04, just=1, gp=gpar(cex=label.size))}
                            else {grid.text(side.label[i], x=-0.1, just=c("center"), rot=side.rot, gp=gpar(cex=label.size))}
                }
            if ((i==1) && (table.label == TRUE)) {grid.text(top.label[j],  y=1.05, vjust=0, gp=gpar(cex=label.size))}
            upViewport()
            upViewport()
            }
        }
    popViewport(0)
    }

}

我不太理解grid视口,不知道该修改哪里,希望得到帮助。

解决方案

问题核心在于cellgram函数可能每次调用都执行了grid.newpage(),导致knitr将每个单元格的绘制识别为独立图形;同时原函数的视口管理也可优化,确保所有绘图操作在同一个grid页面完成。

1. 修改cellgram函数

打开cellgram的代码,删除其中的grid.newpage()语句。tableplot开头已调用grid.newpage()初始化绘图页面,后续所有单元格绘制都应在该页面完成,无需每次新建页面。

2. 优化tableplot的视口与绘图流程

调整视口管理逻辑,确保所有绘图操作在同一页面完成,并在函数末尾返回不可见对象避免knitr误判:

tableplot.default <- function( ... ) {
    # 初始化单个绘图页面
    grid.newpage()

    #---Create Layout 1 and write main title.
    L1 <- grid.layout(2,1,heights=unit(c(3,1),c("lines","null")))
    root_vp <- viewport(layout=L1, width=0.95, height=0.98)
    pushViewport(root_vp)

    pushViewport(viewport(layout.pos.row=1))
    grid.text(title, x=0.02, just=c("left", "bottom"))
    upViewport()

    #---Create Layout 2.
    L2 <- grid.layout(1,2,widths=unit(c(1,1),c("char","null")))
    pushViewport(viewport(layout.pos.row=2, layout=L2))

    #---Create Layout 3.
    L3 <- grid.layout(dim(values)[1],dim(values)[2],respect=T,just=c("left","top"))
    pushViewport(viewport(layout.pos.col=2))

    #---Push Layout 3 with adjustments
    plot_vp <- viewport(layout=L3, x=0, y=1,
                    just=c(0,1),
                    width =unit(1,"npc")-unit(gap,"mm")*(length(v.parts)-1),
                    height=unit(1,"npc")-unit(gap,"mm")*(length(h.parts)-1))
    pushViewport(plot_vp)

    #---Draw cellgrams.
    for (i in 1:dim(values)[1]){
        for (j in 1:dim(values)[2]){
            cell_vp <- viewport(layout.pos.row=i, layout.pos.col=j)
            pushViewport(cell_vp)
            inner_vp <- viewport(just=c(0,1), height=1, width=1,
                            x=unit(gap,"mm")*v.gaps[j],
                            y=unit(1,"npc")-unit(gap,"mm")*h.gaps[i])
            pushViewport(inner_vp)

            # 在当前视口绘图,不新建页面
            cellgram(cell  = values[i,j,], ... )

            if ((j==1) && (table.label == TRUE)) {
                if (side.rot==0) {
                    grid.text(side.label[i], x=-0.04, just=1, gp=gpar(cex=label.size))
                } else {
                    grid.text(side.label[i], x=-0.1, just=c("center"), rot=side.rot, gp=gpar(cex=label.size))
                }
            }
            if ((i==1) && (table.label == TRUE)) {
                grid.text(top.label[j],  y=1.05, vjust=0, gp=gpar(cex=label.size))
            }

            upViewport(2) # 一次性退出两个视口,简化代码
        }
    }

    # 退出所有视口
    popViewport(0)

    # 返回不可见对象,避免knitr额外输出
    invisible(NULL)
}

3. 可选:优化knitr代码块选项

若不想修改函数,可在Rmd代码块中设置fig.show = "hold",让knitr等待所有绘图操作完成后统一输出图形,替代fig.keep = "last":

```{r, fig.show = "hold"}
tableplot(your_data)
内容的提问来源于stack exchange,提问作者user101089
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 21:48:12