使用ggplot2对数正态刻度进行漂亮的刻度(动态而非手动)

问题描述 投票:0回答:6

我正在尝试使用 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())

产生丑陋的蜱虫:

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

谢谢!

编辑:插入图片

r plot ggplot2
6个回答
43
投票

可以使用自定义中断功能来重现基本图形行为:

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 轴上主要网格线之间的中间是有意义的,但并非总是如此。

编辑

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


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. 次要网格线位于主要网格线的中间(将此图与 Andrie 答案中的网格线进行比较)。
  3. x 轴更好。由于某种原因,Andrie 图中的 x 轴范围不同。

给予

enter image description here


17
投票

基本图形函数

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


11
投票

随着

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

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


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


0
投票
ggplot2::scale_y_log10(breaks = log_breaks)

log_breaks <- function(x) {
    lower <- floor(log10(min(x)))
    upper <- ceiling(log10(max(x)))
    cycles <- seq(lower, upper, 1)
    10^cycles
}
© www.soinside.com 2019 - 2024. All rights reserved.