在ggplot2中将面板标签更改为数学公式

18

我想知道如何在 ggplot2 中将 facet 标签更改为数学公式。

d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
  xlim(0, 2) + stat_binhex(na.rm = TRUE) + opts(aspect.ratio = 1)
d + facet_wrap(~ color, ncol = 4)

enter image description here

比如说,我想把facet标签从D改成带下标的Y[1]。非常感谢你们提前的帮助。

我找到了这个回答,但对我没有用。我正在使用R 2.15.1ggplot2 0.9.1


2
请参考:http://had.co.nz/ggplot2/docs/label_parsed.html。您需要重命名标签,然后使用“label = parsed”参数。 - Tyler Rinker
@TylerRinker在这里行不通,因为facet_wrap没有labeller参数。只有facet_grid有,我不知道为什么。 - Luciano Selzer
希望facet_wrap()不是必须的要求。 - Tommy O'Dell
1
请参见 https://github.com/hadley/ggplot2/issues/25 - Brian Diggs
5个回答

24
您可以在 gtable 中编辑 grobs。
ggplot(diamonds, aes(carat, price, fill = ..density..)) +
  xlim(0, 2) + stat_binhex(na.rm = TRUE) + facet_wrap(~ color, ncol = 4)


for(ii in 1:7)
grid.gedit(gPath(paste0("strip_t-", ii), "strip.text"), 
           grep=TRUE, label=bquote(gamma[.(ii)]))

在此输入图片描述

或者,如果您想保存grob,

g <- ggplotGrob(d)
gg <- g$grobs

strips <- grep("strip_t", names(gg))
for(ii in strips)
  gg[[ii]] <- editGrob(getGrob(gg[[ii]], "strip.text", 
                               grep=TRUE, global=TRUE), 
                       label=bquote(gamma[.(ii)]))

g$grobs <- gg

使用ggsave需要额外(丑陋的)工作,因为必须欺骗类ggplot的测试……我认为明确调用pdf(); grid.draw(g); dev.off()会更容易。


Roland编辑:

我进行了一些小的修改,并将其包装到一个函数中:

facet_wrap_labeller <- function(gg.plot,labels=NULL) {
  #works with R 3.0.1 and ggplot2 0.9.3.1
  require(gridExtra)
  
  g <- ggplotGrob(gg.plot)
  gg <- g$grobs      
  strips <- grep("strip_t", names(gg))
    
  for(ii in seq_along(labels))  {
    modgrob <- getGrob(gg[[strips[ii]]], "strip.text", 
                       grep=TRUE, global=TRUE)
    gg[[strips[ii]]]$children[[modgrob$name]] <- editGrob(modgrob,label=labels[ii])
  }
  
  g$grobs <- gg
  class(g) = c("arrange", "ggplot",class(g)) 
  g
}

这样可以漂亮地打印,甚至可以使用ggsave


2
@Roland,我的替代解决方案(“编辑3”)涉及到这个确切的问题。 我的答案中的class(g) <- c("facetAdjust", "gtable", "ggplot")print.facetAdjust可以在这里提供帮助。 - Julius Vainora
2
@Roland 如果您使用gridExtra包,您可以使用class(g) = c("arrange", "ggplot",class(g)),因为print.arrange方法将与ggsave一起工作。 - baptiste
奇怪,你是否已经加载了gridExtra? - baptiste
是的,我在函数内部更改了那个。 - Roland
我没有遇到这个错误,但趁机修复了你在函数中的一个拼写错误。 - baptiste
显示剩余8条评论

13

