Announcement

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

  • multiline now available on SSC

    Thanks to the ever-energetic Kit Baum, a new command multiline is now available on SSC for Stata 11+. (It may even be portable to Stata 8+, but I have not tried.)

    Install with

    Code:
    ssc install multiline
    The main application of multiline is to plot time series (especially but not only those measured in different units or on very different scales) in separate panels.

    This can be a good alternative to the usual superimposed or spaghetti plot if the latter appears unclear or absurd.

    The help file says more, but some examples give the flavour. On second thoughts, I would remove the redundant x-axis title "year" and explain "Price" as "(USD)". There's always something to tweak.

    Code:
    webuse grunfeld, clear
    multiline invest mvalue kstock year if company == 1, recast(connected)
    graph export ml1.png
    
    multiline invest mvalue kstock year if company == 1, recast(connected) ///
    mylabels(`" `" "Gross" "investment" "' `" "Market" "value" "' `"  "Plant and" "equipment value" "' "')
    graph export ml2.png
    Click image for larger version

Name:	ml1.png
Views:	1
Size:	29.0 KB
ID:	1401777

    Click image for larger version

Name:	ml2.png
Views:	1
Size:	30.0 KB
ID:	1401778





    Code:
    sysuse auto, clear
    multiline mpg weight length displacement price, recast(scatter) c(none) by(col(2)) ms(Oh) subtitle(, orient(vertical))
    graph export ml3.png
    Click image for larger version

Name:	ml3.png
Views:	1
Size:	55.3 KB
ID:	1401779

    Last edited by Nick Cox; 13 Jul 2017, 04:44.

  • #2
    Excellent achievement! Indeed, the last graph was something I was looking for.
    Best regards,

    Marcos

    Comment


    • #3
      Updated, thanks to Kit again. The update concerns treatment of missing values.

      Comment


      • #4
        Dear Nick Cox, I want the different colours of each line graph and also draw a borderline just like in the above-displayed graph.
        Click image for larger version

Name:	ml1.png
Views:	1
Size:	35.0 KB
ID:	1487947
        above-displayed graphs.

        Comment


        • #5
          In #1 I used scheme s1color:

          Code:
          set scheme s1color
          I should have made that explicit. Sorry about that.

          Different colours aren't possible at present. I will think about that.

          Comment


          • #6
            Another big step. Thanks for the contribution, and still waiting for lolipop chart in Stata. Is multiline applicable to barchart?

            Comment


            • #7
              Sonnen Blume Thanks. See https://www.statalist.org/forums/for...ailable-on-ssc for bar chart in similar style.

              Lollipop charts, if I understand you correctly, may well be supported elsewhere but in any case could be a combined twoway plots.

              This is a just an example. The design choices are just some of many others possible.

              Code:
              sysuse auto, clear
              set scheme s1color 
              keep if foreign
              sort mpg
              gen order = _n
              
              * -labmask- to be installed from Stata Journal- 
              labmask order, values(make)
              
              su mpg 
              local min `r(min)' 
              local max `r(max)' 
              local mean : di %2.1f r(mean) 
              
              twoway spike mpg order, base(0) pstyle(p1) yla(1/22, valuelabel ang(h) noticks) horizontal ///
              || scatter order mpg, pstyle(p1) ytitle("") legend(off)                                    ///
              xaxis(1 2) xla(`min' `mean' `max', grid axis(1)) xtitle("", axis(2)) xtitle(, place(w) axis(1) size(medium))
              Click image for larger version

Name:	lollipop.png
Views:	1
Size:	45.8 KB
ID:	1488012

              Last edited by Nick Cox; 14 Mar 2019, 01:53.

              Comment


              • #8
                #4 #5 Zeeshan Fareed I have tweaked the program to allow this. With the code below, which will appear on SSC at a later date, you can do this:


                Code:
                webuse grunfeld, clear
                set scheme s1color 
                multiline invest mvalue kstock year if company == 1 , xtitle("") 
                multiline invest mvalue kstock year if company == 1 , xtitle("") recast(connected)
                multiline invest mvalue kstock year if company == 1 , xtitle("") recast(connected) separate
                multiline invest mvalue kstock year if company == 1 , xtitle("") recast(connected) separate by(legend(off))
                multiline invest mvalue kstock year if company == 1 , xtitle("") recast(connected) separate by(legend(off)) ms(O D T)
                multiline invest mvalue kstock year if company == 1 , xtitle("") recast(connected) separate by(legend(off)) ms(O D T) lc(black orange blue)
                multiline invest mvalue kstock year if company == 1 , xtitle("") recast(connected) separate by(legend(off)) ms(O D T) lc(black orange blue) mc(black orange blue)
                To get separate styles, the minimal option is just separate. But then a legend will spring into being, which you should usually suppress. And the marker and line and other options might not be to your liking, so you can change those as usual.

                Here is the result of the last command above.



                Click image for larger version

Name:	multiline4.png
Views:	1
Size:	30.9 KB
ID:	1488096


                Here is the code, which should be multiline.ado and replace the existing file, wherever it is on your machine or system.

                Code:
                *! 1.4.0 NJC 14mar2019 
                * 1.3.1 NJC 14jul2017 
                * 1.3.0 NJC 3jul2017 
                * 1.2.0 NJC 8mar2017 
                * 1.1.0 NJC 2oct2016 
                * 1.0.0 NJC 5sept2016 
                program multiline 
                    version 11
                    syntax varlist(numeric) [if] [in] ///
                    [, by(str asis) mylabels(str asis) MISSing SEParate *] 
                    
                    quietly { 
                        if "`missing'" != "" marksample touse, novarlist 
                        else marksample touse 
                
                        count if `touse' 
                        if r(N) == 0 exit 2000 
                
                        preserve 
                        keep if `touse' 
                        drop `touse' 
                    
                        gettoken yvar rest : varlist 
                        local J = 0 
                        while "`yvar'" != "" { 
                            local ++J 
                            local lbl`J' : var label `yvar' 
                            if `"`lbl`J''"' == "" local lbl`J' "`yvar'" 
                            local last "`yvar'" 
                            gettoken yvar rest : rest    
                        } 
                
                        local xvar "`last'" 
                        local yvar : list varlist - xvar 
                
                        capture tsset 
                        if "`r(panelvar)'" != "" local panelvar "`r(panelvar)'" 
                
                        foreach v of local yvar { 
                            local call `call' `v' `panelvar' `xvar' 
                        }
                
                        tempname y
                        tempfile mydo 
                        local label : value label `xvar'  
                        label save `label' using "`mydo'" 
                        stack `call', into(`y' `panelvar' `xvar') clear 
                        do "`mydo'" 
                        if "`label'" != "" { 
                            label val `xvar' `label' 
                        } 
                
                        local Jm1 = `J' - 1 
                        if `"`mylabels'"' != "" { 
                            tokenize `mylabels' 
                            forval j = 1/`Jm1' { 
                                label def _stack `j' `"``j''"', add 
                            } 
                        } 
                        else forval j = 1/`Jm1' { 
                            label def _stack `j' `"`lbl`j''"', add 
                        } 
                        label val _stack _stack 
                    } 
                
                    sort `panelvar' `xvar' 
                    label var `xvar'  `"`lbl`J''"'
                
                    if "`by'" == "" local by "cols(1)"  
                    else { 
                        local found 0 
                        foreach opt in c co col cols { 
                            if strpos("`by'", "`opt'(") { 
                                local found 1 
                                continue, break 
                            } 
                        }
                        if !`found' local by "`by' cols(1)" 
                    }  
                
                    quietly    if "`separate'" != "" { 
                        separate `y', by(_stack) veryshortlabel 
                        local y `r(varlist)' 
                    } 
                        
                    line `y' `xvar', by(_stack, yrescale note("") `by') ///
                    ytitle("") yla(, ang(h)) c(L) ///
                    subtitle(, pos(9) bcolor(none) nobexpand place(e)) `options' 
                end



                Comment


                • #9
                  Originally posted by Nick Cox View Post
                  Sonnen Blume Thanks. See https://www.statalist.org/forums/for...ailable-on-ssc for bar chart in similar style.

                  Lollipop charts, if I understand you correctly, may well be supported elsewhere but in any case could be a combined twoway plots.

                  This is a just an example. The design choices are just some of many others possible.

                  Code:
                  sysuse auto, clear
                  set scheme s1color
                  keep if foreign
                  sort mpg
                  gen order = _n
                  
                  * -labmask- to be installed from Stata Journal-
                  labmask order, values(make)
                  
                  su mpg
                  local min `r(min)'
                  local max `r(max)'
                  local mean : di %2.1f r(mean)
                  
                  twoway spike mpg order, base(0) pstyle(p1) yla(1/22, valuelabel ang(h) noticks) horizontal ///
                  || scatter order mpg, pstyle(p1) ytitle("") legend(off) ///
                  xaxis(1 2) xla(`min' `mean' `max', grid axis(1)) xtitle("", axis(2)) xtitle(, place(w) axis(1) size(medium))
                  [ATTACH=CONFIG]n1488012[/ATTACH]
                  Thanks a lot professor for the graph. I never realised it exists there in the guise of "spike". Unfortunately, the code you mentioned is not taking 'by' or 'over' option. I have a relevant question to spikeplot which I'll post as a new thread by this citing this one.

                  Comment


                  • #10
                    Hi Nick,

                    I have a few questions regarding the multiline command. Let me use your codes as an example:

                    Code:
                    webuse grunfeld, clear
                    set scheme s1color
                    multiline invest mvalue kstock year if company == 1 , xtitle("") title("Figure 2 Hahaha", size(*.8) color(gs0) position(6))
                    1. How can I get ride of the two titles "Figure 2 Hahaha" for the top two sub-figures and keep the one at the bottom? Typically this is done by tucking title into by, i.e. by(title("Figure 2 Hahaha")), but when I try this, Stata tells me "type mismatch". If I switch to by(title(`""Figure 2 Hahaha""')), then there will be double quotes around the title which isn't what I want.
                    2. How can I customize the scale of the y axis of each of the three sub-figures separately?




                    For the first question, I am aware that i can first label the variable "year", and then add "xtitle" into my command as below. However, my title happens to be very long and it seems that Stata cannot show the full length of the title even if there is some space left. Another reason why this solution is not perfect is that I may want to add an additional note or caption below the main title. But this cannot be done because adding note("xxxx") into the command always add the same note to all the subfigures instead of adding one below the whole figure.
                    Code:
                    webuse grunfeld, clear
                    set scheme s1color
                    lab var year "You know, as of this moment, Darren, we have $7 trillion-plus in the Middle East for"
                    multiline invest mvalue kstock year if company == 1 , xtitle(,)
                    You can see that even if there is some space at the right end, the word "for" is not shown. I know this is a strange question but in my case, I happen to just need to fill in one more word into my figure title. And I do not want to reduce the font size either.
                    Thank you in advance!
                    Click image for larger version

