Ggplot2 Scale_X_Log10() Destroys/Doesn't Apply for Function Plotted via Stat_Function()

In ggplot2, how do I make stat_function subject to scale_x_log10?

The answer seems to be, you can't. There is a predefined order in which ggplot2 does transformations;

  1. Scales
  2. Statistics
  3. Coordinates

Hence, instead of scale transformation, use a coordinate transformation.

base + stat_function(fun = dexp, colour = "red") + coord_trans(x = "log10")

Sample Image

Plotting in R using stat_function on a logarithmic scale

When you use scale_x_log10() then x values are log transformed, and only then used for calculation of y values with stat_function(). Then x values are backtransformed to original values to make scale. y values remain as calculated from log transformed x. You can check this by plotting values without scale_y_log10(). In plot there is straight line.

ggplot(data.frame(x=1:1e4), aes(x)) +
stat_function(fun = function(x) x) +
scale_x_log10()

If you apply scale_y_log10() you log transform already calculated y values, so curve is plotted.

Plot function with x-axis in log-scale using ggplot

Your problem seems similar to the one resolved in this post. Using scale_x_log10, the base 10 log of x is taken and then passed to stat_function for evaluation. Try something like the following:

LP.4mod <- function(x, ...) LP.4(10^x,... ) 
ggplot(data.frame(x=dose), aes(x)) +
scale_x_log10() +
stat_function(fun = LP.4mod,
args = list(A = 42245, B = 1.33, C = 0.599, D = 23412))

Logarithmic scaling with ggplot2 in R

Your data in current form is not log distributed -- most val around 6500 and some 10% higher. If you want to stretch the data, you could use a custom transformation using the scales::trans_new(), or here's a simpler version that just subtracts a baseline value to make a log transform useful. After subtracting 6500, the small values will be mapped to around 50, with the large values around 1000, which is a more appropriate range for a log scale. Then we apply the same transformation to the breaks so that the labels will appear in the right spots. (i.e. the label 6550 is mapped to the data that is mapped to 6550 - 6500 = 50)

This method helps if you want to make the underlying values more distinguishable, but at the cost of distorting the underlying proportions between values. You might be able to help with this by picking useful breaks and labeling them with scaling stats, e.g.

7000
+7% over min

my_breaks <- c(6550, 6600, 6750, 7000, 7500)
baseline = 6500

library(ggplot2)
ggplot(data = solverEntries,
aes(x = val - baseline, y = instance,
color = solver, group = solver)) +
geom_point() +
scale_x_log10(breaks = my_breaks - baseline,
labels = my_breaks, name = "val")

Sample Image

stat_function produces flat line from function

Note the following warning:

> f(c(0,10))
[1] 0
Warning message:
In if (x >= 2) { :
the condition has length > 1 and only the first element will be used

The first element in c(0,10) is not greater than or equal to 2, and since your function was not designed to operate on a vector of values, it only evaluated the first element and returned a single 0 - which is what your call to print(graph) displays. This actually gave the same warning message as above:

> plot(graph)
Warning message:
In if (x >= 2) { :
the condition has length > 1 and only the first element will be used

You just need to vectorize your function:

f2 <- function(x)
{
ifelse(x>=2,-1+x+.3,0)
}
##
> f2(c(0,10))
[1] 0.0 9.3
##
graph2 <- ggplot(data.frame(x = c(0, 10)), aes(x))
graph2 <- graph2 + stat_function(fun=f2)
print(graph2)

Sample Image



Related Topics



Leave a reply



Submit