[R] How to add a top X-axis with a different logarithmic scale?

Marc Schwartz MSchwartz at MedAnalytics.com
Fri Mar 26 15:18:58 CET 2004


On Thu, 2004-03-25 at 17:42, Itay Furman wrote:
> Thanks, Marc, for your reply.

Happy to help.

> I didn't explain myself well, but thanks to you I think I could 
> phrase my question better.
> 
> I want to be able to compute new tick-positions for the top 
> X-axis.
> 
> Why new ticks positions?
> See in the example below (that uses your contributed minimal
> code :-) what happens to the labels when you
> replace 4e06 with 4.1e06.


OK.  I believe that I have a reasonable way of getting this, while
letting R do the 'heavy lifting'.


x <- c(1.1 * 1:4, 25 * 1:5) / 50e+03
y <- c(0.15 * 1:4, 0.6 + 0.05 * 1:5)

old.par <- par(no.readonly=TRUE)
xlim <- range(x)
ylim <- c(0, 1)

plot(x, y, type = "l", log = "x", xlim = xlim, ylim = ylim)

# Set scaling factor
sf <- 4.1e06

# Now set up new upper x axis range based upon sf
xlim.3 <- xlim * sf

# Now create axis tick mark positions for the new scale
# We will use axTicks() and need to consider that when log scales
# are in use, things get a little hairy. So:

# We need to define a new par("xaxp") based upon xlim.3
# We also need to define a new par("usr")[1:2], which is the
# range of the x axis. When log scales are in use, the actual
# range is 10 ^ par("usr")[1:2], so we take these values and rescale
# using 'sf'. We then convert the result back to log scales using
# log10() to get 'usr':
tm <- axTicks(3, axp = c(range(xlim.3), 3), 
              usr = log10(10 ^ par("usr")[1:2] * sf), 
              log = TRUE)

# Now we can draw axis 3, using the adjusted scaling
# We need to adjust the scaled tick mark positions back
# to the actual range of the x axis, so 'at' is adjusted
# here by the sf, but the labels stay as rescaled:
axis(3, at = tm / sf, labels = tm)


par(old.par)




> Also, I assume from your example that using plot.new() + 
> plot.window() + lines(), instead of plot() will not make a 
> difference in the solution. As I mentioned in _my_ example code:
> for a production I will overlay several curves and will
> use lines().

Correct. The key here with multiple series is that you need to consider
the appropriate ranges for each x and y series when combined, so that
the plot region is properly scaled and all values are within the plot
region. So you need to account for overlapping (but presumably not
exactly the same) x and y axis ranges. 

The R function matplot() is probably the best way of doing this with a
single function. See ?matplot.

In the above code, replace the use of plot() with matplot(). The first
two arguments in matplot() will be your x vectors as a matrix and your y
vectors as a matrix. Let's say you have 3 series. If you only have one
set of y values, skip the cbind() step and just use 'y'. matplot() will
recycle the y values as required.

You would also need to adjust the calculation of 'xlim' as:

xlim <- range(c(x1, x2, x3))

We'll keep 'ylim' as is for now.

Combine your x values and y values into matrices:

x <- cbind(x1, x2, x3)
y <- cbind(y1, y2, y3)

Then use matplot():

matplot(x, y, type = "l", log = "x", xlim = xlim, ylim = ylim)

Now do the adjustments for the upper x axis:

sf <- 4.1e06
xlim.3 <- xlim * sf
tm <- axTicks(3, axp = c(range(xlim.3), 3), 
              usr = log10(10 ^ par("usr")[1:2] * sf), 
              log = TRUE)
axis(3, at = tm / sf, labels = tm)


I think that should get you there. Be aware that with log scaling,
substantial changes in ranges can have a material impact on the axis
related calculations, so you may need to play around with things if you
use scaling factors that are substantially larger or smaller than what
we have here. Some scaling values may cause problems with this approach.

I hope that helps.

Marc




More information about the R-help mailing list