Thanks as ever to Kit Baum, a new package nicelabels is now downloadable from SSC. It may be thought of in conjunction with niceloglabels from the Stata Journal (first published 2018) and mylabels (on SSC since 2003). Stata 9 is required.

nicelabels suggests "nice" labels for a graph axis. Why does it appear only after niceloglabels, surely a special case? Is it needed at all -- because graph suggests nice labels by default and usually does a good job? The second question is part of an answer to the first. graph usually does do a good job, but there are instances in which people want to automate label choices, especially if several graphs are being produced by a script and Stata's defaults need a twist or two.

Example graphs here use this scheme, but choose your own favourite by all means.

Code:
. set scheme s1color
Given a minimum and maximum, the command suggests labels, by default "about 5" of them. There is a tight option and you can ask for more, say "about 10".

Code:
. nicelabels 142 233, local(foo)
step:      20
labels:    140 160 180 200 220 240

.
. nicelabels 142 233, local(foo) tight
step:      20
labels:    160 180 200 220

.
. nicelabels 142 233, local(foo) nvals(10)
step:      10
labels:    140 150 160 170 180 190 200 210 220 230 240

.
. nicelabels 142 233, local(foo) nvals(10) tight
step:      10
labels:    150 160 170 180 190 200 210 220 230
What is more common, I guess, is that you have variables in mind. The command calls summarize to look at the range, but we will do that too to show the result.

.
Code:
. sysuse census, clear
(1980 Census data by state)

. summarize medage

    Variable |        Obs        Mean    Std. dev.       Min        Max
-------------+---------------------------------------------------------
      medage |         50       29.54    1.693445       24.2       34.7

.
. nicelabels medage, local(agela)
step:      5
labels:    20 25 30 35

. nicelabels medage, local(agela) tight
step:      5
labels:    25 30

. nicelabels medage, local(agela) nvals(10)
step:      2
labels:    24 26 28 30 32 34 36

. nicelabels medage, local(agela) nvals(10) tight
step:      2
labels:    26 28 30 32 34
I like the third one best, so let's use it for real:

.
Code:
. nicelabels medage, local(agela) nvals(10)
step: 2
labels: 24 26 28 30 32 34 36

.
. egen rank = rank(medage), unique

.
. scatter medage rank, ylabel(`yla', ang(h)) xlabel(1 5(5)50) xtitle(Rank) ms(Oh) name(ALF1, replace)

.
. scatter medage rank, ylabel(`yla', ang(h) format(%2.0f)) xlabel(1 5(5)50) xtitle(Rank) ms(Oh) ///
|| scatter medage rank if inlist(rank, 1, 2, 50), ms(none) mlabel(state2) xscale(r(. 52)) legend(off) name(ALF2, replace)
Array
Array


These two graphs make a small point. The suggested labels and their display format are different choices.
.
Let's show some variants that might appeal:

* A variable is all positive, but regardless you want to insist on labels starting at zero:

Code:
. sysuse auto, clear
(1978 automobile data)

.
. summarize mpg, meanonly

.
. nicelabels 0 `r(max)', local(foo)
step:      10
labels:    0 10 20 30 40 50
* You want the observed minimum and maximum to be the outermost axis labels. This mix isn't guaranteed to be nice!


Code:
. nicelabels mpg, tight local(yla)
step:      10
labels:    20 30 40

.
. summarize mpg, meanonly

.
. local yla `yla' `r(min)' `r(max)'

.
. nicelabels weight, tight local(xla)
step:      1000
labels:    2000 3000 4000

.
. summarize weight, meanonly

.
. local xla `xla' `r(min)' `r(max)'

.
. scatter mpg weight, xla(`xla') yla(`yla', ang(h)) ms(Oh) name(ALF3, replace)
Array


* You want at least 5 labels. You can count the number suggested and tell nicelabels to try again if it does not suggest enough, Some degree of automation may be important to some users.

.
Code:
. nicelabels mpg, tight local(yla)
step:      10
labels:    20 30 40

.
. if wordcount("`yla'") < 5 nicelabels mpg, tight local(yla) nvals(10)
step:      5
labels:    15 20 25 30 35 40
* You want nice labels but they must show % too as a suffix. I used to be strongly against this as repetitive clutter but it can be helpful in some contexts. The deal here is that mylabels will do the % bit.
.
Code:
. sysuse census, clear
(1980 Census data by state)

. generate pc_older = 100 * pop65p / pop

. nicelabels pc_older, local(yla)
step: 5
labels: 0 5 10 15 20

. mylabels `yla', suffix(%) local(yla)
0 "0%" 5 "5%" 10 "10%" 15 "15%" 20 "20%"
.
. scatter pc_older medage, ylabel(`yla', ang(h)) xlabel(, format(%2.0f)) ytitle(% 65 and older) ms(none) mlabel(state2) mlabpos(0) name(ALF4, replace)
Array



Local macros are crucial here to how these commands work. You put labels in a local macro, like a bag. You can edit that bag and finish by passing it to a graph command.