Labeling logarithmic axes in Stata graphs

When you want to use logarithmic axes in Stata graphs, Stata doesn’t exactly make your life easy. Specifying the logarithmic scales is easy enough (via the xscale(log) and xscale(log) options, but making them look nice is a different story. For example, you’d probably want the labeling of the axes to keep pace with the logarithmic scale, (1, 10, 100, 1000…) instead of (1, 2 ,3, 4…). But while labeling a range of values on a normal scale is straightforward using a “rule” (e.g., xlabel(1(10)1000) labels the x-axis from 1 to 1000 in steps of 10), there are no multiplicative versions of these rules. Thus, to label a logarithmic x-axis as (1, 10,100,1000…), you would have to specify each of these values separately in the xlabel() option, which quickly becomes cumbersome, especially if you need to change the labeling later on.
In addition, you might want to place ticks on the axes in a logarithmic way (5, 10, 50, 100, 500, 1000…etc), but you run into the same problem, but worse, because there are more ticks than labels.
Finally, it is often useful to display labels on logarithmic axes as powers of ten (or another base), to avoid very long numbers on the axes. Stata has the %e format which you can use in axis labels, but I don’t find the results very nice looking (e.g., 100 is displayed as “1.0e+02”). I would rather see something like “10^2” (actual superscripts would be even nicer, but I didn’t manage to make that look decent using {superscript:}; the superscripts end up too far from the other characters).

The code below solves  these problems by automating the specification of the location of labels and ticks, and putting these entire specifications in local macros which are then fed to the graph command. In a similar way, the problem of the formatting of the labels is solved by specifying value labels in a macro, feeding that macro to a label define command, and using the resulting value labels in a suboption to xlabel(). Note that for this to work from a do-file, you need to run the code for the macros (2-4) and the code for the graph (5) together.

I admit that this code is altogether longer than what you would need to specify all the values manually, but if you need to change the specification a couple of times, this is still quicker. And of course, you could use it for multiple graphs.

// (1) Generate some data to plot
set obs 10000
gen x = _n
gen y = 1000*x^-.5

// (2) Setting up the axis label values
local labels = ""
forval x = 1(1)4{
  local y = 10^`x'
  local labels =  "`labels'`y' "

// (3) Setting up value labels (note the use of compound quotes)
local vallabel = ""
forval x = 1(1)4{
  local y = 10^`x'
  local vallabel =  `"`vallabel'`y' "10^`x'" "'
label define expon `vallabel',replace
label val x expon
label val y expon

// (4) Setting up ticks
local ticks = ""
forval x = 0(1)3{
  forval z = 1(1)9{
    local y = 10^(`x')*`z'
    local ticks = "`ticks'`y' "

// (5) The actual graph
twoway  scatter y x, ///
          yscale(log) ///      The actual log scales defined here
          xscale(log) ///
          xlabel(`labels', ///  Using the axis label values defined in (2)
            grid ///
            valuelabel) ///     Using value labels defined in (3)
          xmtick(`ticks') ///  Using the axis tick values defined in (4)
          ylabel(`labels', ///
            grid ///
            angle(horizontal) ///
            valuelabel) ///
          ymtick(`ticks') ///
          aspect(1) ///

The resulting graph: