#delim ; prog regaxis, rclass; version 10.0; /* Generate regular linear axis range and tick mark list for a list of variables and/or included constants. *! Author: Roger Newson *! Date: 12 January 2012 */ syntax [ varlist(numeric default=none) ] [if] [in] [ , CYcle(numlist max=1 >0) PHases(numlist) MARgins(numlist max=2 >=0) PCRatios(numlist) MCRatios(numlist max=2 >=0) CBase(numlist max=1 >1) CPPower(numlist max=1 >0) INClude(numlist) MAXTicks(numlist integer max=1 >=2 <=1600) SINgleok LRAnge(name local) LTIcks(name local) LRMIn(name local) LRMAx(name local) LTMIn(name local) LTMAx(name local) LNTick(name local) LVMIn(name local) LVMAx(name local) GRAnge(name) GTIcks(name) GRMIn(name) GRMAx(name) GTMIn(name) GTMAx(name) GNTIck(name) GVMIn(name) GVMAx(name) ]; /* -cycle()- specifies the cycle length for the tick marks. -phases()- specifies a list of phases, generating tick marks with values equal to phase_i+k*cycle, where k is an integer and phase_i is a phase. -margins()- specifies the margins separating the range minimum and maximum from the minimum and maximum ticks. -pcratios()- specifies a list of phase/cycle ratios used for defining the default -phases()- option (ignored if -phases()- is specified). -mcratios()- specifies a list of margin/cycle ratios used for defining the default -margins()- option (ignored if -margins()- is specified). -cbase()- specifies a logarithmic base used for defining the default -cycle()- option (ignored if -cycle()- is specified). -cppower()- specifies the number of cycles per integer power of the -cbase()- option used for defining the default -cycle()- option (ignored if -cycle()- is specified). -include()- specifies a list of numeric constants to be included in the range spanned by the ticks. -maxticks()- specifies the maximum number of ticks. -singleok- specifies that the output tick list can be a single tick if the data range contains only a single value equal to a candidate tick position. -lrange()- is the name of a local macro to store the ramge. -lticks()- is the name of a local macro to store the ticks. -lrmin()- is the name of a local macro to store the range minimum. -lrmax()- is the name of a local macro to store the range maximum. -ltmin()- is the name of a local macro to store the minimum tick. -ltmax()- is the name of a local macro to store the maximum tick. -lntick()- is the name of a local macro to store the number of ticks. -lvmin()- is the name of a local macro to store the minimum value present in the -varlist- and/or the -include()- option. -lvmax()- is the name of a local macro to store the maximum value present in the -varlist- and/or the -include()- option. -grange()- is the name of a global macro to store the range. -gticks()- is the name of a global macro to store the ticks. -grmin()- is the name of a global macro to store the range minimum. -grmax()- is the name of a global macro to store the range maximum. -gtmin()- is the name of a global macro to store the minimum tick. -gtmax()- is the name of a global macro to store the maximum tick. -gntick()- is the name of a global macro to store the number of ticks. -gvmin()- is the name of a global macro to store the minimum value present in the -varlist- and/or the -include()- option. -gvmax()- is the name of a global macro to store the maximum value present in the -varlist- and/or the -include()- option. */ * Check for illegal option values and generate default option values if necessary *; if "`cbase'"=="" {; local cbase=10; }; if "`cppower'"=="" {; local cppower=`cbase'+`cbase'; }; if "`maxticks'"=="" {;local maxticks=25;}; * Find minimum and maximum values in data and -include()- option *; tempname vmin vmax vmincur vmaxcur; scal `vmin'=.; scal `vmax'=.; * Variables *; if "`varlist'"!="" {; marksample touse, novarlist; foreach X of var `varlist' {; qui summ `X' if `touse', meanonly; if r(min)<`vmin' | missing(`vmin') {;scal `vmin'=r(min);}; if r(max)>`vmax' | missing(`vmax') {;scal `vmax'=r(max);}; }; }; * Additional constants *; if "`include'"!="" {; foreach X of numlist `include' {; if `X'<`vmin' | missing(`vmin') {;scal `vmin'=`X';}; if `X'>`vmax' | missing(`vmax') {;scal `vmax'=`X';}; }; }; * Set to zero if all values missing *; if missing(`vmin') {;scal `vmin'=0;}; if missing(`vmax') {;scal `vmax'=0;}; * Compute default -cycle()- option if absent *; if "`cycle'"=="" {; if `vmin'==`vmax' {; local cycle=1; }; else {; local cycle=`cbase'^( ceil(log(`vmax'-`vmin')/log(`cbase')) ) / `cppower'; }; }; * Compute default -pcratios()- and -phases()- options if absent *; if "`pcratios'"=="" {; local pcratios=0; }; if "`phases'"=="" {; foreach R of num `pcratios' {; local phasecur=`R'*`cycle'; local phases "`phases' `phasecur'"; }; }; * Compute default -mcratios()- and -margins()- options if absent *; if "`mcratios'"=="" {; local mcratios "0 0"; }; else {; local nmarg: word count `mcratios'; if `nmarg'==1 {; local mcratios "`mcratios' `mcratios'"; }; }; if "`margins'"=="" {; foreach R of num `mcratios' {; local margcur=`R'*`cycle'; local margins "`margins' `margcur'"; }; }; else {; local nmarg: word count `margins'; if `nmarg'==1 {; local margins "`margins' `margins'"; }; }; * Generate initial phase-specific tick mark lists (without allowing their total to exceed the maximum Stata numlist length) *; local mnumlist=1600; local nnumlist=0; local nphase: word count `phases'; forv i1=1(1)`nphase' {; local phasecur:word `i1' of `phases'; local kmin=floor((`vmin'-`phasecur')/`cycle'); local kmax=ceil((`vmax'-`phasecur')/`cycle'); if `kmin'==`kmax' {; local kmin=`kmin'-1; local kmax=`kmax'+1; }; local nnumlist=`nnumlist'+`kmax'-`kmin'+1; * Fail if there are too many candidate ticks *; if `nnumlist'>`mnumlist' {; disp as error "Options require the creation of a numlist" _n "with over `mnumlist' values." _n "To reduce the size of the largest numlist required," _n "increase the cycle() or base() option." _n; error 123; }; local pticks`i1'=""; forv i2=`kmin'(1)`kmax' {; local tickcur=`phasecur'+`cycle'*`i2'; local pticks`i1' "`pticks`i1'' `tickcur'"; }; }; * Generate first draft of tick mark list *; local ticks=""; forv i1=1(1)`nphase' {;local ticks "`ticks'`pticks`i1''";}; local ticks: list uniq ticks; numlist "`ticks'", sort; local ticks "`r(numlist)'"; local ntick: word count `ticks'; * Trim tick mark list *; local kmin=1; local tickcur: word 1 of `ticks'; while `tickcur'<=`vmin' {; local kmin=`kmin'+1; local tickcur: word `kmin' of `ticks'; }; local kmin=`kmin'-1; local kmax=`ntick'; local tickcur: word `ntick' of `ticks'; while `tickcur'>=`vmax' {; local kmax=`kmax'-1; local tickcur: word `kmax' of `ticks'; }; local kmax=`kmax'+1; if (`kmin'==`kmax') & ("`singleok'"=="") {; local kmin=`kmin'-1; local kmax=`kmax'+1; }; local tempticks=""; forv i1=`kmin'(1)`kmax' {; local tickcur: word `i1' of `ticks'; local tempticks "`tempticks' `tickcur'"; }; local ticks "`tempticks'"; macro drop tempticks; local ntick: word count `ticks'; * Reduce tick mark list if it is too long *; local oldcycle=`cycle'; while `ntick'>`maxticks' {; if `maxticks'==2 {; * Remove middle ticks *; local tickmin: word 1 of `ticks'; local tickmax: word `ntick' of `ticks'; local ticks "`tickmin' `tickmax'"; local ntick=2; }; else {; * More than 2 ticks allowed - reduce numbers in some other way *; if `nphase'>1 {; * Remove last phase *; local nphase=`nphase'-1; local ticks=""; forv i1=1(1)`nphase' {;local ticks "`ticks'`pticks`i1''";}; local ticks: list uniq ticks; numlist "`ticks'", sort; local ticks "`r(numlist)'"; local ntick: word count `ticks'; }; else {; * Only one phase left - increment cycle *; local cycle=`cycle'+`oldcycle'; local phasecur: word 1 of `phases'; local kmin=floor((`vmin'-`phasecur')/`cycle'); local kmax=ceil((`vmax'-`phasecur')/`cycle'); if `kmin'==`kmax' {; local kmin=`kmin'-1; local kmax=`kmax'+1; }; local ticks=""; forv i2=`kmin'(1)`kmax' {; local tickcur=`phasecur'+`cycle'*`i2'; local ticks "`ticks' `tickcur'"; }; local ntick: word count `ticks'; }; * Trim tick mark list *; local kmin=1; local tickcur: word 1 of `ticks'; while `tickcur'<=`vmin' {; local kmin=`kmin'+1; local tickcur: word `kmin' of `ticks'; }; local kmin=`kmin'-1; local kmax=`ntick'; local tickcur: word `ntick' of `ticks'; while `tickcur'>=`vmax' {; local kmax=`kmax'-1; local tickcur: word `kmax' of `ticks'; }; local kmax=`kmax'+1; if (`kmin'==`kmax') & ("`singleok'"=="") {; local kmin=`kmin'-1; local kmax=`kmax'+1; }; local tempticks=""; forv i1=`kmin'(1)`kmax' {; local tickcur: word `i1' of `ticks'; local tempticks "`tempticks' `tickcur'"; }; local ticks "`tempticks'"; macro drop tempticks; local ntick: word count `ticks'; }; }; * Create axis range *; local lmarg: word 1 of `margins'; local umarg: word 2 of `margins'; local tmin: word 1 of `ticks'; local tmax: word `ntick' of `ticks'; local rmin=`tmin'-`lmarg'; local rmax=`tmax'+`umarg'; local range "`rmin' `rmax'"; * Return results *; return scalar vmax=`vmax'; return scalar vmin=`vmin'; return scalar ntick=`ntick'; return scalar tmax=`tmax'; return scalar tmin=`tmin'; return scalar rmax=`rmax'; return scalar rmin=`rmin'; return local ticks "`ticks'"; return local range "`range'"; * Set local and global macro options *; foreach X in range ticks {; if "`l`X''"!="" {; c_local `l`X'' "``X''"; }; if "`g`X''"!="" {; global `g`X'' "``X''"; }; }; foreach X in rmin rmax tmin tmax ntick vmin vmax {; if "`l`X''"!="" {; c_local `l`X''=``X''; }; if "`g`X''"!="" {; global `g`X''=``X''; }; }; end;