也许有人在某个时候更改了edit-Grob函数的名称。 (编辑:大约8个月前,@hadley将其删除。) 现在没有geditGrob,但是来自pkg:grid的editGrob似乎可以正常工作:

 d <- ggplot(diamonds, aes(carat, price, fill = ..density..)) +
              xlim(0, 2) + stat_binhex(na.rm = TRUE) + opts(aspect.ratio = 1)

 #Note: changes in ggplot2 functions cause this to fail from the very beginning now.
 # Frank Harrell's answer this year suggests `facet_warp` now accepts `labeller`


 d <- d + facet_wrap(~ color, ncol = 4)
 grob <- ggplotGrob(d)
 strip_elem <- grid.ls(getGrob(grob, "strip.text.x", grep=TRUE, global=TRUE))$name
#strip.text.x.text.1535
#strip.text.x.text.1541
#strip.text.x.text.1547
#strip.text.x.text.1553
#strip.text.x.text.1559
#strip.text.x.text.1565
#strip.text.x.text.1571
grob <- editGrob(grob, strip_elem[1], label=expression(Y[1]))
grid.draw(grob)

9
截至ggplot 0.9.2版本,这个答案已经不再适用(至少对我来说是如此)。strip_elem <- grid.ls(getGrob(grob, "strip.text.x", grep=TRUE, global=TRUE))$name会返回Error in getGrob(grob, "strip.text.x", grep = TRUE, global = TRUE) : It is only valid to get a child from a 'gTree'。你有任何想法让它工作吗? - Tyler Rinker

9

ggplot2 2.1.0版本开始,labeller已经为facet_wrap实现。


8

我刚刚发现了Roland和Baptiste的一个非常有用的函数,但需要一个略微不同的用例,原始的包装标题应该由一个函数转换而不是作为固定值提供。如果对其他人有用,我会发布稍微修改过的原始函数版本。它允许使用命名(固定值)表达式来包装条带,以及使用自定义函数和ggplot2已提供的函数来处理facet_grid labeller参数(例如label_parsedlabel_bquote)。

facet_wrap_labeller <- function(gg.plot, labels = NULL, labeller = label_value) {
  #works with R 3.1.2 and ggplot2 1.0.1
  require(gridExtra)

  # old labels
  g <- ggplotGrob(gg.plot)
  gg <- g$grobs      
  strips <- grep("strip_t", names(gg))
  modgrobs <- lapply(strips, function(i) {
    getGrob(gg[[i]], "strip.text", grep=TRUE, global=TRUE)
  })
  old_labels <- sapply(modgrobs, function(i) i$label)

  # find new labels
  if (is.null(labels)) # no labels given, use labeller function
    new_labels <- labeller(names(gg.plot$facet$facets), old_labels)
  else if (is.null(names(labels))) # unnamed list of labels, take them in order
    new_labels <- as.list(labels)
  else { # named list of labels, go by name where provided, otherwise keep old
    new_labels <- sapply(as.list(old_labels), function(i) {
      if (!is.null(labels[[i]])) labels[[i]] else i
    })
  }

  # replace labels
  for(i in 1:length(strips))  {
    gg[[strips[i]]]$children[[modgrobs[[i]]$name]] <- 
       editGrob(modgrobs[[i]], label=new_labels[[i]])
  }

  g$grobs <- gg
  class(g) = c("arrange", "ggplot",class(g))
  return(g) 
}

更新/警告

对于gridExtra包的较新版本,如果运行此函数,则会收到错误信息Error: No layers in plot,因为arrange不再在gridExtra中,并且R试图将其解释为ggplot。你可以通过重新引入arrange类的print函数来解决此问题:

print.arrange <- function(x){
    grid::grid.draw(x)
}

现在这样做可以使图表渲染,您可以像这样使用 ggsave()ggsave("test.pdf", plot = facet_wrap_labeller(p, labeller = label_parsed))

示例

一些用例示例:

# artificial data frame
data <- data.frame(x=runif(16), y=runif(16), panel = rep(c("alpha", "beta", "gamma","delta"), 4))
p <- ggplot(data, aes(x,y)) + geom_point() + facet_wrap(~panel)

# no changes, wrap panel headers stay the same
facet_wrap_labeller(p) 

