Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • How to create a two-facet figure combining a bar chart and a coefficient plot with different y-scales

    Hi everyone,

    I would like to create in Stata a figure similar to the one shown at the end of this post, using the example dataset provided (shown with dataex).

    The figure has two vertically stacked panels (facets), combining a bar chart in the upper panel and a coefficient plot in the lower plot. Ideally, the y-scale in the upper panel is expressed as percentages (%) while the y-scale in the lower panel is expressed in percentage points (p.p.). I also would like the x-axis ticks from the lower panel to be aligned with the x-axis ticks from the upper panel so that the coefficient estimates appear aproximately in between the two bars from the upper panel (as shown in the mock figure).

    The mock figure is about labor market transitions (E=employed, U=unemployed, OLF=out of labor force) between one year and the following for group 1 and 2 (that is why there are two bars for each transition in the upper facet).

    Could you please advise on how to create a figure like this in Stata?

    Many thanks in advance!

    Best,
    Otavio

    Code:
    * Example generated by -dataex-. For more info, type help dataex
    clear
    input str6 transition float(group1 group2 coef ci_low ci_high)
    "E_OLF" .18 .16 .030 .020 .045
    "E_U" .20 .19 .015 .005 .025
    "E_E" .23 .22 .040 .025 .055
    end
    Click image for larger version

