Announcement

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

  • Stacked bar charts centered around zero

    Hello,

    I've created the following dataset for the purpose of this question:

    Code:
    * Example generated by -dataex-. For more info, type help dataex
    clear
    input float(female treatment apple orange banana carrot tomato)
    0 0 1 3 2 1 1
    0 0 3 3 3 1 2
    0 0 2 2 2 2 1
    0 0 3 4 2 2 2
    0 0 2 1 4 2 1
    0 0 4 2 1 2 2
    0 0 3 3 2 1 2
    0 1 2 4 3 2 2
    0 1 3 2 2 1 1
    0 1 2 3 4 1 2
    0 1 4 4 2 2 1
    1 0 1 1 3 1 1
    1 0 2 2 2 1 1
    1 0 3 3 3 2 2
    1 0 4 2 2 2 1
    1 0 2 3 3 2 2
    1 1 3 2 1 1 1
    1 1 2 4 2 2 2
    1 1 4 2 3 1 1
    1 1 2 1 2 2 2
    end
    label values apple scale14
    label values orange scale14
    label values banana scale14
    label def scale14 1 "Strongly disagree", modify
    label def scale14 2 "Disagree", modify
    label def scale14 3 "Agree", modify
    label def scale14 4 "Strongly agree", modify
    label values carrot scale12
    label values tomato scale12
    label def scale12 1 "no", modify
    label def scale12 2 "yes", modify
    I've created a horizontal bar chart (stacked) for the variable apple with the following code:
    Code:
    graph hbar, ///
    over(apple) ///
    over(treatment) over(female)  ///
    stack asyvars percentage ///
    graphregion(margin(vsmall) color(gs15)) ///
    bar(1, fintensity(100) fcolor(blue%75) lcolor(blue%75) lwidth(thin)) ///
    bar(2, fintensity(100) fcolor(blue%55) lcolor(blue%55) lwidth(thin)) ///
    bar(3, fintensity(110) fcolor(blue%25) lcolor(blue%25) lwidth(thin)) ///
    bar(4, fintensity(100) fcolor(green%55) lcolor(green%55) lwidth(thin)) ///
    bar(5, fintensity(110) fcolor(green%75) lcolor(green%75) lwidth(thin)) ///
    title("I like apples", fcolor(gs15) pos(12) bexpand size(10.5pt)) ///
    subtitle(" ") legend(label(1 "Strongly disagree") label(2 "Disagree") label(3 "Agree") label(4 "Strongly agree"))
    My first question is:
    1. Is it possible to center responses around 0, i.e. "Strongly disagree" and "Disagree" on the left hand side of the axis and "Agree" and "Strongly agree" on the right-hand side of the axis? That means the scale would be [-100;100] instead of [1;100].

    Secondly, the ultimate goal is to create two subgraphs (one for female==0, the other for female==1). Each graph should depict the distribution of responses for the five variables apple, orange, banana, carrot and tomato (below each other), separate by treatment group for each variable.
    So two further questions would be:
    2. For the two subgraphs, is there a way without using graph combine? To my understanding, twoway does not work here.
    3. I'm unable to include all five variables into one graph, particulary due to the fact that the response scales are not the exact same for all variables.

    Thank you!!
    Last edited by Julia Mueller; 04 Sep 2024, 11:14.

  • #2
    I believe that the idea you have in mind is implemented by floatplot from SSC. However, I see two potential issues:

    1. For your categories "Strongly Disagree - Strongly Agree," you have four options, but there is no neutral/indifferent category, which would have served as a natural midpoint. As it stands, there is no true midpoint in your scale.

    2. I don't quite understand how you intend to combine a 4-level categorical variable with a binary response variable. Could you please elaborate on how such a graph would look?

    For combining variables measured on the same scale, you can reshape your data.

    Code:
    clear
    input float(female treatment apple orange banana carrot tomato)
    0 0 1 3 2 1 1
    0 0 3 3 3 1 2
    0 0 2 2 2 2 1
    0 0 3 4 2 2 2
    0 0 2 1 4 2 1
    0 0 4 2 1 2 2
    0 0 3 3 2 1 2
    0 1 2 4 3 2 2
    0 1 3 2 2 1 1
    0 1 2 3 4 1 2
    0 1 4 4 2 2 1
    1 0 1 1 3 1 1
    1 0 2 2 2 1 1
    1 0 3 3 3 2 2
    1 0 4 2 2 2 1
    1 0 2 3 3 2 2
    1 1 3 2 1 1 1
    1 1 2 4 2 2 2
    1 1 4 2 3 1 1
    1 1 2 1 2 2 2
    end
    label values apple scale14
    label values orange scale14
    label values banana scale14
    label def scale14 1 "Strongly disagree", modify
    label def scale14 2 "Disagree", modify
    label def scale14 3 "Agree", modify
    label def scale14 4 "Strongly agree", modify
    label values carrot scale12
    label values tomato scale12
    label def scale12 1 "no", modify
    label def scale12 2 "yes", modify
    
    lab values female female
    lab def female 0 "Male" 1 "Female"
    rename (apple-banana) var=
    gen id=_n
    reshape long var, i(id) j(Fruit) string
    *ssc install floatplot, replace
    
    *CENTERING ON AGREE
    floatplot var, over(Fruit) by(female, leg(pos(6)) note("")) center(3) xtitle("") fcolors(red red*0.5 blue*0.5 blue) leg(row(1))
    Click image for larger version

