可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am trying to create a plot with logarithmically spaced grids using ggplot2 just like in the below figure. I get equidistant grids, but not log spaced ones. I know I am missing some parameter which I don't seem to get as of now. I have seen a lot of questions on the topic like Pretty ticks for log normal scale using ggplot2 (dynamic not manual), but do not solve the problem I am looking for.
set.seed(5)
x <- rlnorm(1000, meanlog=3.5, sdlog=1)
y <- rlnorm(1000, meanlog=4.0, sdlog=1)
d <- data.frame(x, y)
plot(x, y, log="xy", las=1)
grid(nx=NULL, ny=NULL, col= "blue", lty="dotted", equilogs=FALSE)
library(magicaxis)
magaxis(side=1:2, ratio=0.5, unlog=FALSE, labels=FALSE)
library(ggplot2)
library(MASS)
library(scales)
a <- ggplot(d, aes(x=x, y=y)) + geom_point() +
scale_x_log10(limits = c(1, NA),
labels = trans_format("log10", math_format(10^.x)),
breaks=trans_breaks("log10", function(x) 10^x, n=4)) +
scale_y_log10(limits = c(1, NA),
labels = trans_format("log10", math_format(10^.x)),
breaks=trans_breaks("log10", function(x) 10^x, n=4)) +
theme_bw() + theme(panel.grid.minor = element_line(color="blue", linetype="dotted"), panel.grid.major = element_line(color="blue", linetype="dotted"))
a + annotation_logticks(base = 10)
回答1:
Are you looking for diminishing spacing grid like this?
ggplot(d, aes(x=x, y=y)) + geom_point() +
coord_trans(y="log10", x="log10") +
scale_y_continuous(trans = log10_trans(),
breaks = trans_breaks("log10", function(x) 10^x),
labels = trans_format("log10", math_format(10^.x))) +
scale_x_continuous(trans = log10_trans(),
breaks = trans_breaks("log10", function(x) 10^x),
labels = trans_format("log10", math_format(10^.x)))
回答2:
For logarithmically spaced grids using ggplot2, the answer provided by Samehmagd shines for its simplicity, requires no manual settings, and it answers the question.
On using it on my own data, I discovered something that seems to be a bug. In my hands at least, this strategy fails when the values to be plotted are under 1 (thus generating negative values when log10 is applied).
This is the example, as contributed by Samehmagd, plus my contribution towards the end:
library(ggplot2)
library(scales)
set.seed(5)
x <- rlnorm(1000, meanlog=3.5, sdlog=1)
y <- rlnorm(1000, meanlog=4.0, sdlog=1)
d <- data.frame(x, y)
# peek at d
head(d)
# x y
# 1 14.284064 12.74253
# 2 132.205740 189.53295
# 3 9.435773 35.44751
# 4 35.521664 54.97449
# 5 183.358064 61.84004
# 6 18.121372 36.24753
# Plot successfully (this figure is identical to Samehmagd's)
ggplot(d, aes(x=x, y=y)) + geom_point() + coord_trans(y="log10", x="log10") + scale_y_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x))) + scale_x_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x)))
# Now, here is when it breaks
f <- 1/d
# peek at f
head(f)
# x y
# 1 0.070008087 0.078477335
# 2 0.007563968 0.005276127
# 3 0.105979655 0.028210728
# 4 0.028151834 0.018190255
# 5 0.005453810 0.016170753
# 6 0.055183459 0.027588083
# Get the plotting to fail just by using f instead of d, no other change.
ggplot(f, aes(x=x, y=y)) + geom_point() + coord_trans(y="log10", x="log10") + scale_y_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x))) + scale_x_continuous(trans=log10_trans(), breaks=trans_breaks("log10", function(x) 10^x), labels=trans_format("log10", math_format(10^.x)))
Error in if (zero_range(range)) { : missing value where TRUE/FALSE needed
In addition: Warning message:
In trans$transform(out$range) : NaNs produced
Whether or not this failure is a bug or a feature, it is well worth documenting.
回答3:
You can define the breaks manually.
> ticks <- 2:10
> ooms <- 10^(0:3)
> breaks <- as.vector(ticks %o% ooms)
> breaks
[1] 2 3 4 5 6 7 8 9 10
[10] 20 30 40 50 60 70 80 90 100
[19] 200 300 400 500 600 700 800 900 1000
[28] 2000 3000 4000 5000 6000 7000 8000 9000 10000
This snippet defines the breaks, hides some of them, and generates the plot.
library(ggplot2)
set.seed(5)
x <- rlnorm(1000, meanlog=3.5, sdlog=1)
y <- rlnorm(1000, meanlog=4.0, sdlog=1)
d <- data.frame(x, y)
ticks <- 2:10
# define the OOMs (orders of magnitudes)
ooms <- 10^(0:3)
breaks <- as.vector(ticks %o% ooms)
# select the labels to show
show.labels <- c(T, F, F, T, F, F, F, F, T)
labels <- as.character(breaks * show.labels)
labels <- gsub("^0$", "", labels)
p <- ggplot(d, aes(x=x, y=y)) + geom_point() +
scale_x_log10(limits = c(1, NA), labels = labels, breaks = breaks) +
scale_y_log10(limits = c(1, NA), labels = labels, breaks = breaks) +
theme_bw() + theme(panel.grid.minor = element_line(color="blue", linetype="dotted"), panel.grid.major = element_line(color="blue", linetype="dotted")) +
annotation_logticks(base = 10)
p
回答4:
To build on Gabor's answer, it is unnecessary to define the ticks only in the exact range of the plot. You can instead define breaks for a large range of values that will cover pretty much everything you'd ever expect to see and use those to create nice grid lines for any plot. While perhaps not the most elegant solution, it's easy and more generalizable than having to manually figure out ranges every time.
breaks <- 10^(-10:10)
minor_breaks <- rep(1:9, 21)*(10^rep(-10:10, each=9))
d %>%
ggplot(aes(x, y)) +
geom_point() +
scale_x_log10(breaks = breaks, minor_breaks = minor_breaks) +
scale_y_log10(breaks = breaks, minor_breaks = minor_breaks) +
annotation_logticks() +
coord_equal() +
theme_bw()