38

facet_wrapまたはfacet_grid呼び出しでストリップ ラベル テキストを動的にラップする方法を探しています。を使用してこれを達成する方法を見つけましたが、出力strwrapが必要に応じて機能するように を指定する必要がありますwidth。多くの場合、ファセットの数は事前にわからないため、この方法ではwidth、データセットとプロット サイズに基づいてパラメーターを繰り返し調整する必要があります。ラップ機能の幅を動的に指定することは可能ですか? または、より適切に機能するファセットにラベルを付けるための別のオプションはありますか?

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))
4

4 に答える 4

60

この質問が投稿されて以来、(>= 1.0.0 だと思います) をlabel_wrap_gen()使用した新しい関数がこれを適切に処理します。ggplot2

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

動作させるには幅を指定する必要があることに注意してください。

古い ggplot2 バージョンの場合:

facet_wrap(~groupwrap, labeller = label_wrap_gen())
于 2016-11-16T02:44:29.147 に答える
10

@baptiste と @thunk からのガイダンスのおかげで、私は以下の関数を作成しました。これは、ファセット ラベルを自動的にラップするという非常に良い仕事をしているようです。ただし、改善のための提案はいつでも大歓迎です。

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)
于 2013-05-24T13:11:59.013 に答える
3

(コメントとしては長すぎますが、実際の回答でもありません)

一般的な解決策が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"

于 2013-05-20T21:51:06.203 に答える
1

また、コメントには長すぎますが、完全な回答はありません。それはバティストの答えの行に沿っていますが、さらにいくつかの指針があります:

p <- ggplot(df) + geom_point(aes(x=x, y=y)) + facet_wrap(~groupwrap)

# get the grobs of the plot and get the widths of the columns
grobs <- ggplotGrob(p)
grobs$width

# here you would have to use convertWidth from gridDebug package
# to convert all the units in the widths to the same unit (say 'pt'),
# including exctraction from the strings they are in -- also, I
# couldn't make it work neither for the unit 'null' nor for 'grobwidth',
# so you'll have to add up all the other ones, neglect grobwidth, and
# subtract all the widths that are not null (which is the width of each
# panel) from the device width
library('grid')
convertWidth(DO FOR EACH ELEMENT OF grobs$width)
sum <- SUM_UP_ALL_THE_NON-PANEL_WIDTHS

# get the width of the graphics device
device <- par('din')[1]

# get width of all panels in a row
panels_width <- device - sum

# get total number of panels in your case
df$group <- as.factor(df$group)
npanels <- nlevels(df$group)

# get number of panels per row (i.e. number of columns in graph) with
# the function that ggplot2 uses internally
cols <- n2mfrow(npanels)

# get estimate of width of single panel
panel_width <- panels_width / cols

申し訳ありませんが、これはまだ部分的にまだらです。しかし、それは私が得た限りなので、これらのアイデアが途中で役立つことを願っています...

于 2013-05-21T09:57:19.763 に答える