Ich versuche, ggplot2 zu verwenden, um ein Leistungsdiagramm mit einer normalen y-Skala zu erstellen. Leider kann ich keine Nice-Ticks für die Basisplot-Funktion erzeugen.
Hier mein Beispiel:
library(ggplot2)
library(scales)
# fix RNG
set.seed(seed=1)
# simulate returns
y=rnorm(999,0.02,0.2)
# M$Y are the cummulative returns (like an index)
M=data.frame(X=1:1000,Y=100)
for (i in 2:1000)
M[i,"Y"]=M[i-1,"Y"]*(1+y[i-1])
ggplot(M,aes(x=X,y=Y))+geom_line()+scale_y_continuous(trans=log_trans())
produziert hässliche Zecken:
Ich habe auch versucht:
ggplot(M,aes(x=X,y=Y)) + geom_line() +
scale_y_continuous(trans=log_trans(), breaks=pretty_breaks())
Wie bekomme ich die gleichen Pausen/Ticks wie in der Standard-Plotfunktion:
plot(M,type="l",log="y")
Das Ergebnis sollte so aussehen, aber nicht mit harten Eingaben, sondern dynamisch. Ich habe Funktionen wie axisTicks()
ausprobiert, war aber nicht erfolgreich:
ggplot(M,aes(x=X,y=Y)) + geom_line() +
scale_y_continuous(trans=log_trans(), breaks=c(1,10,100,10000))
Vielen Dank!
bearbeiten: eingefügte Bilder
Das Verhalten der Basisgrafik kann mithilfe einer benutzerdefinierten Unterbrechungsfunktion reproduziert werden:
base_breaks <- function(n = 10){
function(x) {
axisTicks(log10(range(x, na.rm = TRUE)), log = TRUE, n = n)
}
}
Wenn Sie dies auf die Beispieldaten anwenden, erhalten Sie dasselbe Ergebnis wie mit trans_breaks('log10', function(x) 10^x)
:
ggplot(M, aes(x = X, y = Y)) + geom_line() +
scale_y_continuous(trans = log_trans(), breaks = base_breaks()) +
theme(panel.grid.minor = element_blank())
Wir können jedoch dieselbe Funktion für eine Teilmenge der Daten mit y-Werten zwischen 50 und 600 verwenden:
M2 <- subset(M, Y > 50 & Y < 600)
ggplot(M2, aes(x = X, y = Y)) + geom_line() +
scale_y_continuous(trans = log_trans(), breaks = base_breaks()) +
theme(panel.grid.minor = element_blank())
Da Zehnerpotenzen hier nicht mehr geeignet sind, erzeugt base_breaks
alternative hübsche Pausen:
Beachten Sie, dass ich kleinere Gitternetzlinien deaktiviert habe. In einigen Fällen ist es sinnvoll, Gitternetzlinien auf halbem Weg zwischen den großen Gitternetzlinien auf der y-Achse zu haben, aber nicht immer.
Bearbeiten
Nehmen wir an, wir ändern M so, dass der Mindestwert 0,1 beträgt:
M <- M - min(M) + 0.1
Die base_breaks () - Funktion wählt immer noch hübsche Brüche aus, aber die Bezeichnungen befinden sich in wissenschaftlicher Notation, was möglicherweise nicht als "hübsch" angesehen wird:
ggplot(M, aes(x = X, y = Y)) + geom_line() +
scale_y_continuous(trans = log_trans(), breaks = base_breaks()) +
theme(panel.grid.minor = element_blank())
Wir können die Textformatierung steuern, indem Sie eine Textformatierungsfunktion an das labels
-Argument von scale_y_continuous
übergeben. In diesem Fall erledigt prettyNum
aus dem Basispaket die Aufgabe:
ggplot(M, aes(x = X, y = Y)) + geom_line() +
scale_y_continuous(trans = log_trans(), breaks = base_breaks(),
labels = prettyNum) +
theme(panel.grid.minor = element_blank())
Wenn ich Diagramme auf der Protokollskala konstruiere, finde ich die folgenden Funktionen ziemlich gut:
g = ggplot(M,aes(x=X,y=Y)) + geom_line()
g + scale_y_continuous(trans = 'log10',
breaks = trans_breaks('log10', function(x) 10^x),
labels = trans_format('log10', math_format(10^.x)))
Ein paar Unterschiede:
Geben
Die Basisgrafikfunktion axTicks()
gibt die Achsenunterbrechungen für die aktuelle Zeichnung zurück. Sie können dies also verwenden, um identische Pausen wie bei Basisgrafiken zurückzugeben. Der einzige Nachteil ist, dass Sie zuerst die Basisgrafik zeichnen müssen.
library(ggplot2)
library(scales)
plot(M, type="l",log="y")
breaks <- axTicks(side=2)
ggplot(M,aes(x=X,y=Y)) + geom_line() +
scale_y_continuous(breaks=breaks) +
coord_trans(y="log")
Mit dieser Funktion können Sie sowohl die gewünschte Anzahl von Haupt- als auch Nebenstrich festlegen. Für diesen Effekt muss es zweimal angegeben werden:
#' log scale
#'
#' Creates a function which returns ticks for a given data range. It uses some
#' code from scales::log_breaks, but in contrast to that function it not only
#' the exponentials of the base b, but log minor ticks (f*b^i, where f and i are
#' integers), too.
#'
#' @param n Approximate number of ticks to produce
#' @param base Logarithm base
#'
#' @return
#'
#' A function which expects one parameter:
#'
#' * **x**: (numeric vector) The data for which to create a set of ticks.
#'
#' @export
logTicks <- function(n = 5, base = 10){
# Divisors of the logarithm base. E.g. for base 10: 1, 2, 5, 10.
divisors <- which((base / seq_len(base)) %% 1 == 0)
mkTcks <- function(min, max, base, divisor){
f <- seq(divisor, base, by = divisor)
return(unique(c(base^min, as.vector(outer(f, base^(min:max), `*`)))))
}
function(x) {
rng <- range(x, na.rm = TRUE)
lrng <- log(rng, base = base)
min <- floor(lrng[1])
max <- ceiling(lrng[2])
tck <- function(divisor){
t <- mkTcks(min, max, base, divisor)
t[t >= rng[1] & t <= rng[2]]
}
# For all possible divisors, produce a set of ticks and count how many ticks
# result
tcks <- lapply(divisors, function(d) tck(d))
l <- vapply(tcks, length, numeric(1))
# Take the set of ticks which is nearest to the desired number of ticks
i <- which.min(abs(n - l))
if(l[i] < 2){
# The data range is too small to show more than 1 logarithm tick, fall
# back to linear interpolation
ticks <- pretty(x, n = n, min.n = 2)
}else{
ticks <- tcks[[i]]
}
return(ticks)
}
}
Ihr Beispiel:
library(ggplot2)
library(scales)
# fix RNG
set.seed(seed=1)
# simulate returns
y=rnorm(999,0.02,0.2)
# M$Y are the cummulative returns (like an index)
M=data.frame(X=1:1000,Y=100)
for (i in 2:1000)
M[i,"Y"]=M[i-1,"Y"]*(1+y[i-1])
ggplot(M,aes(x=X,y=Y))+geom_line()+
scale_y_log10(breaks = logTicks(n = 4), minor_breaks = logTicks(n = 40))