Name:	mock_figure.png
Views:	1
Size:	22.4 KB
ID:	1785602

    Last edited by Otavio Conceicao; 05 Apr 2026, 09:14.

  • #2
    Code:
    * Example generated by -dataex-. For more info, type help dataex
    clear
    input str6 transition float(group1 group2 coef ci_low ci_high)
    "E_OLF" .18 .16 .030 .020 .045
    "E_U" .20 .19 .015 .005 .025
    "E_E" .23 .22 .040 .025 .055
    end
    
    encode transition, gen(id)
    local rarr = ustrunescape("\u2192")
    label define mylabel 1 "E `rarr' OLF", modify
    label define mylabel 2 "E `rarr' U", add
    label define mylabel 3 "E `rarr' E", add
    label values id mylabel
    
    forvalues i = .0(.04).25 {  /*.16(.02).24 if excluding 0 */
        local j  = `=`i'*100'
        local l = `"`l'  `i' "`j'%""'
    }
    
     graph bar group1 group2 , over(transition, label(nolabel)) name(gr1,replace) /// 
        legend(off) ylabel(`l') ytitle(Share)    /*exclude0 yscale(r(.14 .24))*/
     scatter coef id, mcolor(blue) || rcap ci_high ci_low id , lcolor(blue)  /// 
        ||, name(gr2,replace) xlabel(1(1)3, valuelabel) legend(off) xscale(r(.5 3.5)) /// 
        xtitle("") ytitle(Percentage Points) ylabel(, format(%03.2f) )
    
     graph combine gr1 gr2, col(1)  title(Transition Rates, size(10pt))


    This is using the white_tableau scheme from Asjad Naqvi's -schemepack- on SSC
    Click image for larger version

Name:	Graph.png
Views:	1
Size:	43.9 KB
ID:	1785607

    Comment


    • #3
      Many thanks, Scott Merryman !

      Comment


      • #4
        Scott Merryman gave an excellent answer. I have some picky or pedantic comments in addition. The small issues involved range from graphical logic through avoiding a possible coding pitfall to just personal taste.

        Scott tactfully rewrote the bar graph code to start bars at zero. I agree with Scott's preference that the bars should not start at 0.15 just because that's the lowest value. (If there were a substantive reason to regard the lowest value as a benchmark, the story could be different, but I am guessing otherwise.)

        The data are not real but I am guessing that they are fairly realistic.

        The problem with the solution that bars should start at zero is then that those bars spend ink (metaphorical kind) underlining that the values are not zero.

        I am imagining that the major focus here is not onsuch emphasis that values are not zero, but simply on comparing rates with each other.

        This is precisely the rationale for switching to graph dot. In this case we surely want to make use of its undocumented vertical option. (And indeed to add a legend somewhere: I can't improve on group1 and group2 as text but Otavio will be able to do that.

        graph dot for what are ofren called (Cleveland) dot charts has been in Stata as an official command since 2003 (and there were community-contributed commands for similar plots before that). But they still often seem neglected.

        Sales pitches if you want to read them are included in

        Code:
        . search dot chart, sj
        
        Search of official help files, FAQs, Examples, and Stata Journals
        
        SJ-24-2 gr0095  . . . The joy of sets: Graphical alt. to Euler & Venn diagrams
                . . . . . . . . . . . . . . . . . . . . . . N. J. Cox and T. P. Morris
                (help upsetplot, vennbar, sortmean if installed)
                Q2/24   SJ 24(2):329--361
                introduces graphical alternatives for Euler or Venn diagrams
                mapped to bar or dot charts
        
        SJ-11-3 gr0049  . . . . . . . . . .  Stata tip 102: Highlighting specific bars
                . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  N. J. Cox
                Q3/11   SJ 11(3):474--477
                tip on highlighting a subset of observations in a bar or
                dot chart
        
        SJ-8-2  gr0034  . . . . . . . . . .  Speaking Stata: Between tables and graphs
                (help labmask, seqvar if installed) . . . . . . . . . . . .  N. J. Cox
                Q2/08   SJ 8(2):269--289
                outlines techniques for producing table-like graphs
        A quite different small issue is that loops over non-integers can go awry. I have therefore rewritten the loop as a loop over desired integers.

        Code:
        SJ-10-1 pr0051  . . . . . . . . . . . . Stata tip 85: Looping over nonintegers
                . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  N. J. Cox
                Q1/10   SJ 10(1):160--163                                (no commands)
                tip on using forvalues to loop over numbers
        gives the story.

        Some other choices in this code boil down to personal taste. If the values on the dot chart coincided you mught need to reach for markers such as Oh and +


        Code:
        * Example generated by -dataex-. For more info, type help dataex
        clear
        input str6 transition float(group1 group2 coef ci_low ci_high)
        "E_OLF" .18 .16 .030 .020 .045
        "E_U" .20 .19 .015 .005 .025
        "E_E" .23 .22 .040 .025 .055
        end
        
        encode transition, gen(id)
        local rarr = ustrunescape("\u2192")
        label define mylabel 1 "E `rarr' OLF", modify
        label define mylabel 2 "E `rarr' U", add
        label define mylabel 3 "E `rarr' E", add
        label values id mylabel
        
        forvalues i = 16(2)24 { 
            local j  = `i'/100 
            local l `l'  `j' "`i'%" 
        }
        
         graph dot (asis) group1 group2 , over(transition, label(nolabel)) name(gr1,replace) /// 
            marker(1, msize(large)) marker(2, msize(large)) lines(lc(gs12) lw(vthin)) /// 
            legend(row(1) pos(12)) vertical exclude0 ylabel(`l') ytitle(Share)  yscale(r(.14 .24)) 
            
         scatter coef id, mcolor(blue) msize(large) || rcap ci_high ci_low id , lcolor(blue)  /// 
            ||, name(gr2,replace) xlabel(1(1)3, valuelabel) legend(off) xscale(r(.5 3.5)) /// 
            xtitle("") ytitle(Percentage Points) ylabel(, format(%03.2f) )
        
         graph combine gr1 gr2, col(1)  title(Transition Rates, size(10pt))
        Click image for larger version

Name:	otavio.png
Views:	2
Size:	36.2 KB
ID:	1785619
        Attached Files

        Comment


        • #5
          Dear Nick Cox , many thanks! When I tried to generate a figure like yours, however, I didn't get it exactly right (see the figure below).

          Could you please help me generate a figure exactly like yours?

          Thanks!
          Click image for larger version

Name:	figure.png
Views:	1
Size:	45.8 KB
ID:	1785624

          Last edited by Otavio Conceicao; 06 Apr 2026, 12:37.

          Comment


          • #6
            It looks as if you are using Stata 17 or earlier and/or scheme s2color. See FAQ Advice 11 for the request to tell us if you are not using the latest version of Stata.

            I am using Stata 19.50’and
            scheme stcolor. Using scheme s1color in any recent version of Stata would get you closer to
            my graph.

            Comment


            • #7
              Otavio Conceicao if you are using -scheme s2color-, you can use -graphregion(color(white))- and related options to remove the blue background

              Comment


              • #8
                Using scheme s1color, you can try applying the following modifications to get close to stcolor. Ideas are taken from the following threads:

                1. https://www.statalist.org/forums/for...scheme-stcolor
                2. https://www.statalist.org/forums/for...order-on-graph

                Code:
                * Example generated by -dataex-. For more info, type help dataex
                clear
                input str6 transition float(group1 group2 coef ci_low ci_high)
                "E_OLF" .18 .16 .030 .020 .045
                "E_U" .20 .19 .015 .005 .025
                "E_E" .23 .22 .040 .025 .055
                end
                
                set scheme s1color
                
                encode transition, gen(id)
                local rarr = ustrunescape("\u2192")
                label define mylabel 1 "E `rarr' OLF", modify
                label define mylabel 2 "E `rarr' U", add
                label define mylabel 3 "E `rarr' E", add
                label values id mylabel
                
                forvalues i = 16(2)24 {
                    local j  = `i'/100
                    local l `l'  `j' "`i'%"
                }
                
                 graph dot (asis) group1 group2 , over(transition, label(nolabel)) name(gr1,replace) ///
                    marker(1, msize(large) mc("26 133 255")) marker(2, msize(large) mc("212 17 89")) ///
                    lines(lc(gs12) lw(vthin)) legend(row(1) pos(12) nobox region(lstyle(none))) ///
                    vertical exclude0 ylabel(`l', angle(horiz)) ytitle(Share)  yscale(r(.14 .24))
                    
                    gr_edit .plotregion1.style.editstyle boxstyle(linestyle(color(none))) editcopy
                    
                 scatter coef id, mcolor(blue) msize(large) || rcap ci_high ci_low id , lcolor(blue)  ///
                    ||, name(gr2,replace) xlabel(1(1)3, valuelabel) legend(off) xscale(r(.5 3.5)) ///
                    xtitle("") ytitle(Percentage Points) ylabel(, format(%03.2f) angle(horiz) )
                    
                    gr_edit .plotregion1.style.editstyle boxstyle(linestyle(color(none))) editcopy
                
                 graph combine gr1 gr2, col(1)  title(Transition Rates, size(10pt))
                Res.:

                Click image for larger version

Name:	Graph.png
Views:	1
Size:	23.5 KB
ID:	1785635

                Last edited by Andrew Musau; 07 Apr 2026, 08:32.

                Comment

                Working...
                X