Marker style

How markers are rendered is determined by the employed graph scheme. Use marker options to change the default look. The marker options can be specified for each series separately, but also as global options (in which case they are taken as defaults for the individual series):

. sysuse auto, clear
(1978 Automobile Data)

. keep if rep78>=3
(10 observations deleted)

. regress mpg headroom i.rep i.foreign
(output omitted)

. estimates store m1

. regress mpg headroom i.rep##i.foreign
(output omitted)

. estimates store m2

. coefplot (m1, msymbol(D) mlcolor(magenta) mfcolor(magenta*.3)) ///
>          (m2, msymbol(S)) ///
>          , mfcolor(white) msize(large)
Code
markers/markeropts.svg

The overall style of markers and their confidence intervals can also be changed using the pstyle() option (pstyles are named composite styles provided by the graph scheme). By default, coefplot uses the first pstyle for the first series, the second pstyle for the second series, and so on. Here is an example in which the styles are changed:

. coefplot (m1, pstyle(p3)) (m2, pstyle(p4))
Code
markers/pstyle.svg

Specifying pstyle() changes the pstyle both for markers and confidence spikes. To use a different pstyle for the confidence spikes, specify option cipts(pstyle()):

. coefplot (m1, ciopts(pstyle(p2))) (m2, pstyle(p3) ciopts(pstyle(p4)))
Code
markers/pstyle2.svg

Markers only / CIs only

To print markers without confidence intervals, you can specify the noci option; to print only the confidence intervals without markers, type cionly:

. sysuse auto, clear
(1978 Automobile Data)

. regress price mpg trunk length turn
(output omitted)

. coefplot (., noci label(Markers only)) ///
>          (., cionly label(CIs only) key(ci)) ///
>     , drop(_cons)
Code
markers/nocicionly.svg

Series without markers are omitted from the legend. This is why option key(ci) has been specified. The option requests generating a legend key for the confidence interval.

[top]

Marker labels

Values of point estimates

To add the values of the point estimates as marker labels, use the mlabel option, possibly together with format() to set the display format:

. sysuse auto, clear
(1978 Automobile Data)

. keep if rep78>=3
(10 observations deleted)

. regress mpg headroom i.rep##i.foreign
(output omitted)

. coefplot, xline(0) mlabel format(%9.2g) mlabposition(12) mlabgap(*2)
Code
markers/mlabel.svg

Stata graphs do not support background colors for marker labels, which makes labels unreadable if you place them on top of the markers using mlabposition(0). However, here is a workaround. The trick is to add a second "confidence interval" that is a bar of fixed width:

. mata: st_matrix("e(box)", (st_matrix("e(b)"):-2 \ st_matrix("e(b)"):+2))

. coefplot, xline(0) mlabel format(%9.2g) mlabposition(0) msymbol(i) ///
>     ci(95 box) ciopts(recast(. rbar) barwidth(. 0.35) color(. white))
Code
markers/mlabel2.svg

The dot in the suboptions within ciopts() specifies the "default" style; see help stylelists.

Instead of physically adding the coordinates of the bars as matrix e(box) to the estimation set, the coordinates can also be generated on the fly by the transform() option. In the example below, a second set of confidence limits is requested (the specified level does not matter as long as its unique) that is then transformed to b±2 with the help of transform() (also see Accessing internal temporary variables):

. sysuse auto, clear
(1978 Automobile Data)

. keep if rep78>=3
(10 observations deleted)

. regress mpg headroom i.rep##i.foreign
(output omitted)

. coefplot, xline(0) mlabel format(%9.2g) mlabposition(0) msymbol(i) ///
>     ci(95 99) transform(* = "cond(@==@ll2, @b-2, cond(@==@ul2, @b+2, @))") ///
>     ciopts(recast(. rbar) barwidth(. 0.35) color(. white))
Code
markers/mlabel2b.svg

Here is a further example where a box is placed around the numbers:

. coefplot, xline(0) mlabel format(%9.2g) mlabposition(0) msymbol(i) ///
>     ci(95 99) transform(* = "cond(@==@ll2, @b-2, cond(@==@ul2, @b+2, @))") ///
>     ciopts(recast(. rbar) barwidth(. 0.35) fcolor(. white) lwidth(. medium))
Code
markers/mlabel3.svg

A bit unfortunate might be that due to the box the exact location of a coefficient can no longer be seen in the graph. Here is an example where an additional vertical spike is added to mark the point estimates.

. coefplot, xline(0) mlabel format(%9.2g) mlabposition(0) msymbol(i) ///
>     ci(95 (b b) 99) transform(* = "cond(@==@ll3, @b-2, cond(@==@ul3, @b+2, @))") ///
>     ciopts(recast(. rcap rbar) msize(. 7 .) barwidth(. . 0.35) ///
>         fcolor(. . white) lwidth(. . medium))
Code
markers/mlabel4.svg

In the example, a confidence interval of zero width is used to produce the vertical spikes ((b b) means to include a confidence interval whose lower and upper limits are both equal to e(b)).

The plot might still not be optimal since for the first coefficient, the confidence interval is hidden behind the marker label box. Plotting the confidence intervals as bars can, for example, solve this problem:

. coefplot, xline(0) mlabel format(%9.2g) mlabposition(0) msymbol(i) ///
>     ci(95 (b b) 99) transform(* = "cond(@==@ll3, @b-2, cond(@==@ul3, @b+2, @))") ///
>     ciopts(recast(rbar rcap rbar) bstyle(ci2 . .) msize(. 7 .) ///
>         barwidth(0.5 . 0.35) fcolor(. . white) lwidth(. . medium))
Code
markers/mlabel5.svg
[top]