Name:	multiline2.png
Views:	1
Size:	81.9 KB
ID:	1544811

                    Last edited by shem shen; 04 Apr 2020, 20:14.

                    Comment


                    • #11
                      Working backwards: the problem with your variable label approach is that your variable label is too long, so that is why one word is omitted. The problem is nothing to do with the graphics. Check describe to see that is so.

                      Your examples highlighted a bug in multiline -- so thanks for that, although solutions for your title problems don't involve the bug fix.


                      Code:
                      webuse grunfeld, clear
                      set scheme s1color
                      multiline invest mvalue kstock year if company == 1 , xtitle("Figure 2 Hahaha", size(*.8) color(gs0)) name(G1, replace)
                      
                      webuse grunfeld, clear
                      set scheme s1color
                      local xtitle "You know, as of this moment, Darren, we have $7 trillion-plus in the Middle East for"
                      multiline invest mvalue kstock year if company == 1 , xtitle("`xtitle'") name(G2, replace)

                      As for

                      How can I customize the scale of the y axis of each of the three sub-figures separately?


                      the answer is that you can't. The point of
                      multiline is to change data structure temporarily and then draw a graph using by(, yrescale). There isn't further flexibility that I can tell you about. You need graph combine on customised graphs, which multiline is designed precisely to avoid.


                      Code:
                      *! 1.4.1 NJC 5apr2020 
                      *! 1.4.0 NJC 14mar2019 
                      * 1.3.1 NJC 14jul2017 
                      * 1.3.0 NJC 3jul2017 
                      * 1.2.0 NJC 8mar2017 
                      * 1.1.0 NJC 2oct2016 
                      * 1.0.0 NJC 5sept2016 
                      program multiline 
                          version 11
                          syntax varlist(numeric) [if] [in] ///
                          [, by(str asis) mylabels(str asis) MISSing SEParate *] 
                          
                          quietly { 
                              if "`missing'" != "" marksample touse, novarlist 
                              else marksample touse 
                      
                              count if `touse' 
                              if r(N) == 0 exit 2000 
                      
                              preserve 
                              keep if `touse' 
                              drop `touse' 
                          
                              gettoken yvar rest : varlist 
                              local J = 0 
                              while "`yvar'" != "" { 
                                  local ++J 
                                  local lbl`J' : var label `yvar' 
                                  if `"`lbl`J''"' == "" local lbl`J' "`yvar'" 
                                  local last "`yvar'" 
                                  gettoken yvar rest : rest    
                              } 
                      
                              local xvar "`last'" 
                              local yvar : list varlist - xvar 
                      
                              capture tsset 
                              if "`r(panelvar)'" != "" local panelvar "`r(panelvar)'" 
                      
                              foreach v of local yvar { 
                                  local call `call' `v' `panelvar' `xvar' 
                              }
                      
                              tempname y
                              tempfile mydo 
                              local label : value label `xvar'  
                              label save `label' using "`mydo'" 
                              stack `call', into(`y' `panelvar' `xvar') clear 
                              do "`mydo'" 
                              if "`label'" != "" { 
                                  label val `xvar' `label' 
                              } 
                      
                              local Jm1 = `J' - 1 
                              if `"`mylabels'"' != "" { 
                                  tokenize `mylabels' 
                                  forval j = 1/`Jm1' { 
                                      label def _stack `j' `"``j''"', add 
                                  } 
                              } 
                              else forval j = 1/`Jm1' { 
                                  label def _stack `j' `"`lbl`j''"', add 
                              } 
                              label val _stack _stack 
                          } 
                      
                          sort `panelvar' `xvar' 
                          label var `xvar'  `"`lbl`J''"'
                      
                          if `"`by'"' == "" local by "cols(1)"  
                          else { 
                              local found 0 
                              foreach opt in c co col cols { 
                                  if strpos(`"`by'"', "`opt'(") { 
                                      local found 1 
                                      continue, break 
                                  } 
                              }
                              if !`found' local by `"`by' cols(1)"' 
                          }  
                      
                          quietly    if "`separate'" != "" { 
                              separate `y', by(_stack) veryshortlabel 
                              local y `r(varlist)' 
                          } 
                              
                          line `y' `xvar', by(_stack, yrescale note("") `by') ///
                          ytitle("") yla(, ang(h)) c(L) ///
                          subtitle(, pos(9) bcolor(none) nobexpand place(e)) `options' 
                      end
                      Code:
                      
                      

                      Comment


                      • #12
                        Originally posted by Nick Cox View Post
                        Working backwards: the problem with your variable label approach is that your variable label is too long, so that is why one word is omitted. The problem is nothing to do with the graphics. Check describe to see that is so.

                        Your examples highlighted a bug in multiline -- so thanks for that, although solutions for your title problems don't involve the bug fix.


                        Code:
                        webuse grunfeld, clear
                        set scheme s1color
                        multiline invest mvalue kstock year if company == 1 , xtitle("Figure 2 Hahaha", size(*.8) color(gs0)) name(G1, replace)
                        
                        webuse grunfeld, clear
                        set scheme s1color
                        local xtitle "You know, as of this moment, Darren, we have $7 trillion-plus in the Middle East for"
                        multiline invest mvalue kstock year if company == 1 , xtitle("`xtitle'") name(G2, replace)

                        As for



                        the answer is that you can't. The point of
                        multiline is to change data structure temporarily and then draw a graph using by(, yrescale). There isn't further flexibility that I can tell you about. You need graph combine on customised graphs, which multiline is designed precisely to avoid.


                        Code:
                        *! 1.4.1 NJC 5apr2020
                        *! 1.4.0 NJC 14mar2019
                        * 1.3.1 NJC 14jul2017
                        * 1.3.0 NJC 3jul2017
                        * 1.2.0 NJC 8mar2017
                        * 1.1.0 NJC 2oct2016
                        * 1.0.0 NJC 5sept2016
                        program multiline
                        version 11
                        syntax varlist(numeric) [if] [in] ///
                        [, by(str asis) mylabels(str asis) MISSing SEParate *]
                        
                        quietly {
                        if "`missing'" != "" marksample touse, novarlist
                        else marksample touse
                        
                        count if `touse'
                        if r(N) == 0 exit 2000
                        
                        preserve
                        keep if `touse'
                        drop `touse'
                        
                        gettoken yvar rest : varlist
                        local J = 0
                        while "`yvar'" != "" {
                        local ++J
                        local lbl`J' : var label `yvar'
                        if `"`lbl`J''"' == "" local lbl`J' "`yvar'"
                        local last "`yvar'"
                        gettoken yvar rest : rest
                        }
                        
                        local xvar "`last'"
                        local yvar : list varlist - xvar
                        
                        capture tsset
                        if "`r(panelvar)'" != "" local panelvar "`r(panelvar)'"
                        
                        foreach v of local yvar {
                        local call `call' `v' `panelvar' `xvar'
                        }
                        
                        tempname y
                        tempfile mydo
                        local label : value label `xvar'
                        label save `label' using "`mydo'"
                        stack `call', into(`y' `panelvar' `xvar') clear
                        do "`mydo'"
                        if "`label'" != "" {
                        label val `xvar' `label'
                        }
                        
                        local Jm1 = `J' - 1
                        if `"`mylabels'"' != "" {
                        tokenize `mylabels'
                        forval j = 1/`Jm1' {
                        label def _stack `j' `"``j''"', add
                        }
                        }
                        else forval j = 1/`Jm1' {
                        label def _stack `j' `"`lbl`j''"', add
                        }
                        label val _stack _stack
                        }
                        
                        sort `panelvar' `xvar'
                        label var `xvar' `"`lbl`J''"'
                        
                        if `"`by'"' == "" local by "cols(1)"
                        else {
                        local found 0
                        foreach opt in c co col cols {
                        if strpos(`"`by'"', "`opt'(") {
                        local found 1
                        continue, break
                        }
                        }
                        if !`found' local by `"`by' cols(1)"'
                        }
                        
                        quietly if "`separate'" != "" {
                        separate `y', by(_stack) veryshortlabel
                        local y `r(varlist)'
                        }
                        
                        line `y' `xvar', by(_stack, yrescale note("") `by') ///
                        ytitle("") yla(, ang(h)) c(L) ///
                        subtitle(, pos(9) bcolor(none) nobexpand place(e)) `options'
                        end
                        Code:
                        
                        
                        Thanks Nick for your quick reply! This is really helpful!!!

                        Comment


                        • #13
                          Thanks to Kit Baum the ado file and the help are now up-to-date on SSC as of 1.4.1.

                          Comment


                          • #14
                            Thanks as always to Kit Baum, new versions of the code and help file are up at SSC.

                            Comment


                            • #15
                              Dear Nick,

                              After ssc installation, I did a version check:
                              Code:
                              which multiline
                              *! 1.6.0 NJC 27mar2022
                              Is this correct?
                              http://publicationslist.org/eric.melse

                              Comment

                              Working...
                              X