Reputation: 2219
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)
Upvotes: 14
Views: 15928
Reputation: 943
I know I'm coming late to the party, but I am reckoning that this solution might turn out useful. While the outer product trick is clever and useful, the scales
package has us covered:
library(ggplot2)
set.seed(5)
tibble(
x = rlnorm(1000, meanlog=3.5, sdlog=1),
y = rlnorm(1000, meanlog=4.0, sdlog=1)
) %>%
ggplot(aes(x=x, y=y)) +
geom_point() +
scale_x_log10(minor_breaks=scales::minor_breaks_n(10)) +
scale_y_log10(minor_breaks=scales::minor_breaks_n(10)) +
theme_bw() +
theme(
panel.grid.minor = element_line(color="blue", linetype="dotted"),
panel.grid.major = element_line(color="blue", linetype="dotted")
)
Upvotes: 0
Reputation: 791
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()
Upvotes: 21
Reputation: 473
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)))
Upvotes: 5
Reputation: 5057
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
Upvotes: 2
Reputation: 31
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.
Upvotes: 3