String expressions

The default for the mlabel option is to display the values of the point estimates as marker labels. To display p-values instead of point estimates, for example, you could type mlabel(@pval) (see Accessing internal temporary variables for available @-variables). Furthermore, a string expression may be provided as an argument to mlabel() to construct more complicated marker labels, as in the following example:

. sysuse auto, clear
(1978 Automobile Data)

. keep if rep78>=3
(10 observations deleted)

. regress mpg headroom i.rep##i.foreign
(output omitted)

. coefplot, xline(0) mlabposition(1) mlabgap(*2) ///
>     mlabel("{it:p} = " + string(@pval,"%9.3f"))
Code
markers/strmlabel.svg

Here is a more complicated example that displays different symbols as marker labels depending on the size of the p-value:

. coefplot, xline(0) mlabposition(1) ///
>     mlabel(cond(@pval<.001, "***", ///
>            cond(@pval<.01, "**",   ///
>            cond(@pval<.05, "*",    ///
>            cond(@pval<.1, "+", ""))))) ///
>     note("+ p < .1, * p < .05, ** p < .01, *** p < .001")
Code
markers/strmlabel2.svg
[top]

Custom labels

To create custom labels for specific markers, you can use the mlabels() option. For example, the following graph includes information on hypotheses (positive effect or null effect) as marker labels:

. sysuse auto, clear
(1978 Automobile Data)

. regress price mpg trunk length turn if foreign==0
(output omitted)

. estimates store domestic

. regress price mpg trunk length turn if foreign==1
(output omitted)

. estimates store foreign

. coefplot (domestic, mlabels(length = 1 "+" * = 11 "0")) ///
>          (foreign, mlabels(trunk length = 1 "+" * = 11 "0")) ///
>     , drop(_cons) xline(0) note("Hypotheses: + positive effect; 0 no effect")
Code
markers/cmlabel.svg

Of course, you can also attach labels only to some of the coefficients and you can use marker label options to affect the rendering of the labels:

. coefplot ///
>     (domestic, mlabels(trunk = 12 "I don't like that this is not significant") ///
>         mlabangle(45) mlabgap(2) mlabsize(medium) mlabcolor(red)) ///
>     (foreign, mlabels(length = 1 "This is the {bf:only} significant effect!") ///
>         mlabtextstyle(small_label)) ///
>     , drop(_cons) xline(0) 
Code
markers/cmlabel2.svg
[top]

Weighted markers

To scale the size of markers use the weight() option. In the following example, the sizes of the marker symbols are proportional to the inverse of the standard errors:

. sysuse auto, clear
(1978 Automobile Data)

. regress price mpg trunk length turn
(output omitted)

. coefplot, weight(1/@se) ms(oh) drop(_cons) xline(0) 
Code
markers/weight.svg

@se is an internal variable provided by coefplot. See Accessing internal temporary variables in the help file for the list of available internal variables. Additional internal variables can be made available through the aux() option. Here is an example in which the markers for group means are scaled by the number of observations in each group:

. sysuse auto, clear
(1978 Automobile Data)

. mean price, over(rep78)

Mean estimation                   Number of obs   =         69

            1: rep78 = 1
            2: rep78 = 2
            3: rep78 = 3
            4: rep78 = 4
            5: rep78 = 5

--------------------------------------------------------------
        Over |       Mean   Std. Err.     [95% Conf. Interval]
-------------+------------------------------------------------
price        |
           1 |     4564.5      369.5      3827.174    5301.826
           2 |   5967.625   1265.494      3442.372    8492.878
           3 |   6429.233   643.5995       5144.95    7713.516
           4 |     6071.5   402.9585      5267.409    6875.591
           5 |       5913   788.6821      4339.209    7486.791
--------------------------------------------------------------

. matrix list e(_N)

e(_N)[1,5]
     price:  price:  price:  price:  price:
         1       2       3       4       5
r1       2       8      30      18      11

. coefplot, ciopts(recast(rcap)) aux(_N) weight(@aux1) mfcolor(*.6)
Code
markers/weight2.svg

The mean command returns the group sizes in vector e(_N) from where they can be read by typing aux(_N).

If you apply the weight() option in a graph that contains multiple series, marker sizes will be computed for each series separately. Consider the following example:

. sysuse auto, clear
(1978 Automobile Data)

. regress price mpg trunk length turn if foreign==0
(output omitted)

. estimates store domestic

. regress price mpg trunk length turn if foreign==1
(output omitted)

. estimates store foreign

. coefplot domestic foreign, weight(1/@se) ms(oh) drop(_cons) xline(0)
Code
markers/weight3.svg

The sizes of the marker can be compared within series, that is, within series they are proportional to the inverse of the standard errors. Between series, however, this relation does not hold. Here is a picture in which the same information is displayed while applying weight() to all markers at once (see the help file for information on options asequation and swapnames):

. coefplot (domestic foreign), asequation swapnames nolabel ///
>     weight(1/@se) ms(oh) drop(_cons) xline(0)
Code
markers/weight4.svg

As is evident, to be comparable, the sizes of the markers for domestic have to be made a bit larger. A trick to make marker sizes comparable across series is to include all data in each series, so that the same information is used to determine the marker sizes, but then suppress the extra estimates by setting them to missing using the transform() option:

. coefplot (foreign, transform(* = .) \ domestic) ///
>          (domestic, transform(* = .) \ foreign) ///
>     , weight(1/@se) ms(oh) drop(_cons) xline(0)
(transform missing for some coefficients or CIs)
Code
markers/weight5.svg