it-swarm.com.de

Hübsche Ticks für lognormale Skalierung mit ggplot2 (dynamisch nicht manuell)

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:

enter image description here

Ich habe auch versucht:

enter image description here

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

enter image description here

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

enter image description here

Vielen Dank!

bearbeiten: eingefügte Bilder

40
Matthias_H

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

breaks at powers of ten

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:

pretty breaks

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

enter image description here

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

enter image description here

35
Heather Turner

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:

  1. Die Achsenbeschriftungen werden als Zehnerpotenzen angezeigt - was mir gefällt
  2. Die Nebenrasterlinie befindet sich in der Mitte der Hauptrasterlinien (vergleichen Sie diese Darstellung mit den Rasterlinien in Andries Antwort).
  3. Die X-Achse ist schöner. In Andries Plot unterscheidet sich der Bereich der x-Achse aus irgendeinem Grund.

Geben

enter image description here

19
csgillespie

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

enter image description here

16
Andrie

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

 plot with logarithmic scale

0
akraf