【问题标题】:How to apply a clipping mask to geom in a ggplot?如何将剪贴蒙版应用于ggplot中的geom?
【发布时间】:2019-01-13 21:38:54
【问题描述】:

我正在尝试将剪贴蒙版应用到 ggplot 中的几何图形以屏蔽部分数据,但保持轴、网格、其他几何图形和图例可见。我不想创建一个特定的情节,因此我不是在寻找一种解决方法,用多边形掩盖情节的某些部分。

这是我想模仿的那种设计(面具,不一定是主题,我现在怎么做):

(source)

另见this example

有人可能会争辩说,我可以过滤不包含在定义掩码的多边形中的数据。但是,虽然它适用于点,并且可以适用于多边形/线状对象,但它适用于栅格(边界不会完全遵循非垂直或非水平线)。 所以我尝试了以下方法:

library(ggplot2)
library(gridSVG)
library(grImport)

# Create a plot
p <- ggplot(diamonds[1:300,], aes(carat, price)) + geom_point(aes(colour = cut))

# And a clipping mask
pg <- polygonGrob(c(.7, 0, 0, 1, 1),
              c(0, .7, 1, 1, 0))
cp <- clipPath(pg)

我能够使用包gridSVG 来定义clipping mask,但是我很难将它应用到ggplot 对象上,即使在使用ggplotGrob() 提取grob(参见资源here)之后也是如此。我无法将剪贴蒙版应用到 grob:

g <-  ggplotGrob(p) # store the plot as a grob

registerClipPath("mask", cp)
g_clipped <- clipPath(g)

gridsvg(name = "test_c2.svg")
grid.draw(clipPathGrob(g_clipped, cp)$grob)
dev.off()

我的直觉是应该绘制g_clipped,但我不能grid.draw() 它,因为它是一个clipPath 对象。还有grid.draw() 此处写的行显示未屏蔽的情节。我想我不太了解 clipPath 对象的功能。

函数grobify() 听起来它可以帮助没有gridSVG 的替代方法,请参阅details here,但我不理解非常简约的文档。

由于我什至无法将剪贴蒙版应用于整个情节,因此我的目标还很遥远。

如果您能帮助我了解如何从 gridSVG 应用剪贴蒙版或有其他解决方案将剪贴蒙版应用于特定几何图形,请告诉我。

