ggplot2 scheint keine eingebaute Methode zu haben, um mit Überlappungen für Text auf scatter plots umzugehen. Ich habe jedoch eine andere Situation, in der die Bezeichnungen auf einer diskreten Achse stehen, und ich frage mich, ob jemand hier eine bessere Lösung hat als das, was ich gemacht habe.Umgang mit ggplot2 und überlappenden Beschriftungen auf einer diskreten Achse
Einige Codebeispiel:
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 = "")
So sehen wir, dass die x-Achsenbeschriftungen auf der jeweils anderen sind. Zwei Lösungen kommen mir in den Sinn: 1) Abkürzungen der Etiketten und 2) Hinzufügen von Zeilenumbrüchen zu den Etiketten. In vielen Fällen (1) wird es reichen, aber in einigen Fällen ist dies nicht möglich. Also schrieb ich eine Funktion zum Hinzufügen von Zeilenumbrüchen (\n
) alle n-ten Zeichen an die Saiten, um überlappende Namen zu vermeiden:
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 = "")
Und der Ausgang ist:
Dann kann man einige Zeit verbringen spielen mit die Intervallgröße, um zu vermeiden, dass zwischen den Etiketten zu viel Leerraum ist.
Wenn die Anzahl der Etiketten variiert, ist diese Art der Lösung nicht so gut, da sich die optimale Intervallgröße ändert. Da die normale Schriftart nicht mono-beabstandet ist, wirkt sich der Text der Beschriftungen auch auf die Breite aus, und daher muss man bei der Auswahl eines guten Intervalls besondere Vorsicht walten lassen (dies kann man vermeiden, indem man eine Monospace-Schriftart verwendet) , aber sie sind extra breit). Schließlich ist die new_lines_adder()
Funktion dumm, dass es Wörter in zwei auf dumme Weisen teilt, die Menschen nicht tun würden. Z.B. in dem oben genannten spaltete es "Atem" in "Brust". Man könnte es umschreiben, um dieses Problem zu vermeiden.
Man kann auch die Schriftgröße verringern, aber dies ist ein Kompromiss mit der Lesbarkeit und oft ist die Verringerung der Schriftgröße unnötig.
Was ist der beste Weg, um diese Art von Etikettenüberladung zu behandeln?
Ich beschäftige mich normalerweise mit überlappenden Labels, indem ich sie rotiere: '+ theme (axis.text.x = element_text (angle = 60, hjust = 1))' (aber es ist nicht ideal, wenn sie sehr lang sind, da es a erzeugt großer Rand) – scoa