如何处理离散轴上的ggplot2和重叠标签

Del*_*eet 9 plot r ggplot2 axis-labels

ggplot2似乎没有内置的方法来处理散点图上的文本过度绘图.但是,我有一个不同的情况,标签是离散轴上的标签,我想知道这里有人有一个比我一直做的更好的解决方案.

一些示例代码:

library(ggplot2)

#some example data
test.data = data.frame(text = c("A full commitment's what I'm thinking of",
                                "History quickly crashing through your veins",
                                "And I take A deep breath and I get real high",
                                "And again, the Internet is not something that you just dump something on. It's not a big truck."),
                       mean = c(3.5, 3, 5, 4),
                       CI.lower = c(4, 3.5, 5.5, 4.5),
                       CI.upper = c(3, 2.5, 4.5, 3.5))

#plot
ggplot(test.data, aes_string(x = "text", y = "mean")) +
  geom_point(stat="identity") +
  geom_errorbar(aes(ymax = CI.upper, ymin = CI.lower), width = .1) +
  scale_x_discrete(labels = test.data$text, name = "")
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

因此我们看到x轴标签彼此重叠.我想到了两种解决方案:1)缩写标签,2)在标签上添加换行符.在许多情况下(1)会做,但在某些情况下,它无法完成.所以我编写了一个函数,用于\n在字符串中每隔n个字符添加换行符()以避免重叠名称:

library(ggplot2)

#Inserts newlines into strings every N interval
new_lines_adder = function(test.string, interval){
  #length of str
  string.length = nchar(test.string)
  #split by N char intervals
  split.starts = seq(1,string.length,interval)
  split.ends = c(split.starts[-1]-1,nchar(test.string))
  #split it
  test.string = substring(test.string, split.starts, split.ends)
  #put it back together with newlines
  test.string = paste0(test.string,collapse = "\n")
  return(test.string)
}

#a user-level wrapper that also works on character vectors, data.frames, matrices and factors
add_newlines = function(x, interval) {
  if (class(x) == "data.frame" | class(x) == "matrix" | class(x) == "factor") {
    x = as.vector(x)
  }

  if (length(x) == 1) {
    return(new_lines_adder(x, interval))
  } else {
    t = sapply(x, FUN = new_lines_adder, interval = interval) #apply splitter to each
    names(t) = NULL #remove names
    return(t)
  }
}

#plot again
ggplot(test.data, aes_string(x = "text", y = "mean")) +
  geom_point(stat="identity") +
  geom_errorbar(aes(ymax = CI.upper, ymin = CI.lower), width = .1) +
  scale_x_discrete(labels = add_newlines(test.data$text, 20), name = "")
Run Code Online (Sandbox Code Playgroud)

输出是: 在此输入图像描述

然后人们可以花一些时间玩间隔大小,以避免标签之间有太多的空白区域.

如果标签的数量不同,这种解决方案不太好,因为最佳间隔大小会发生变化.另外,由于普通字体不是单行间距,标签的文字也会对宽度产生影响,因此在选择好间隔时必须格外小心(可以通过使用单空格字体来避免这种情况) ,但它们是特别宽的).最后,这个new_lines_adder()功能很愚蠢,因为它会以人类不会做的愚蠢方式将单词分成两部分.例如,在上面它将"呼吸"分成"br \nreath".人们可以重写它以避免这个问题.

人们也可以减小字体大小,但这是可读性的折衷,通常不需要减小字体大小.

处理这种标签过度绘图的最佳方法是什么?

Del*_*eet 0

该解决方案以@Stibu 答案和评论为基础,考虑了组的数量,并使用 Stibu 开发的智能拆分,同时添加了对由斜杠分隔的单词的修复。

功能:

#Inserts newlines into strings every N interval
new_lines_adder = function(x, interval) {
  #add spaces after /
  x = str_replace_all(x, "/", "/ ")
  #split at spaces
  x.split = strsplit(x, " ")[[1]]
  # get length of snippets, add one for space
  lens <- nchar(x.split) + 1
  # now the trick: split the text into lines with
  # length of at most interval + 1 (including the spaces)
  lines <- cumsum(lens) %/% (interval + 1)
  # construct the lines
  x.lines <- tapply(x.split, lines, function(line)
    paste0(paste(line, collapse=" "), "\n"), simplify = TRUE)
  # put everything into a single string
  result <- paste(x.lines, collapse="")
  #remove spaces we added after /
  result = str_replace_all(result, "/ ", "/")
  return(result)
}

#wrapper for the above, meant for users
add_newlines = function(x, total.length = 85) {
  # make sure, x is a character array   
  x = as.character(x)
  #determine number of groups
  groups = length(x)
  # apply splitter to each
  t = sapply(x, FUN = new_lines_adder, interval = round(total.length/groups), USE.NAMES=FALSE)
  return(t)
}
Run Code Online (Sandbox Code Playgroud)

我尝试了默认输入的一些值,85 是文本结果适合示例数据的值。标签 2 中的任何较高位置和“静脉”都会向上移动并距离第三个标签太近。

它看起来是这样的:

在此输入图像描述

尽管如此,最好使用总文本宽度的实际测量值,而不是字符数,因为必须依赖此代理通常意味着标签浪费大量空间。也许可以new_lines_adder()用一些基于的代码重写strwidth来处理字符宽度不等的问题。

我不会回答这个问题,以防有人能找到一种方法来做到这一点。

我已将这两个函数添加到github 上的个人包中,因此任何想要使用它们的人都可以从那里获取它们。