help addlegendhttps://github.com/benjann/addlegend/

Title

addlegend -- Utility to add a custom legend to a twoway graph

Syntax

addlegend [graphname] [numlist]
[, options ]
: keylist

_mklegend [graphname] [numlist]
[, options ]
: keylist

where keylist is

key [ || key [...]]

and key is

[symboldef]
"text" ["text" [...]]
[, symopts
txtopts ]

and symboldef is - or

(symlist [, symopts])
[(symlist [, symopts]) [...]]

and symlist is

[symbol [symbol [...]]]

and symbol is

symbolstyle marker

line line

rline double line

area area

bar bar

cap capped line; can also type rcap

capsym line capped with symbols; can also type rcapsym

options

Main
lskip(#) baseline skip between legend keys

frame[(subopts)] draw
frame around legend

margin(marginstyle) reset margin of graph region

nodraw do not update graph window

symopts
y(#) or Y(#) vertical position of legend key, in percent or units of Y-axis

x(#) or X(#) horizontal position of legend key, in percent or units of X-axis

h(#) or H(#) height of key's symbol, in percent or units of Y-axis

w(#) or W(#) width of key's symbol, in percent or units of X-axis

marker_options options affecting look of markers

line_options options affecting look of lines

area_options options affecting look of areas

txtopts
ty(#) or TY(#) vertical position of text, relative to symbol, in percent or units

tx(#) or TX(#) horizontal position of text, relative to symbol, in percent or units

tw(#) or TW(#) with of text, in percent or units; only relevant for frame()

text(textbox_options) options affecting look of text

Description

addlegend creates a custom legend and adds it to an existing
twoway graph, thereby removing the legend created by Stata's
legend() option. In contrast to the
legend() option, addlegend can combine multiple
symbols in a single legend key and the keys can be freely positioned on the
plot. addlegend requires the addplot command
(Jann 2015) to be
installed on the system; type ssc install addplot to install
addplot.

Argument graphname selects the memory graph to be affected. The default
is to use the current (topmost) graph. Argument numlist
selects the subgraph(s) to be affected if the graph has been created using
graph combine or the by() option. The default
is to modify all twoway subgraphs found in the graph.

addlegend uses the dimensions of the axes of the selected graph (or
the first selected subgraph) to determine the position and size of the
legend. Use options
y(),
x(),
h(),
w(),
ty(),
tx(), and
tw()
(as well as similar suboptions within the frame() option)
to override the default behavior. These options come in two flavors,
lower case and upper case. Use the lower-case variant, e.g. y(),
to specify a setting in percent of the range of the relevant axis; use the
upper-case variant, e.g. Y(), to specify a setting in original units
of the axis. If both are specified, the upper-case variant takes precedence
over the lower-case variant.

Options can be specified at different levels, at the global level, at the
level of a legend key, or at the level of a key's symbol. Upper-level
settings are used as defaults for lower-level settings, and options
specified a lower level take precedence over options specified at an upper
level (ignoring case; for example, y() specified at a lower level
takes precedence over Y() specified at an upper level). Furthermore,
if specified at the level of a legend key, options
y(),
x(),
h(),
w(),
ty(),
tx(), and
tw()
are sticky in the sense that they change the default settings for
subsequent keys. This is not true for options specified at the
level of a key's symbol, which are non-sticky and only affect the current
symbol. Finally, if both y() and
Y() are omitted at the level of a legend key,
the vertical position the key is determined as y - lskip *
h, where y and h are the position and symbol height of the
previous key and lskip is the baselineskip as set by option
lskip().

Command _mklegend is the engine behind addlegend. It analyses
the selected graph, creates the code of the custom legend (a set of
twoway scatteri commands), and stores it in macro
r(legend). addlegend then applies addplot to add the
contents of r(legend) to the selected graph.

Options

Main

lskip(#) sets the baseline skip between legend keys as a factor of
the symbol height; the default is lskip(1.5). Option lskip() has no
effect on legend keys that are positioned explicitly by the
y() option.

frame[(subopts)] draws a frame around the legend. The
size and position of the frame will be determined automatically, but you will
most likely have to adjust its width using suboption w() (or by setting
the text width using option tw()). Furthermore, you
may want to adjust the padding (inner margin of the frame) using suboptions ym() and
xm(). subopts are as follows.

ym(#) and YM(#) set the vertical padding (margin at top
and bottom between legend keys and frame) that is applied unless the frame
is positioned manually using y() and h(). The default is
ym(2.5). YM() takes precedence over ym().

xm(#) and XM(#) set the default horizontal padding (margin at left
and right between legend keys and frame) that is applied unless the frame
is positioned manually using x() and w(). The default is
xm(2). XM() takes precedence over xm().

y(#) and Y(#) set the position of the top edge of the frame, in percent
of the range of the Y-axis or in units of the Y-axis, respectively. Y()
takes precedence over y(). Together, y() and x() determine the
position of the upper-left corner of the frame.

x(#) and X(#) set the position of the left edge of the frame,
in percent of the range of the X-axis or in units of the X-axis,
respectively. X() takes precedence over x().

h(#) and H(#) set the height of the frame, in percent of the
range of the Y-axis or in units of the Y-axis, respectively. H()
takes precedence over h().

w(#) and W(#) set the width of the frame, in percent of the
range of the X-axis or in units of the X-axis, respectively. W()
takes precedence over w().

area_options are options affecting the look of the fill and outline
of the frame; see help area_options. By default, options
lstyle(foreground) and fcolor(white) will be applied. For
example, type fcolor(none) for a frame without fill.

margin(marginstyle) resets the margin of the graph region; see help
marginstyle. This is useful if you want to place the legend in
the margin of the graph instead of in the plot region. margin() has
no effect if specified with _mklegend.

nodraw causes the graph data to be modified without updating the
display in the graph window. Use graph display
to view the modified graph after applying addlegend with option
nodraw. nodraw has no effect if specified with _mklegend.

symopts

y(#) and Y(#) set the vertical position of the (first) legend
key (i.e., the midpoint of the vertical space allocated for the key's
symbol), in percent of the range of the Y-axis or in units of the Y-axis,
respectively. The default is x(95). Y() takes precedence over
y().

Specifying y() or Y() only sets the position of the current
(first) key. The positions of subsequent keys are determined as
y - lskip * h, where
y and h are the position and symbol height of the previous key
and lskip is the baselineskip as set by option
lskip().

x(#) and X(#) set the horizontal position of the legend key
(i.e., the left edge of the space allocated for the key's symbol, or the
right edge if the key's width is negative), in percent of the range of the
X-axis or in units of the X-axis, respectively. The default is
x(2). X() takes precedence over y().

h(#) and H(#) set the height to be allocated for the legend
key's symbol, in percent of the range of the Y-axis or in units of the
Y-axis, respectively. The default is h(5). H() takes precedence
over h().

w(#) and W(#) set the width to be allocated for the legend
key's symbol, in percent of the range of the X-axis or in units of the
X-axis, respectively. The default is w(5). W() takes precedence
over w().

marker_options are options affecting the look of the markers included
in the legend key's symbol; see help marker_options. If omitted,
option pstyle() will be set automatically based on the order of the
key.

line_options are options affecting the look of the lines included
in the legend key's symbol; see help line_options.

area_options are options affecting the look of the areas included
in the legend key's symbol; see help area_options.

txtopts

ty(#) and TY(#) set the vertical offset of the text, relative
to the position of the key's symbol, in percent of the range of the Y-axis
or in units of the Y-axis, respectively. The default is
ty(0). TY() takes precedence over ty().

tx(#) and TX(#) set the horizontal offset of the text, relative
to the position of the key's symbol, in percent of the range of the X-axis
or in units of the X-axis, respectively. The default is to set the offset
to the width of the key's symbol plus 1 percent of the width of the
X-axis. TX() takes precedence over tx().

tw(#) and TW(#) set the with of the text, in percent of the
range of the Y-axis or in units of the Y-axis, respectively. The default is
tw(20). This setting only affects how
frame() determines the size of the frame; it is
irrelevant if frame() is omitted. TW() takes
precedence over tw().

text(textbox_options) specifies options affecting look of the text,
such as its size, color, or justification; see help
textbox_options. If omitted, options placement() and
justification() are set automatically depending on the sign of
tx().

Examples

Composite symbols

The following example illustrates how to create a composite symbol.

. sysuse auto

. twoway (sc mpg turn, msize(large) ms(Oh)) (sc mpg turn, msize(large) ms(X) pstyle(p1)) (lfit mpg turn, pstyle(p2))

. `"addlegend, X(45) frame: (Oh X, msize(large)) "Mileage (mpg)" || (line) "Fitted values""'

Option X(45) has been used to shift the legend to the right of the
graph (the left edge of the space allocated for the keys' symbols is positioned
at X = 45). By default, the legend is placed in the top-left corner.

Custom positioning of legend keys

The following example illustrates how the legend keys can be placed in different
locations on the plot.

. sysuse auto

. twoway (hist weight if foreign==0, psty(p1bar) color(%50)) (hist weight if foreign==1, psty(p2bar) color(%50))

. `"addlegend, lskip(0) color(%50): (bar) "Domestic", X(4840) W(-300) || (bar) "Foreign", X(1760) W(300)"'

Note how setting the symbol width to a negative value changes the default
placement of the key's text.

Headings

To create a heading that is aligned with the keys' symbols, type

"text" ["text" [...]]

Alternatively, to create a heading that is aligned with the
keys' texts, type

- "text" ["text" [...]]

The following example illustrates the difference.

. sysuse uslifeexp

. twoway (connect le_f le_m year)

. `"addlegend: "Heading aligned with symbol" || (line) () "female" || - "Heading aligned with text" || (line) () "male""'

Placing the legend outside of the plot region

If you want to place the legend outside of the plot region, use the
margin() option to make sure that there is enough
space for the legend in the graph's margin.

. sysuse auto

. twoway (sc mpg turn, msize(large) ms(Oh)) (sc mpg turn, msize(large) ms(X) pstyle(p1)) (lfit mpg turn, pstyle(p2))

. `"addlegend, x(105) margin(r=40): (Oh X, msize(large)) "Mileage (mpg)" || (line) "Fitted values""'

Add legend to subgraph

In case of a graph that contains multiple subgraphs, specify addlegend #
to add the legend to subgraph # (by default, the legend is added to all
subgraphs).

. sysuse auto

. scatter mpg trunk weight, legend(off) name(weight, replace) nodraw

. scatter mpg trunk price, legend(off) name(price, replace) nodraw

. graph combine weight price

. `"addlegend 2, x(60) tw(35) frame: () "Mileage per gallon" || () "Trunk space""'

Note that specifying a key's symbol as () selects the plot's default
marker symbol.

Use of _mklegend

addlegend is implemented as a wrapper for _mklegend followed by
addplot. In some cases you might want to apply _mklegend
manually instad of using addlegend. Note that _mklegend stores
the legend's code in macro r(legend).

For example, addlegend always removes the legend created by Stata's
legend() option. The following example illustrates how you could
create a graph that includes both types of legends.

. sysuse auto

. twoway (sc mpg turn) (lfit mpg turn)

. `"_mklegend, x(60): () "Observations" || (line) "Linear fit""'

. `"addplot: `r(legend)', norescaling legend(order(1 "Mileage (mpg)" 2 "Fitted values"))"'

Rather than using addplot you can also include the code generated by
_mklegend directly in a twoway command:

. sysuse auto

. twoway (sc mpg turn) (lfit mpg turn), nodraw

. `"_mklegend, x(80) frame: () "Observations" || (line) "Linear fit""'

. twoway (sc mpg turn) (lfit mpg turn) `r(legend)', legend(off)

A further option is to include the legend's code in an
addplot() option:

. sysuse auto

. lpoly weight length, degree(1) ci nodraw

. `"_mklegend, tw(25) frame: () "data" || (area, astyle(ci)) (line) "lopoly fit and 95% CI""'

. lpoly weight length, degree(1) ci legend(off) addplot(`r(legend)')

Returned results

Scalars:

r(Ymin) minimum of the graph's Y axis

r(Ymax) maximum of the graph's Y axis

r(Xmin) minimum of the graph's X axis

r(Xmax) maximum of the graph's X axis

r(lskip) value of option lskip()

r(y) (initial) value of option y()

r(x) (initial) value of option x()

r(h) (initial) value of option h()

r(w) (initial) value of option w()

r(ty) (initial) value of option ty()

r(tx) (initial) value of option tx()

r(tw) (initial) value of option tw()

r(Y) (initial) value of option Y()

r(X) (initial) value of option X()

r(H) (initial) value of option H()

r(W) (initial) value of option W()

r(TY) (initial) value of option TY()

r(TX) (initial) value of option TX()

r(TW) (initial) value of option TW()

r(fr_y) value of option y() in frame()

r(fr_x) value of option x() in frame()

r(fr_h) value of option h() in frame()

r(fr_w) value of option w() in frame()

r(fr_ym) value of option ym() in frame()

r(fr_xm) value of option xm() in frame()

r(fr_Y) value of option Y() in frame()

r(fr_X) value of option X() in frame()

r(fr_H) value of option H() in frame()

r(fr_W) value of option W() in frame()

r(fr_YM) value of option YM() in frame()

r(fr_XM) value of option XM() in frame()

Macros:

r(legend) code that generates the legend

References

Jann, B. 2015. A note on adding objects to an existing twoway graph. The Stata Journal
15(3): 751-755. https://doi.org/10.1177/1536867X1501500308

Author

Ben Jann, University of Bern, ben.jann@unibe.ch

Thanks for citing this software as follows:

Jann, B. 2026. addlegend: Stata utility to add a custom legend to a twoway graph. Available from
https://github.com/benjann/addlegend/.

Also see

Online: help for
graph twoway, addplot (from SSC)