web-dev-qa-db-fra.com

Jolis ticks pour log log scale en utilisant ggplot2

J'essaie d'utiliser ggplot2 pour créer un graphique de performances avec une échelle log normale y. Malheureusement, je ne suis pas en mesure de produire de jalons Nice comme pour la fonction de tracé de base.

Voici mon exemple:

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

produit des tiques laides:

enter image description here

J'ai aussi essayé:

enter image description here

ggplot(M,aes(x=X,y=Y)) + geom_line() + 
  scale_y_continuous(trans=log_trans(), breaks=pretty_breaks())

Comment puis-je obtenir les mêmes pauses/ticks que dans la fonction de tracé par défaut:

plot(M,type="l",log="y")

enter image description here

Le résultat devrait ressembler à ceci, mais pas avec une frappe dure, mais dynamique. J'ai essayé des fonctions comme axisTicks() mais sans succès:

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

Merci!

modifier: images insérées

40
Matthias_H

Le comportement graphique de base peut être reproduit à l'aide d'une fonction de rupture personnalisée:

base_breaks <- function(n = 10){
    function(x) {
        axisTicks(log10(range(x, na.rm = TRUE)), log = TRUE, n = n)
    }
}

Appliquer ceci aux données d'exemple donne le même résultat que d'utiliser 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

Cependant, nous pouvons utiliser la même fonction sur un sous-ensemble de données, avec des valeurs y comprises entre 50 et 600:

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

Comme les puissances de dix ne conviennent plus ici, base_breaks produit de jolies coupures alternatives:

pretty breaks

Notez que j'ai désactivé les lignes de grille mineures: dans certains cas, il serait judicieux de disposer des lignes de grille à mi-chemin entre les lignes principales de la grille sur l'axe des y, mais pas toujours.

Modifier

Supposons que nous modifions M pour que la valeur minimale soit 0.1:

M <- M - min(M) + 0.1

La fonction base_breaks () sélectionne toujours les jolies coupures, mais les étiquettes sont en notation scientifique, qui peut ne pas être vue comme "jolie":

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

Nous pouvons contrôler la mise en forme du texte en passant une fonction de mise en forme du texte à l'argument labels de scale_y_continuous. Dans ce cas, prettyNum du paquet de base fait le travail correctement:

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

Lorsque je construis des graphiques sur l'échelle du journal, je trouve que les solutions suivantes fonctionnent assez bien:

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

Quelques différences:

  1. Les étiquettes des axes sont représentées par des puissances de dix - ce que j'aime bien
  2. La ligne de grille mineure se trouve au milieu des lignes de grille principales (comparez ce graphique avec les lignes de grille dans la réponse d'Andrie).
  3. L'axe des x est plus joli. Pour une raison quelconque dans le graphique d'Andrie, la plage de l'axe des x est différente.

Pour donner

enter image description here

19
csgillespie

La fonction graphique de base axTicks() renvoie les sauts d’axe pour le tracé actuel. Vous pouvez donc l'utiliser pour renvoyer des sauts identiques aux graphiques de base. Le seul inconvénient est que vous devez commencer par tracer le graphique de base.

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

Cette fonction permet de spécifier le nombre souhaité de ticks majeurs et mineurs. Il doit être spécifié deux fois à cet effet:

#' 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)
  }
}

Votre exemple:

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