45

我正在尝试使用 ggplot2 创建具有对数正态 y 刻度的性能图表。不幸的是,对于基本绘图功能,我无法生成漂亮的刻度。

这是我的例子:

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

产生丑陋的滴答声:

在此处输入图像描述

我也试过:

在此处输入图像描述

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

如何获得与默认绘图功能相同的中断/刻度:

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

在此处输入图像描述

结果应该看起来像这样,但不是硬输入中断而是动态的。我尝试了类似axisTicks()但没有成功的功能:

ggplot(M, aes(x = X,y = Y)) + geom_line() + 
  scale_y_continuous(trans = log_trans(), breaks = c(1, 10, 100, 10000))

在此处输入图像描述

谢谢!

编辑:插入图片

4

5 回答 5

41

可以使用自定义中断函数重现基本图形行为:

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

十次方破

但是,我们可以在数据子集上使用相同的函数,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())

由于 10 的幂在这里不再适合,base_breaks产生替代的漂亮休息:

漂亮的休息

请注意,我已关闭次要网格线:在某些情况下,在 y 轴上的主要网格线之间设置网格线是有意义的,但并非总是如此。

编辑

假设我们修改 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())

在此处输入图像描述

我们可以通过将文本格式化函数传递给 的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())

在此处输入图像描述

于 2014-03-06T14:47:22.003 回答
23

当我在对数刻度上构建图表时,我发现以下工作非常好:

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轴更好。出于某种原因,在 Andrie 的图中,x 轴范围不同。

给予

在此处输入图像描述

于 2013-01-10T13:17:13.947 回答
16

基本图形函数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")

在此处输入图像描述

于 2013-01-10T10:29:58.217 回答
8

这个问题终于随着scales 1.0.0新函数的发布而得到解决log_breaks(),它返回基数整数幂的整数倍。

library(ggplot2)
ggplot(M, aes(x = X,y = Y)) + 
  geom_line() + 
  scale_y_log10(breaks = log_breaks())

于 2019-11-02T08:42:44.843 回答
2

此函数允许指定所需的主要和次要刻度数。必须为该效果指定两次:

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

具有对数刻度的绘图

于 2019-01-23T10:37:40.033 回答