Подтвердить что ты не робот

Довольно тикает для нормальной шкалы журнала, используя ggplot2 (динамический не ручной)

Я пытаюсь использовать ggplot2 для создания диаграммы производительности с обычной шкалой журнала. К сожалению, я не могу производить хорошие тики, как для функции базового графика.

Вот мой пример:

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

производит уродливые клещи:

enter image description here

Я также пробовал:

enter image description here

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

Как я могу получить те же разрывы/тики, что и в функции графика по умолчанию:

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

enter image description here

Результат должен выглядеть так, но не с жестким типом перерывов, а с динамическим. Я пробовал функции типа axisTicks(), но не был успешным:

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

Спасибо!

edit: вставленные фотографии

4b9b3361

Ответ 1

Поведение базовой графики можно воспроизвести с помощью специальной функции breaks:

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

Применение этого к данным примера дает тот же результат, что и при использовании 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

Однако мы можем использовать одну и ту же функцию в подмножестве данных, причем значения y от 50 до 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())

Поскольку полномочия десяти уже не подходят здесь, base_breaks создает альтернативные симпатичные разрывы:

pretty breaks

Обратите внимание, что я отключил второстепенные линии сетки: в некоторых случаях имеет смысл иметь линии сетки на полпути между основными линиями сетки по оси y, но не всегда.

Edit

Предположим, что мы модифицируем M так, чтобы минимальное значение равно 0,1:

M <- M - min(M) + 0.1

Функция base_breaks() по-прежнему выбирает довольно разрывы, но метки находятся в научной нотации, которая не может рассматриваться как "симпатичная":

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

Мы можем управлять форматированием текста, передавая функцию форматирования текста в аргумент labels scale_y_continuous. В этом случае prettyNum из базового пакета выполняет работу красиво:

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

Ответ 2

Когда я строю графики в логарифмическом масштабе, я нахожу следующие работы довольно хорошо:

library(ggplot2)
library(scales)

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

Пара отличий:

  1. Метки осей показаны как степени десяти - что мне нравится
  2. Малая линия сетки находится в середине основных линий сетки (сравните этот график с линиями сетки в ответе Андри).
  3. Ось х лучше. По какой-то причине на графике Андри диапазон оси X отличается.

Дать

enter image description here

Ответ 3

Базовая графическая функция axTicks() возвращает осевые разрывы для текущего графика. Таким образом, вы можете использовать это, чтобы вернуть разрывы, идентичные базовой графике. Единственным недостатком является то, что вы сначала должны построить график базовой графики.

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

Ответ 4

Эта функция позволяет указать как желаемое количество основных, так и второстепенных тиков. Это должно быть указано дважды для этого эффекта:

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

Ваш пример:

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