【问题讨论】:

    标签: r ggplot2 mask clipping grob


    【解决方案1】:

    以下是一个网格解决方案,但在很大程度上是一种变通方法。它展示了如何将非矩形裁剪区域应用于 ggplot,以便裁剪图中的一组点。你的尝试并没有太大的错误。需要注意的几点:

    1. 您需要grid.force() ggplotGrob 对象,以便grid 可以看到grobs。
    2. 不要将 ggplot grob 定义为剪切路径 - 剪切路径是多边形。
    3. 剪切路径应用于 ggplot 绘图面板中的点 grob。这意味着绘图面板中的其他对象、面板背景和网格线不会被剪裁。仅裁剪数据点。

    我在绘图中添加了一条蓝线,以表明该线也不需要剪裁;但可以根据需要剪裁。

    还有注释的代码行,当不注释时,将绘制剪切区域,并将网格线和点移动到前面(即在较深灰色剪切区域的前面)。

    library(ggplot2)
    library(gridSVG)
    library(grid)
    
    # Open the graphics device
    gridsvg(name = "test.svg")
    
    # Create a plot
    p <- ggplot(diamonds[1:300, ], aes(carat, price)) + 
           geom_point(aes(colour = cut)) +
           geom_line(data = data.frame(x = c(.3, .9), y = c(500, 2500)), aes(x,y), col = "skyblue", size = 2)
    g <- ggplotGrob(p) # Store the plot as a grob
    
    
    g = grid.force(g)  # So that grid sees all grobs
    grid.draw(g)       # Draw the plot
    
    # Define the clipping path
    pg <- polygonGrob(c(.7, 0, 0, 1, 1),
                      c(0, .7, 1, 1, 0))
    # The clipping path can be nearly any shape you desire. 
    # Try this for a circular region
    # pg = circleGrob(x = .5, y = .6, r = .5)
    cp <- clipPath(pg)
    
    # Add the clipping path to the points grob.
    # That is, only the points inside the polygon will be visible,
    # but the background and grid lines will not be clipped. 
    # Nor will the blue line be clipped.
    # grid.ls(g)     # names of the grobs
    seekViewport(grep("panel.[0-9]", grid.ls(g)$name, value = TRUE))
    grid.clipPath("points", cp, grep = TRUE)   
    
    # To clip the blue line, uncomment the next line
    # grid.clipPath("GRID.polyline", cp, grep = TRUE)       
    
    # To show the clipping region,    
    # uncomment the next two lines.
    # showcp = editGrob(pg, gp = gpar(fill = rgb(0, 0, 0, 0.05), col = "transparent"))
    # grid.draw(showcp)
    
    # And to move the grid lines, remaining data points, and blue line in front of the clipping region,
    # uncomment the next five lines
    # panel = grid.get("panel", grep = TRUE)   # Get the panel, and remove the background grob
    # panel = removeGrob(panel, "background", grep = TRUE)
    
    # grid.remove("points", grep = TRUE)     # Remove points and grid lines from the rendered plot
    # grid.remove("line", grep = TRUE, global = TRUE)
    
    # grid.draw(panel)     # Draw the edited panel - on top of the clipping region 
    
    
    # Turn off the graphics device
    dev.off()
    
    # Find text.svg in your working directory
    




    编辑使用绘制数据点的坐标系定义剪辑区域。

    library(ggplot2)
    library(gridSVG)
    library(grid)
    
    # Open the graphics device
    gridsvg(name = "test.svg")
    
    # Create a plot
    p <- ggplot(diamonds[1:300, ], aes(carat, price)) + 
           geom_point(aes(colour = cut)) +
           geom_line(data = data.frame(x = c(.3, .9), y = c(500, 2500)), aes(x,y), col = "skyblue", size = 2)
    g <- ggplotGrob(p) # Store the plot as a grob
    
    
    g = grid.force(g)  # So that grid sees all grobs
    grid.draw(g)       # Draw the plot
    
    # Get axis limits (including any expansion)
    axis.limits = summarise_layout(ggplot_build(p))[1, c('xmin', 'xmax', 'ymin', 'ymax')]
    
    # Find the 'panel' viewport,
    # then push to a new viewport, 
    # one that exactly overlaps the 'panel' viewport,
    # but with limits on the x and y scales that are the same
    # as the limits for the original ggplot. 
    seekViewport(grep("panel.[0-9]", grid.ls(g)$name, value = TRUE))
    pushViewport(dataViewport(xscale = axis.limits[1, 1:2],
                              yscale = axis.limits[1, 3:4]))
    
    # Define the clipping path
     pg <- polygonGrob(x = c(.6,   0.3, .3,   .8,   1.2), 
                       y = c(500, 1500, 2900, 2900, 1500), 
                       default.units="native")
    cp <- clipPath(pg)
    
    # Add the clipping path to the points grob.
    # That is, only the points inside the polygon will be visible,
    # but the background and grid lines will not be clipped. 
    # Nor will the blue line be clipped.
    # grid.ls(g)     # names of the grobs
    
    grid.clipPath("points", cp, grep = TRUE)   
    
    # To clip the blue line, uncomment the next line
     grid.clipPath("GRID.polyline", cp, grep = TRUE)       
    
    # To show the clipping region. 
     showcp = editGrob(pg, gp = gpar(fill = rgb(0, 0, 0, 0.05), col = "transparent"))
     grid.draw(showcp)
    
    # And to move the grid lines and remaining data points in front of the clipping region.
     panel = grid.get("panel", grep = TRUE)   # Get the panel, and remove the background grob
     panel = removeGrob(panel, "background", grep = TRUE)
    
     grid.remove("points", grep = TRUE)     # Remove points and grid lines from the rendered plot
     grid.remove("line", grep = TRUE, global = TRUE)
    
     grid.draw(panel)     # Draw the edited panel - on top of the clipping region 
    
    
    # Turn off the graphics device
    dev.off()
    
    # Find text.svg in your working directory
    

    【讨论】:

    • 感谢您的回答,它使事情变得更清晰并打开了选项。要清楚,裁剪多边形的坐标在视口坐标系中?另外,通过修改 grep 函数的参数,我可以选择我想要的几何图形(在这里你选择面板的所有元素,因此选择所有几何图形?)?
    • 第一个问题。坐标以“npc”为单位:(0,0) 是左下角,(1,1) 是右上角。但是可以使用绘制数据的坐标来定义剪切区域。我将添加一个编辑来展示它是如何完成的。
    • 第二个问题。不完全的。在grid 中,视口是绘制grobs 的区域。我使用一个技巧来选择视口。 'panel' 视口(即绘制所有面板 grob 的视口)与 'panel' grob 具有相同的名称。我本可以使用current.vpTree() 来获取视口名称,但没有很好地设置,并且更难提取所需的视口名称。
    • 第二个问题继续。 seekViewport 推荐意味着我移动了绘制所有面板 grobs 的视口:grid.lines、背景、数据点和蓝线。在这个阶段我没有选择任何 grobs(或 geoms)。
    • 感谢所有有用的信息。下周晚些时候我会看山雀,因为我现在没有太多时间,如果我有其他问题可能会回来找你(你不介意回答他们)。你有很大的帮助。再次感谢。
    【解决方案2】:

    由于您从 ggplot 对象开始,将遮罩本身创建为 geom 层可能更简单,而不是将所有内容转换为 grob 并在那里的网格系统中工作。

    这里可以使用 ggpolypath 包中的 geom_polypath() 函数。与 ggplot2 中的标准 geom_polygon 不同,它能够处理带孔的多边形(参见 vignette):

    # sample data frame for clipping. The first four x & y coordinates are for the outer ends;
    # the next four are for the hole in the polygon.
    clipping.df <- data.frame(x = c(0, 1.5, 1.5, 0, 0.2, 1, 0.7, 0.3),
                              y = c(0, 0, 3000, 3000, 250, 2000, 2800, 1500),
                              hole = rep(c(FALSE, TRUE), each = 4),
                              group = rep(c("1", "2"), each = 4))
    
    library(ggpolypath)
    p +
      geom_polypath(data = clipping.df,
                    aes(x = x, y = y, group = group),
                    colour = NA, fill = "black", alpha = 0.5,
                    inherit.aes = FALSE) +
      scale_x_continuous(expand = c(0, 0)) + # don't show edges beyond the extent
      scale_y_continuous(expand = c(0, 0))   # of the polygon
    

    【讨论】:

    • 感谢您的想法。它可能适用于某些情况,但是,它并没有达到我的目标。该方法的两个主要限制是:(1)它掩盖了网格,(2)它掩盖了 geom 下的每个薄层,而剪贴蒙版方法允许您掩盖某些图层,但不能掩盖下面的图层。我会继续 digind ;)
    • 也许您可以提供一个示例来展示您的理想输出?至于您的第一个问题,theme() 有一个 panel.ontop 选项,它将网格放置在数据层上。
    • 也许this image 更好地代表了剪切蒙版的兴趣(此处应用于灰色区域)。我不知道,所以谢谢你。然而,这仍然不能完全令人满意,因为网格现在会重叠所有内容,而剪贴蒙版可以让几何图形保持在网格前面。因此,我真的在寻找以一致且结构化的方式将剪贴蒙版应用于几何图形,而不是生成特定的一次性图的解决方法。
    猜你喜欢
    • 2011-06-23
    • 1970-01-01
    • 2016-07-07
    • 1970-01-01
    • 2012-07-07
    • 2011-10-20
    • 2018-08-10
    • 1970-01-01
    • 2021-02-20
    相关资源
    最近更新 更多