# replace each panel title statically
facet_wrap_labeller(p, labels = expression(alpha^1, beta^1, gamma^1, delta^1)) 

# only alpha and delta are replaced
facet_wrap_labeller(p, labels = expression(alpha = alpha^2, delta = delta^2)) 

# parse original labels
facet_wrap_labeller(p, labeller = label_parsed) 

# use original labels but modifying them via bquote
facet_wrap_labeller(p, labeller = label_bquote(.(x)^3)) 

# custom function (e.g. for latex to expression conversion)
library(latex2exp)
facet_wrap_labeller(p, labeller = function(var, val) { 
  lapply(paste0("$\\sum\\", val, "$"), latex2exp)
}) 

嗯,每当我调用facet_wrap_labeller函数时,就会出现“错误:绘图中没有层”的提示。我正在加载相同版本的ggplot2(1.0.1)...尽管我的R版本更新(3.2.2)。还有其他人遇到同样的问题吗? - Grant
你是对的,谢谢你的反馈。据我所知,问题在于最新版本的gridExtra(安装了R 3.2.2之后会得到)不再具有arrange类,因此它尝试将其绘制为ggplot,但它不再是ggplot,而是已经是gtable/grob对象,所以它会失败并出现您观察到的错误消息。我不知道是否有一种使用gridExtra的新方法,也许@baptiste有想法?目前,您可以执行以下操作:删除行class(g) = c("arrange", "ggplot",class(g))并在您的图形上使用grid.arrange(facet_wrap_labeller(p)) - sebkopf
@Grant,算了,重新引入print.arrange更容易。我已经相应地更新了答案,基本上只需添加:print.arrange <- function(x){ grid::grid.draw(x) } - sebkopf

2
感谢其他回答和评论提到了 label_parsed。虽然还不是很清楚如何使用标签解析器,但我在这里添加了一个简单的可重现示例。
library(dplyr)
library(ggplot2)

# Create a first facet variable with examples of math formulas
iris2 <- iris %>%
    mutate(species_math = factor(Species,
                            levels = c("setosa", "versicolor", "virginica"),
                            labels = c("m^2",
                                       expression(bar(x) == sum(frac(x[i], n), i==1, n) * beta * Q[t-1]),
                                       bquote(pi == .(pi)))))

# Create a second facet variable with mean lengths
# This illustrates how to pass a numeric vector inside a formula
iris_mean <- iris2 %>%
    group_by(Species) %>%
    summarise(across(ends_with("Length"), mean), .groups="drop")

iris2$mean_length <- factor(iris2$Species,
                           levels =  c("setosa", "versicolor", "virginica"),
                           labels = mapply(function(p, s) bquote(bar(p) == .(p) ~ bar(s) ==.(s)),
                                            round(iris_mean$Petal.Length,3), round(iris_mean$Sepal.Length,3)))


iris2 %>%
    ggplot(aes(x = Petal.Length, y = Petal.Width)) +
    geom_point() +
    facet_wrap(species_math ~ mean_length + Species, labeller = labeller(species_math = label_parsed, mean_length = label_parsed))

ggsave("~/downloads/formula_in_facet.png",
       width = 12, height = 8, units = "cm")

enter image description here

如上例所示,标签器可以解析以下内容:
  1. 字符向量,例如“m^2”用于简单公式
  2. expression,用于带有指数的更复杂的数学表达式
  3. bquote 的输出,以包括公式中的数字值。另请参见this answer,了解如何将bquote与多个值的数字向量一起使用。
  4. 请参阅this other answer,了解如何仅将标签器应用于一个分面变量。在我们的案例中,我们仅将其应用于species_math变量。
语法与Latex数学公式不同,因为label_parsed将标签解释为plotmath表达式。例如,在Latex中,指数写作x_i,而在绘图数学表达式中写作x[i],希腊字母直接写作alpha,而不是在Latex中的\alpha。您可以在plotmath函数的帮助页面中找到许多公式。祝您在plotmath示例中好运。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接