ggplot2似乎没有内置的方法来处理scatter plots上的文本的过度绘图。但是,我有一个不同的情况,标签是离散轴上的标签,我想知道这里的人是否有比我一直做的更好的解决方案。
一些示例代码:
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 = "")
因此我们看到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 = "")
输出是:
然后人们可以花一些时间玩间隔大小,以避免标签之间有太多的空白区域。
如果标签数量不同,这种解决方案不太好,因为最佳间隔大小会发生变化。另外,由于普通字体不是单行间距,标签的文字也会对宽度产生影响,因此在选择好的间隔时必须格外小心(可以通过使用单空间字体来避免这种情况) ,但它们是特别宽的)。最后,new_lines_adder()
函数是愚蠢的,因为它会以人类不会做的愚蠢方式将单词分成两个。例如。在上面它将“呼吸”分成“br \ nreath”。人们可以重写它以避免这个问题。
还可以减小字体大小,但这是可读性的折衷,通常不需要减小字体大小。
处理这种标签过度绘图的最佳方法是什么?
答案 0 :(得分:4)
我尝试将new_lines_adder
的不同版本组合在一起:
new_lines_adder = function(test.string, interval) {
#split at spaces
string.split = strsplit(test.string," ")[[1]]
# get length of snippets, add one for space
lens <- nchar(string.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
test.lines <- tapply(string.split,lines,function(line)
paste0(paste(line,collapse=" "),"\n"),simplify = TRUE)
# put everything into a single string
result <- paste(test.lines,collapse="")
return(result)
}
它仅在空格处分割线条,并确保这些线条最多包含interval
给出的字符数。有了这个,你的情节看起来如下:
我不认为这是最好的方式。它仍然忽略了并非所有字符都具有相同的宽度。也许使用strwidth
可以实现更好的目标。
顺便说一下:您可以将add_newlines
简化为以下内容:
add_newlines = function(x, interval) {
# make sure, x is a character array
x = as.character(x)
# apply splitter to each
t = sapply(x, FUN = new_lines_adder, interval = interval,USE.NAMES=FALSE)
return(t)
}
一开始,as.character
确保您有一个字符串。如果您已经有了字符串,那么这样做也没有什么坏处,因此不需要if
子句。
下一个if
子句也是不必要的:如果x只包含一个元素,则sapply可以正常工作。您可以通过设置USE.NAMES=FALSE
来取消名称,这样就不需要在附加行中删除名称。
答案 1 :(得分:0)
在@Stibu回答和评论的基础上,该解决方案考虑了组的数量,并使用Stibu开发的智能拆分,同时为斜线分隔的单词添加了修复。
功能:
import org.springframework.http.HttpStatus;
我尝试了默认输入的一些值,85是示例数据的文本结果正确的值。标签2中的任何更高和“静脉”都会向上移动并且与第三个标签太接近。
以下是它的外观:
尽管如此,最好使用总文本宽度的实际度量,而不是必须依赖此代理的字符数,这通常意味着标签浪费了大量空间。也许可以使用基于#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)
}
的一些代码重写new_lines_adder()
来处理字符宽度不等的问题。
如果有人能找到办法,我会留下这个问题。
我已将这两个功能添加到my personal package on github,因此任何想要使用它们的人都可以从那里获取它们。