Announcement

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

  • Graphs not aligned along x-axes when using -graph combine-

    Dear Statalist,

    I'm having an issue when using graph combine, where sub-graphs aren't aligned along their x-axes. Please see below:

    Code:
    sysuse lifeexp, clear
     
    gen loggnp = log10(gnppc)
    replace lexp = 10000 in 1 // to illustrate a different ylabel width for one subgraph
    label var loggnp "Log base 10 of GNP per capita"
     
    *****************
    * First attempt *
    *****************
    /*
    Problems:
    1) I don't want xtitles and xlabels repeated on each axis
                    (axes should be aligned along x with a single x-axis at bottom)
    2a) x-axes aren't being aligned correctly, since the y-axis
                    for Eur & C.Asia is wider (max value of 10000) than the other y-axes
                    (max values of only 2 digits [80 and 75])
    */
                  
    levelsof region, local(levs)
    foreach r of local levs {
                    scatter lexp loggnp if region == `r', ///
                                    name(g`r', replace) ylabel(, angle(0))
    }
     
    graph combine g1 g2 g3, cols(1) xcommon imargin(zero)

    example1.png

    Code:
    ******************
    * Second attempt *
    ******************
    /*
    Fixed Problem 1, but Problem 2 is still an issue
    Problem
    2b) not only is the difference in width of the widest y-label between sub-graphs
                    an issue, but the ytitle widths are also throwing off the x-alignment
    */
    levelsof region, local(levs)
    foreach r of local levs {
                    if `r' != 3 local xlabel none
                    else local xlabel
                    scatter lexp loggnp if region == `r', ///
                                    name(g`r', replace) ylabel(, angle(0)) ///
                                    xtitle("") xlabel(`xlabel') ///
                                    ytitle("`: label region `r''", orientation(horizontal))
    }
     
    graph combine g1 g2 g3, cols(1) xcommon imargin(zero) ///
                    b1title("Log base 10 of GNP per capita") ///
                    l1title("Life expectancy at birth")
    example2.png


    I would by able to get correct alignment by doing scatter lexp loggnp, by(region), but due to features not in this example, I need to plot each subgraph individually and later graph combine them.

    If anyone has any thoughts, they would be greatly appreciated.

    Thank you,

    Andrew Maurer
    Last edited by Andrew Maurer; 09 Oct 2014, 10:42.

  • #2
    I don't have any bright ideas about your specific question. But I don't understand how the vertical axis in the Europe and Central Asia graph can be so different in scale from the others when all of them are displaying life expectancy at birth. The problem seems to arise from the single point near the top of that panel where the life expectancy is said to be 10,000. Where is that place -- I want to move there! That point almost has to be an error. I think if you correct or remove that point, the rest of the data will settle out properly, all three graphs will have a common and reasonable vertical scale, and the other problems you are worrying about will solve themselves.

    By the way, you might consider a slightly different display of the data using Nick Cox's new -subsetplot- command (-ssc install subsetplot-). I think it would make it easier for people to appreciate the similarities and differences among the regions.
    Last edited by Clyde Schechter; 09 Oct 2014, 11:04.

    Comment


    • #3
      Seen this too, and often. I don't know a way round it except to use consistent instructions on the y axis labels and titles, or (as you say) to redesign the graph, so that the different panels come out of a by() option. The latter can need a different data structure, as you don't say.

      Comment


      • #4
        Originally posted by Clyde Schechter View Post
        The problem seems to arise from the single point near the top of that panel where the life expectancy is said to be 10,000.
        Thanks for the response, and my apologies for not clarifying. I just threw that data point in to illustrate my problem (replace lexp = 10000 in 1 // to illustrate a different ylabel width for one subgraph).

        Comment


        • #5
          Oh, I see. Well, I still wonder if my point doesn't apply in a somewhat different way.

          If you are making three scatter plots of the same variables in three regions, the y-axis scales typically wouldn't vary that much. If they really do, then just having three nicely aligned plots stacked on top of each other might actually lull the reader into not noticing that one region's results are a different order of magnitude than the others'. And if that is really true, then perhaps all three graphs should be plotted with a log scale on the y-axis? If that isn't possible due to negative or zero values (in the real data) or other reasons, then probably you have to force the same ylabel() values by specifying those values explicitly in the scatter command.

          If the real problem involves plots of different variables, rather than three plots of the same variables, then you might consider a change of units to make the values of the y-variables more comparable, and then force a common set of ylabels.

          Comment


          • #6
            Partial solutions:

            1. Drop the ylabel(, angle(0)) option, then - in an S1 or S2 scheme - the y-axis labels will use the same space regardless of the number of digits.

            2. Include an ycommon option in the graph combine command.

            Comment


            • #7
              Originally posted by Nick Cox View Post
              Seen this too, and often. I don't know a way round it except to use consistent instructions on the y axis labels and titles, or (as you say) to redesign the graph, so that the different panels come out of a by() option. The latter can need a different data structure, as you don't say.
              That's too bad. I was hoping this wasn't the case. I would think it should be possible, by trying to emulate whatever the by() option does for alignment, but I haven't figured it out by looking at the ado files for twoway, graph, and dependencies. Sergiy Radyakin has a presentation Advanced Graphics Programming in Stata, that seems promising, but it's pretty low level.

              Originally posted by Svend Juul View Post
              Partial solutions:

              1. Drop the ylabel(, angle(0)) option, then - in an S1 or S2 scheme - the y-axis labels will use the same space regardless of the number of digits.

              2. Include an ycommon option in the graph combine command.
              Thanks. Although this would fix the alignment issue, it wouldn't be presented in the way I'm looking to present the graph.

              I worked out a solution that works in this case, using graph... by, rather than graph combine, but I imagine cases where this wouldn't work. It's a bit "hacked" with the gr_edit command. Does anyone know if I there's an option in twoway equivalent to the later command gr_edit .plotregion1.subtitle[`i'].as_textbox.setstyle, style(no)?

              attempt3.png
              Edit: the l1title should have been "`: variable label lexp'", and not "`: variable label region'". My mistake.

              Code:
              *****************
              * Third attempt *
              *****************
               
              sysuse lifeexp, clear
              gen loggnp = log10(gnppc)
              replace lexp = 10000 in 1 // to illustrate a different ylabel width for one subgraph
              label var loggnp "Log base 10 of GNP per capita"
               
              // Trick to force all y-axes through 0, but rescale vertically
              //            (add twoway functiony = 0, range(. .)
              sum loggnp, meanonly
              local xmin = r(min)
               
              twoway scatter lexp loggnp ///
                              , subtitle(, position(9) bcolor(none) alignment(middle) justification(right) placement(east) size(small)) ///
                                              ylabel(, labsize(*.6) angle(0)) plotregion(margin(zero)) ///
                              || function y = 0, range(`xmin' `xmin') lstyle(none) ///
                              || , by(region, cols(1) yrescale b1title("`: variable label loggnp'") ///
                                              l1title("`: variable label region'") imargin(0 0 2 2) legend(off))
               
              // At this point, the sub-graph titles are overlapping
              //            Trick to correct formatting is to go into graph editor, choose the widest title
              //            (in this case "Eur & C.Asia"), edit the subtitle ("subtitle1"), choose format,
              //            and uncheck "Expand area to fill cell". This is equivalent to the gr_edit command
              //            below. In general, we don't know which value of the byvar will have the
              //            longest title, so I do the equivalent of unchecking "Expand area to fill cell"
              //            for all of them.
              qui distinct region
              forval i = 1/`=r(ndistinct)' {
                              gr_edit .plotregion1.subtitle[`i'].as_textbox.setstyle, style(no)
              }
              Last edited by Andrew Maurer; 10 Oct 2014, 08:44.

              Comment


              • #8
                This is just to note that other solutions may obviate the need for any kind of work-around. Clyde kindly mentioned subsetplot (SSC); another is sparkline (SSC).

                Comment


                • #9
                  I am facing a similar problem as #2 with dot plots. Has anyone found an excellent solution? Thanks!

                  Comment


                  • #10
                    The only solution I know of is to reshape your data such that you can use the by() option instead of using graph combine. For these types of problems I have been avoiding graph combine whenever I can, and with some creativity that is almost always.
                    ---------------------------------
                    Maarten L. Buis
                    University of Konstanz
                    Department of history and sociology
                    box 40
                    78457 Konstanz
                    Germany
                    http://www.maartenbuis.nl
                    ---------------------------------

                    Comment


                    • #11
                      Sorry to bump an old post, but maybe it helps people with the same issue.

                      I found a few options that can be used to make sure the axes are aligned:

                      Code:
                      ylabel(, labelminlen() labgap() )
                      Code:
                      ytitle(, justif(left) width(15))

                      Note that probably only labelminlen in ylabel() and width in ytitle() seems to suffice to fix the issue, but the other options might be helpful in other scenarios or just for aesthetic reasons.

                      Using the same example as above, we have these results:

                      Code:
                      ******************
                      * Second attempt *
                      ******************
                      levelsof region, local(levs)
                      foreach r of local levs {
                                      if `r' != 3 local xlabel none
                                      else local xlabel
                                      scatter lexp loggnp if region == `r', ///
                                                      name(g`r', replace) ylabel(, angle(0) labelminlen(10) labgap(1)) ///
                                                      xtitle("") xlabel(`xlabel') ///
                                                      ytitle("`: label region `r''", orientation(horizontal) justif(left) width(15))
                      }
                       
                      graph combine g1 g2 g3, cols(1) xcommon imargin(zero) ///
                                      b1title("Log base 10 of GNP per capita") ///
                                      l1title("Life expectancy at birth")

                      Click image for larger version

Name:	gr.jpg
Views:	1
Size:	31.5 KB
ID:	1736361


                      Comment


                      • #12
                        You really don't need to apologise for bumping any old thread so long as what you say is relevant, as it strongly is.

                        In turn, I want to mention that a solution mentioned in #3 and #10 is now (2020) written up at https://journals.sagepub.com/doi/pdf...36867X20976341

                        Comment


                        • #13
                          Just wanted to add a thank you to Marc Rain for the solution in #11, and add that I was able to use the same idea to successfully fix alignment issues in a call to graph dot, within the over groups, like so: over( ... , label( labelminlen(21))). This is nice, because I had not expected it to work outside the twoway suite of commands.
                          Last edited by Hemanshu Kumar; 06 Sep 2024, 01:25.

                          Comment

                          Working...
                          X