Name:	Graph.png
Views:	1
Size:	15.1 KB
ID:	1763065

    Last edited by Andrew Musau; 04 Sep 2024, 14:06.

    Comment


    • #3
      Hi Andrew, this is incredibly helpful, thank you so much!

      1. For your categories "Strongly Disagree - Strongly Agree," you have four options, but there is no neutral/indifferent category, which would have served as a natural midpoint. As it stands, there is no true midpoint in your scale.
      I had hoped there was a way to center right between two categories, like on the right-hand side in the attached picture (source https://www.datarevelations.com/rethinkingdivergent/).
      Attached Files

      Comment


      • #4
        Regarding
        2. I don't quite understand how you intend to combine a 4-level categorical variable with a binary response variable. Could you please elaborate on how such a graph would look?
        Absolutely! The idea was to include the legend similar to the one in the attached picture at the top (source: https://www.pewresearch.org/short-re...bal-ssm_1-png/). I would then like the three fruits at the top with their own legend and the vegetables below, again, including their own legend.

        For this it probably just makes sense to either create entirely separate graphs or simply graph combine.
        Attached Files

        Comment


        • #5
          Reviewing the documentation, I see that there is an option for that.

          Code:
          clear
          input float(female treatment apple orange banana carrot tomato)
          0 0 1 3 2 1 1
          0 0 3 3 3 1 2
          0 0 2 2 2 2 1
          0 0 3 4 2 2 2
          0 0 2 1 4 2 1
          0 0 4 2 1 2 2
          0 0 3 3 2 1 2
          0 1 2 4 3 2 2
          0 1 3 2 2 1 1
          0 1 2 3 4 1 2
          0 1 4 4 2 2 1
          1 0 1 1 3 1 1
          1 0 2 2 2 1 1
          1 0 3 3 3 2 2
          1 0 4 2 2 2 1
          1 0 2 3 3 2 2
          1 1 3 2 1 1 1
          1 1 2 4 2 2 2
          1 1 4 2 3 1 1
          1 1 2 1 2 2 2
          end
          label values apple scale14
          label values orange scale14
          label values banana scale14
          label def scale14 1 "Strongly disagree", modify
          label def scale14 2 "Disagree", modify
          label def scale14 3 "Agree", modify
          label def scale14 4 "Strongly agree", modify
          label values carrot scale12
          label values tomato scale12
          label def scale12 1 "no", modify
          label def scale12 2 "yes", modify
          
          lab values female female
          lab def female 0 "Male" 1 "Female"
          
          preserve
          rename (apple-banana) var=
          gen id=_n
          reshape long var, i(id) j(Fruit) string
          *ssc install floatplot, replace
          
          floatplot var, over(Fruit) by(female, leg(pos(6)) note("")) highnegative(2) xtitle("") fcolors(red red*0.5 blue*0.5 blue) leg(row(1))
          
          restore, preserve
          
          rename (carrot-tomato) var=
          gen id=_n
          reshape long var, i(id) j(Vegetable) string
          
          floatplot var, over(Vegetable) by(female, leg(pos(6)) note("")) highnegative(1) xtitle("") fcolors(red blue) leg(row(1)) saving(gr2, replace)
          restore
          
          gr combine gr1.gph gr2.gph, col(1)
          You can resize the graphs.
          Click image for larger version

Name:	Graph.png
Views:	1
Size:	16.1 KB
ID:	1763075

          Last edited by Andrew Musau; 04 Sep 2024, 15:01.

          Comment


          • #6
            This is incredible, thank you!

            Two more things, if it's okay:
            1. Is there a way to differentiate between treatments for each category? So e.g. apple would have two bars, one for treatment==0 and the other for treatment==1?
            2. Is it possible to change the order of the categories to 1 apple 2 orange 3 banana?

            Thank you!

            Comment


            • #7
              I agree with Andrew Musau in recommending floatplot.

              I got this far with some similar ideas. By a mix of accident and design I already pre-empted the questions in #6 to some extent.

              Code:
              * Example generated by -dataex-. For more info, type help dataex
              clear
              input float(female treatment apple orange banana carrot tomato)
              0 0 1 3 2 1 1
              0 0 3 3 3 1 2
              0 0 2 2 2 2 1
              0 0 3 4 2 2 2
              0 0 2 1 4 2 1
              0 0 4 2 1 2 2
              0 0 3 3 2 1 2
              0 1 2 4 3 2 2
              0 1 3 2 2 1 1
              0 1 2 3 4 1 2
              0 1 4 4 2 2 1
              1 0 1 1 3 1 1
              1 0 2 2 2 1 1
              1 0 3 3 3 2 2
              1 0 4 2 2 2 1
              1 0 2 3 3 2 2
              1 1 3 2 1 1 1
              1 1 2 4 2 2 2
              1 1 4 2 3 1 1
              1 1 2 1 2 2 2
              end
              label values apple scale14
              label values orange scale14
              label values banana scale14
              label def scale14 1 "Strongly disagree", modify
              label def scale14 2 "Disagree", modify
              label def scale14 3 "Agree", modify
              label def scale14 4 "Strongly agree", modify
              * label values carrot scale12
              * label values tomato scale12
              * label def scale12 1 "no", modify
              * label def scale12 2 "yes", modify
              
              gen long id = _n 
              rename (apple-tomato) (Percent=)
              reshape long Percent, i(id) j(food) string 
              label def what 1 apple 2 orange 3 banana 4 carrot 5 tomato
              encode food, gen(what) label(what)
              
              egen which = group(female treatment)
              label def which 1 "male control" 2 "male treated" 3 "female control" 4 "female treated"
              label val which which 
              
              label val Percent scale14 
              label var what "Whatever this is, really"
              label var Percent "% agreeing or disagreeing"
              
              floatplot Percent if inrange(what, 1, 3), over(what) by(which, compact note("") row(1) legend(pos(12))) highnegative(2) fcolors(red red*0.3 blue*0.3 blue) vertical legend(row(1)) name(G1, replace)
              
              label def scale12 1 "No", modify
              label def scale12 2 "Yes", modify
              label val  Percent scale12
              
              floatplot Percent if inrange(what, 4, 5), over(what) by(which, compact note("") row(1) legend(pos(12))) highnegative(1) fcolors(red blue) vertical legend(row(1)) name(G2, replace)
              Whether you are well advised to put everything in one graph is a good question.

              Click image for larger version

Name:	floatplot1.png
Views:	1
Size:	99.0 KB
ID:	1763079
              Click image for larger version

Name:	floatplot2.png
Views:	1
Size:	79.0 KB
ID:	1763080

              Comment


              • #8
                For context see my 2021 presentation via https://www.stata.com/meeting/uk21/

                Comment


                • #9
                  As two over options are not allowed, a different alternative to Nick's suggestions would be to combine graphs by treatment for the fruit and vegetable variables separately. Then you'd combine the combined graphs. Here is a way for the fruit variables. I use grc1leg2.

                  Code:
                  net install grc1leg2, from(http://digital.cgdev.org/doc/stata/MO/Misc)
                  Code:
                  clear
                  input float(female treatment apple orange banana carrot tomato)
                  0 0 1 3 2 1 1
                  0 0 3 3 3 1 2
                  0 0 2 2 2 2 1
                  0 0 3 4 2 2 2
                  0 0 2 1 4 2 1
                  0 0 4 2 1 2 2
                  0 0 3 3 2 1 2
                  0 1 2 4 3 2 2
                  0 1 3 2 2 1 1
                  0 1 2 3 4 1 2
                  0 1 4 4 2 2 1
                  1 0 1 1 3 1 1
                  1 0 2 2 2 1 1
                  1 0 3 3 3 2 2
                  1 0 4 2 2 2 1
                  1 0 2 3 3 2 2
                  1 1 3 2 1 1 1
                  1 1 2 4 2 2 2
                  1 1 4 2 3 1 1
                  1 1 2 1 2 2 2
                  end
                  label values apple scale14
                  label values orange scale14
                  label values banana scale14
                  label def scale14 1 "Strongly disagree", modify
                  label def scale14 2 "Disagree", modify
                  label def scale14 3 "Agree", modify
                  label def scale14 4 "Strongly agree", modify
                  label values carrot scale12
                  label values tomato scale12
                  label def scale12 1 "no", modify
                  label def scale12 2 "yes", modify
                  
                  lab values female female
                  lab def female 0 "Male" 1 "Female"
                  lab values treatment treatment
                  lab def treatment 0 "Control" 1 "Treatment"
                  
                  foreach var in apple orange banana{
                      floatplot `var', over(treatment)  by(female, leg(pos(6)) note("")) highnegative(2) ///
                      xtitle("") fcolors(red red*0.5 blue*0.5 blue) leg(row(1)) ytitle(`=proper("`var'")') saving(`var', replace)
                  }
                  grc1leg2 apple.gph orange.gph banana.gph, col(1) title(Fruits)
                  Click image for larger version

Name:	Graph.png
Views:	1
Size:	25.0 KB
ID:	1763084

                  Comment


                  • #10
                    Here is a way to fudge the (1,2,3,4) and (1,2) scales together.

                    Code:
                    * Example generated by -dataex-. For more info, type help dataex
                    clear
                    input float(female treatment apple orange banana carrot tomato)
                    0 0 1 3 2 1 1
                    0 0 3 3 3 1 2
                    0 0 2 2 2 2 1
                    0 0 3 4 2 2 2
                    0 0 2 1 4 2 1
                    0 0 4 2 1 2 2
                    0 0 3 3 2 1 2
                    0 1 2 4 3 2 2
                    0 1 3 2 2 1 1
                    0 1 2 3 4 1 2
                    0 1 4 4 2 2 1
                    1 0 1 1 3 1 1
                    1 0 2 2 2 1 1
                    1 0 3 3 3 2 2
                    1 0 4 2 2 2 1
                    1 0 2 3 3 2 2
                    1 1 3 2 1 1 1
                    1 1 2 4 2 2 2
                    1 1 4 2 3 1 1
                    1 1 2 1 2 2 2
                    end
                    
                    gen long id = _n 
                    rename (apple-tomato) (Percent=)
                    reshape long Percent, i(id) j(food) string 
                    label def what 1 apple 2 orange 3 banana 4 carrot 5 tomato
                    encode food, gen(what) label(what)
                    
                    egen which = group(female treatment)
                    label def which 1 "male control" 2 "male treated" 3 "female control" 4 "female treated"
                    label val which which 
                    
                    label val Percent scale14 
                    label var what "Whatever this is, really"
                    label var Percent "% disagreeing or agreeing"
                    
                    recode Percent 1=1 2=3 3=4 4=6 if inrange(what, 1,3)
                    recode Percent 1=2 2=5 if inrange(what,4,5)
                    
                    floatplot Percent, over(what) by(which, compact note("") row(1) legend(pos(12))) highnegative(3) fcolors(red red*0.6 red*0.3 blue*0.3 blue*0.6 blue) legend(row(2) order(1 "Strongly disagree" 3 "Disagree" 4 "Agree" 6 "Strongly agree" - 2 "No" 5 "Yes" -)) ysc(reverse) subtitle(, fcolor(none))
                    Click image for larger version

Name:	floatplot3.png
Views:	1
Size:	46.8 KB
ID:	1763105

                    Comment


                    • #11
                      There's also this, which may be closer to what you most want to compare. (There is presumably a serious medical dataset underneath or beyond all this!)

                      There's endless scope for shuffling the order of {male, female} X {control, treated}.

                      Code:
                      floatplot Percent, over(which) by(what, compact note("") row(1) legend(pos(12))) highnegative(3) fcolors(red red*0.6 red*0.3 blue*0.3 blue*0.6 blue) legend(row(2) order(1 "Strongly disagree" 3 "Disagree" 4 "Agree" 6 "Strongly agree" - 2 "No" 5 "Yes" -))  subtitle(, fcolor(none)) ytitle("")
                      Click image for larger version

Name:	floatplot4.png
Views:	1
Size:	41.9 KB
ID:	1763107

                      Comment


                      • #12
                        Thank you both for your extensive help, it's been extremely helpful.
                        #9 and #10 are both viable options for me and it's very interesting learning more about this command.

                        I do have a follow-up question if that's okay:
                        With floatplot, the height of the bars can be adjusted with the barwidth() option, but is there a way to decrease the height of the bars without the gaps between the bars increasing?

                        Using the code from #10 with texoffset and adjusted barwidth gives me the height of the bars I'd like, but the increased gaps between the bars are a bit uneasy on the eye imo:

                        Code:
                        * Example generated by -dataex-. For more info, type help dataex
                        clear
                        input float(female treatment apple orange banana carrot tomato)
                        0 0 1 3 2 1 1
                        0 0 3 3 3 1 2
                        0 0 2 2 2 2 1
                        0 0 3 4 2 2 2
                        0 0 2 1 4 2 1
                        0 0 4 2 1 2 2
                        0 0 3 3 2 1 2
                        0 1 2 4 3 2 2
                        0 1 3 2 2 1 1
                        0 1 2 3 4 1 2
                        0 1 4 4 2 2 1
                        1 0 1 1 3 1 1
                        1 0 2 2 2 1 1
                        1 0 3 3 3 2 2
                        1 0 4 2 2 2 1
                        1 0 2 3 3 2 2
                        1 1 3 2 1 1 1
                        1 1 2 4 2 2 2
                        1 1 4 2 3 1 1
                        1 1 2 1 2 2 2
                        end
                        
                        gen long id = _n 
                        rename (apple-tomato) (Percent=)
                        reshape long Percent, i(id) j(food) string 
                        label def what 1 apple 2 orange 3 banana 4 carrot 5 tomato
                        encode food, gen(what) label(what)
                        
                        egen which = group(female treatment)
                        label def which 1 "male control" 2 "male treated" 3 "female control" 4 "female treated"
                        label val which which 
                        
                        label val Percent scale14 
                        label var what "Whatever this is, really"
                        label var Percent "% disagreeing or agreeing"
                        
                        recode Percent 1=1 2=3 3=4 4=6 if inrange(what, 1,3)
                        recode Percent 1=2 2=5 if inrange(what,4,5)
                        
                        floatplot Percent, over(what) by(which, compact note("") row(1) legend(pos(12))) highnegative(3) textoffset(0) barwidth(0.2) fcolors(red red*0.6 red*0.3 blue*0.3 blue*0.6 blue) legend(row(2) order(1 "Strongly disagree" 3 "Disagree" 4 "Agree" 6 "Strongly agree" - 2 "No" 5 "Yes" -)) ysc(reverse) subtitle(, fcolor(none))

                        Thank you!

                        Comment


                        • #13
                          #12 I don't really follow. Decreased barwidth() implies more space between bars. The default position of text is chosen because it's hard to read numbers if a strong colour is the backdrop. Otherwise your options include aspect(), xsize() and ysize().

                          Comment


                          • #14
                            As Nick notes, you can change the graph's aspect ratio.

                            Code:
                            help aspect_option
                            Not changing the bar widths and specifying an aspect ratio less than 1 results in the following:

                            Code:
                            * Example generated by -dataex-. For more info, type help dataex
                            clear
                            input float(female treatment apple orange banana carrot tomato)
                            0 0 1 3 2 1 1
                            0 0 3 3 3 1 2
                            0 0 2 2 2 2 1
                            0 0 3 4 2 2 2
                            0 0 2 1 4 2 1
                            0 0 4 2 1 2 2
                            0 0 3 3 2 1 2
                            0 1 2 4 3 2 2
                            0 1 3 2 2 1 1
                            0 1 2 3 4 1 2
                            0 1 4 4 2 2 1
                            1 0 1 1 3 1 1
                            1 0 2 2 2 1 1
                            1 0 3 3 3 2 2
                            1 0 4 2 2 2 1
                            1 0 2 3 3 2 2
                            1 1 3 2 1 1 1
                            1 1 2 4 2 2 2
                            1 1 4 2 3 1 1
                            1 1 2 1 2 2 2
                            end
                            
                            gen long id = _n 
                            rename (apple-tomato) (Percent=)
                            reshape long Percent, i(id) j(food) string 
                            label def what 1 apple 2 orange 3 banana 4 carrot 5 tomato
                            encode food, gen(what) label(what)
                            
                            egen which = group(female treatment)
                            label def which 1 "male control" 2 "male treated" 3 "female control" 4 "female treated"
                            label val which which 
                            
                            label val Percent scale14 
                            label var what "Whatever this is, really"
                            label var Percent "% disagreeing or agreeing"
                            
                            recode Percent 1=1 2=3 3=4 4=6 if inrange(what, 1,3)
                            recode Percent 1=2 2=5 if inrange(what,4,5)
                            
                            floatplot Percent, over(what) by(which, compact note("") row(1) legend(pos(12))) highnegative(3) ///
                            textoffset(0) barwidth() fcolors(red red*0.6 red*0.3 blue*0.3 blue*0.6 blue) leg(row(2)  ///
                            order(1 "Strongly disagree" 3 "Disagree" 4 "Agree" 6 "Strongly agree" - 2 "No" 5 "Yes" -)) ///
                            ysc(reverse) subtitle(, fcolor(none)) aspect(0.95)
                            Click image for larger version

Name:	Graph.png
Views:	1
Size:	35.0 KB
ID:	1763367

                            Comment


                            • #15
                              Thank you both, aspect() is what helped me solve the issue. Appreciate your help!!

                              Comment

                              Working...
                              X