我正在尝试使用 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))
谢谢!
编辑:插入图片
可以使用自定义中断功能来重现基本图形行为:
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())
由于十的幂在这里不再适用,
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())
当我在对数刻度上构建图表时,我发现以下方法效果很好:
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)))
一些差异:
给予
基本图形函数
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")
随着
scales 1.0.0
的发布和新函数log_breaks()
的发布,这个问题终于得到了解决,该函数返回基数整数幂的整数倍。
library(ggplot2)
ggplot(M, aes(x = X,y = Y)) +
geom_line() +
scale_y_log10(breaks = log_breaks())
此函数允许指定所需的主要和次要刻度数。必须指定两次才能达到该效果:
#' 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))
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
}