首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用ggplot2动态包装刻面标签

如何使用ggplot2动态包装刻面标签
EN

Stack Overflow用户
提问于 2013-05-21 01:14:06
回答 4查看 18.1K关注 0票数 37

我正在寻找一种在facet_wrapfacet_grid调用中动态包装条带标签文本的方法。我已经找到了一种使用strwrap来完成此任务的方法,但是我需要指定一个width才能使输出按预期方式工作。通常,面的数量事先是未知的,因此此方法要求我根据数据集和绘图大小迭代地调整width参数。是否可以动态指定wrap函数的宽度,或者是否有另一种更好的方法来标记facet?

library(ggplot2)
df = expand.grid(group=paste(c("Very Very Very Long Group Name "), 1:9),
                 x=rnorm(5), y=rnorm(5), stringsAsFactors=FALSE)

df$groupwrap = unlist(lapply(strwrap(df$group, width=30, simplify=FALSE), paste, 
                             collapse="\n"))
p = ggplot(df) +
  geom_point(aes(x=x, y=y)) +
  facet_wrap(~groupwrap)

更新:基于@baptiste和@thunk提供的指导,我提出了下面的选择。目前,它只适用于指定的字体系列和大小,但理想情况下,还应该能够使用默认的theme设置。也许有更多ggplot2经验的人会有一些改进的建议。

library('grid')
grobs <- ggplotGrob(p)

sum = sum(sapply(grobs$width, function(x) convertWidth(x, "in")))
panels_width = par("din")[1] - sum  # inches

df$group = as.factor(df$group)
npanels = nlevels(df$group)
if (class(p$facet)[1] == "wrap") {
  cols = n2mfrow(npanels)[1]
} else {
  cols = npanels
}

ps = 12
family = "sans"
pad = 0.01  # inches
panel_width = panels_width / cols
char_width = strwidth(levels(df$group)[
  which.max(nchar(levels(df$group)))], units="inches", cex=ps / par("ps"), 
                      family=family) / max(nchar(levels(df$group)))
width = floor((panel_width - pad)/ char_width)  # characters

df$groupwrap = unlist(lapply(strwrap(df$group, width=width, simplify=FALSE), 
                             paste, collapse="\n"))
ggplot(df) +
  geom_point(aes(x=x, y=y)) +
  facet_wrap(~groupwrap) +
  theme(strip.text.x=element_text(size=ps, family=family))
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-05-24 21:11:59

感谢@baptiste和@thunk的指导,我创建了下面的函数,它似乎在自动包装facet标签方面做得很好。不过,改进的建议总是受欢迎的。

strwrap_strip_text = function(p, pad=0.05) { 
  # get facet font attributes
  th = theme_get()
  if (length(p$theme) > 0L)
    th = th + p$theme

  require("grid")
  grobs <- ggplotGrob(p)

  # wrap strip x text
  if ((class(p$facet)[1] == "grid" && !is.null(names(p$facet$cols))) ||
        class(p$facet)[1] == "wrap")
  {
    ps = calc_element("strip.text.x", th)[["size"]]
    family = calc_element("strip.text.x", th)[["family"]]
    face = calc_element("strip.text.x", th)[["face"]]

    if (class(p$facet)[1] == "wrap") {
      nm = names(p$facet$facets)
    } else {
      nm = names(p$facet$cols)
    }

    # get number of facet columns
    levs = levels(factor(p$data[[nm]]))
    npanels = length(levs)
    if (class(p$facet)[1] == "wrap") {
      cols = n2mfrow(npanels)[1]
    } else {
      cols = npanels
    }

    # get plot width
    sum = sum(sapply(grobs$width, function(x) convertWidth(x, "in")))
    panels_width = par("din")[1] - sum  # inches
    # determine strwrap width
    panel_width = panels_width / cols
    mx_ind = which.max(nchar(levs))
    char_width = strwidth(levs[mx_ind], units="inches", cex=ps / par("ps"), 
                          family=family, font=gpar(fontface=face)$font) / 
      nchar(levs[mx_ind])
    width = floor((panel_width - pad)/ char_width)  # characters

    # wrap facet text
    p$data[[nm]] = unlist(lapply(strwrap(p$data[[nm]], width=width, 
                                         simplify=FALSE), paste, collapse="\n"))
  }

  if (class(p$facet)[1] == "grid" && !is.null(names(p$facet$rows))) {  
    ps = calc_element("strip.text.y", th)[["size"]]
    family = calc_element("strip.text.y", th)[["family"]]
    face = calc_element("strip.text.y", th)[["face"]]

    nm = names(p$facet$rows)

    # get number of facet columns
    levs = levels(factor(p$data[[nm]]))
    rows = length(levs)

    # get plot height
    sum = sum(sapply(grobs$height, function(x) convertWidth(x, "in")))
    panels_height = par("din")[2] - sum  # inches
    # determine strwrap width
    panels_height = panels_height / rows
    mx_ind = which.max(nchar(levs))
    char_height = strwidth(levs[mx_ind], units="inches", cex=ps / par("ps"), 
                           family=family, font=gpar(fontface=face)$font) / 
      nchar(levs[mx_ind])
    width = floor((panels_height - pad)/ char_height)  # characters

    # wrap facet text
    p$data[[nm]] = unlist(lapply(strwrap(p$data[[nm]], width=width, 
                                         simplify=FALSE), paste, collapse="\n"))
  }

  invisible(p)
}

要使用该函数,请调用它来代替print

library(ggplot2)
df = expand.grid(group=paste(c("Very Very Very Long Group Name "), 1:4),
                 group1=paste(c("Very Very Very Long Group Name "), 5:8),
                 x=rnorm(5), y=rnorm(5), stringsAsFactors=FALSE)

p = ggplot(df) +
  geom_point(aes(x=x, y=y)) +
  facet_grid(group1~group)
strwrap_strip_text(p)
票数 10
EN

Stack Overflow用户

发布于 2016-11-16 10:44:29

自从这个问题发布后,带有ggplot2的新label_wrap_gen()函数(我认为是>= 1.0.0 )很好地处理了这个问题:

facet_wrap(~groupwrap, labeller = labeller(groupwrap = label_wrap_gen(10)))

请注意,您必须指定宽度才能使其工作。

对于较旧的ggplot2版本:

facet_wrap(~groupwrap, labeller = label_wrap_gen())
票数 57
EN

Stack Overflow用户

发布于 2013-05-21 05:51:06

(作为评论太长了,但也不是真正的答案)

我不认为一般的解决方案会直接存在于ggplot2中;这是网格单元自引用的经典问题:ggplot2想要动态计算视口大小,而strwrap需要知道固定的宽度来决定如何拆分文本。(有一个非常相似的问题,但我忘记了时间和地点)。

但是,您可以编写一个帮助函数来估计在绘制之前需要多少包装。在伪代码中,

# takes the facetting variable and device size
estimate_wrap = function(f, size=8, fudge=1){ 

    n = nlevels(f)
    for (loop over the labels of strwidth wider than (full.size * fudge) / n){
     new_factor_level[ii] = strwrap(label[ii], available width)
    }

  return(new_factor)
}

(需要一些标准单位转换)

当然,如果你想使用space="free",事情会变得更复杂。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16654691

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档