Announcement

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

  • Clustered bar graph with multiple categorical variables

    Hello,

    I am hoping to create a clustered bar graph to compare satisfaction with information received from various sources, such that the categories of satisfaction (i.e., not at all, a little, somewhat, very much) are on the x-axis with a separate bar for each source of information (i.e., A, B, C, D). Despite trying multiple different commands, namely 'bar graph, over()' and 'as category', I have not been able to achieve this. Guidance would be very much appreciated!!

    In the data below, 0 = Not applicable, 1 = Not at all, 2 = A little, 3 = Somewhat, 4 = A lot
    ID A B C D
    1 1 1 2 4
    2 1 2 1 4
    3 1 3 1 3
    4 1 0 0 0
    5 1 0 0 0
    6 1 1 1 1
    7 0 0 1 1
    8 1 2 1 2
    9 1 1 1 1
    10 1 1 2 .
    11 1 1 1 3
    12 1 1 1 2
    13 1 1 1 1
    14 0 0 2 2
    15 1 1 1 1
    16 1 1 1 4
    17 1 2 1 2
    18 1 1 1 1
    19 3 2 2 3
    20 1 1 1 4

  • #2
    I don't get a clear sense from this what you want to be clusters, or what you want to be bars, and your syntax does not help much, as bar graph isn't even legal and you don't mention any variable names.

    Either way, I see here a two-way table, so you need a way of showing the frequency or percent breakdown by different sources. Conversely, if the identifiers are important information you would need a quite different display.

    In Stata terms you would have more flexibility with a long data structure. Here I ignore the not applicables and focus on some methods for ordinal scales such as you have here. For more on that, see my presentation at https://www.stata.com/meeting/uk21/

    Please note the use of CODE delimiters and a data example similar to what you would get with dataex.

    I would probably tone down some of the colours in further work.


    Code:
    clear 
    input ID    A    B    C    D
    1    1    1    2    4
    2    1    2    1    4
    3    1    3    1    3
    4    1    0    0    0
    5    1    0    0    0
    6    1    1    1    1
    7    0    0    1    1
    8    1    2    1    2
    9    1    1    1    1
    10    1    1    2    .
    11    1    1    1    3
    12    1    1    1    2
    13    1    1    1    1
    14    0    0    2    2
    15    1    1    1    1
    16    1    1    1    4
    17    1    2    1    2
    18    1    1    1    1
    19    3    2    2    3
    20    1    1    1    4
    end 
    
    rename (A-D) (answer=)
    reshape long answer, i(ID) j(source) string 
    label def answer 0 "Not applicable" 1 "Not at all" 2  "A little" 3 "Somewhat" 4 "A lot"
    label val answer answer 
    
    set scheme s1color 
    
    preserve 
    
    drop if answer == 0 
    
    * download from Stata Journal 
    tabplot answer source, percent(source) name(G1, replace) showval separate(answer) ///
    bar1(color(red)) bar2(color(red*0.5)) bar3(color(blue*0.5)) bar4(color(blue)) yasis yla(1/4) ysc(r(1 .))
    
    * download from SSC 
    floatplot answer, over(source) highnegative(2) name(G2, replace) fcolors(red red*0.5 blue*0.5 blue) vertical subtitle(% by source)
    
    restore

    Click image for larger version

Name:	yablo_G1.png
Views:	1
Size:	20.6 KB
ID:	1672160
    Click image for larger version

Name:	yablo_G2.png
Views:	1
Size:	22.0 KB
ID:	1672161





    Comment


    • #3
      Thank you very much for your reply, Nick. My apologies regarding the clarity of my post. Regarding the variables, the names are simply 'ID', 'A', 'B', etc.

      With code below, I am able to have only one variable listed:
      graph bar (count), over(A, label(angle(90))) blabel(bar) title(Satisfaction of individuals with information obtained from Source A)

      By creating a long data structure, would this code be sufficient?

      I am hoping for something like the figure below:

      Thank you so much!

      Click image for larger version

Name:	Screen Shot 2022-07-05 at 10.53.03 AM.png
Views:	1
Size:	145.1 KB
ID:	1672218

      Comment


      • #4
        The graph in #3 is still unclear to me, as the sum of percents vastly exceeds 100 for each source.

        The code in #3 won't work with the data structure recommended in #2 as you no longer have a variable A. and in any case it only works with source A.

        You may be seeking something more like

        Code:
        rename (A-D) (answer=)
        reshape long answer, i(ID) j(source) string 
        label def answer 0 "Not applicable" 1 "Not at all" 2  "A little" 3 "Somewhat" 4 "A lot"
        label val answer answer 
        
        set scheme s1color 
        
        drop if answer == 0 
        
        graph bar (percent), over(source) over(answer) asyvars
        although that has to be a tentative answer as I have no idea how you are calculating percents.

        Comment


        • #5
          The graph in #3 was just a mock-up, not based on any real values! Sorry for the confusion.

          Your code worked perfectly. Thank you so much!

          Comment


          • #6
            Apologies for reactivating an old post. I am using Stata 18 on a Mac.

            I have survey data where students were asked to nominate their 3 most valuable kinds of topics in a professional development course, so as a result I have 8 variables that have a value of 1 if the student chose that kind of topic and 0 if they didn't (not 1 variable with 8 mutually exclusive categories, as would be if students could only nominate 1 most valuable).

            My goal is to create a graph bar with the 8 variables along the y axis and the bars showing the percentage selected within each of those variables (see the attached image). I have managed that in the code below, but would also then like to separate each variable by which year the student is currently in (either 1st, or 2nd and 3rd together, which is another binary variable) and present those as grouped bars. I can manage to present the information as all 8 bars for the 1st years and then all 8 for the other category of students, but can't manage to group the bars.

            Code:
            clear
            input id year_of_study q454_1 q454_2 q454_3 q454_4 q454_5 q454_6 q454_7 q454_8
            
            2842423    1st year    0    0    1    1    1    0    0    0
            6109904    1st year    1    1    0    1    0    0    0    0
            7891094    1st year    0    0    1    0    0    0    1    1
            4152485    1st year    0    1    0    0    0    1    1    0
            7080675    1st year    1    1    0    1    0    0    0    0
            9178590    1st year    0    1    0    1    0    1    0    0
            8396157    1st year    0    0    1    1    1    0    0    0
            9015996    1st year    1    0    0    1    1    0    0    0
            6124645    1st year    0    1    1    1    0    0    0    0
            3435129    1st year    0    0    1    1    0    0    0    1
            3880357    2nd year    0    0    0    0    0    0    0    0
            8847443    2nd year    0    0    0    1    0    1    0    0
            4472876    2nd year    1    0    1    0    0    0    1    0
            7303073    2nd year    1    0    1    0    1    0    0    0
            6904076    2nd year    1    0    1    1    0    0    0    0
            5832606    2nd year    1    0    1    0    1    0    0    0                           
            6068406    2nd year    0    0    0    0    0    0    0    1
            3995112    2nd year    0    0    0    0    1    1    1    0
            5009258    2nd year    1    0    1    0    1    0    0    0
            9582047    2nd year    0    0    1    1    0    0    0    1
            end
            
            graph bar (mean) q454_1 - q454_8,     ///
            percentages showyvars     ///
            yvaroptions(relabel(1 "Meet students"     ///
            2 "Study readiness"     ///
            3 "Self-management"     ///
            4 "Academic skills"     ///
            5 "Career info"     ///
            6 "Prof. development"     ///
            7 "Learn opportunities"     ///
            8 "Learn supports" ) label(labsize(tiny)))     ///
            bargap(10)  ytitle("% of students chose among 3 most valuable topics/areas") yscale(range(0 100))     ///
            ylabel(0(10)100) title("Student most valuable topics") note("N=99", size(tiny) span)     ///
            legend(off) scheme(swift_red)
            Any and all help would be very appreciated!

            Attached Files

            Comment


            • #7
              Revisiting an old thread is never a problem if your post is relevant. Ideas posted in 2022 can easily still be pertinent.

              I have problems following this on several levels, mostly trivial, and have made some guesses here and there.

              Your code won't run. EIther you had some code and tried to make it closer to what dataex would do, or you ran dataex and then edited the output for some reason. Either way, the code is broken and won't run, if only because you're asking Stata to read 10 numeric variables, but presenting 11 fields, two of which can't be read as numeric.

              You're asking for a community-contributed scheme without explaining where it comes from. Please see FAQ Advice #12 on explaining community-contributed code you use, I haven't installed that, scheme and so have just ignored that call, which you can fix independenlly,

              You are asking for labels to go up to 100, but your results are all much smaller. My guess is that your call isn't asking for the calculations you need. The point is one you make, that your percents don't have to add to 100. You can get means with the graph command, but as you want them shown as percents, the solution seems to be to multiply by 100, not ask for graph bar's idea of percentages.

              I think you need a different data layout to do (easily) what you want.

              Asking for a tiny font size is almost never a good solution. The real problem is that you need enough space to show your category text clearly. That is among the most important information in the graph. Switching to horizontal bars is in my view usually a better solution.

              Otherwise i hope this helps. Putting the y axis stuff at the top is a purely personal preference for graphs with table flavour. If you don't like them just omit the ysc(alt) option. But see https://journals.sagepub.com/doi/pdf...867X1201200314 for a discussion of this choice.

              Code:
              clear
              input id year_of_study q454_1 q454_2 q454_3 q454_4 q454_5 q454_6 q454_7 q454_8
              2842423    1     0    0    1    1    1    0    0    0
              6109904    1     1    1    0    1    0    0    0    0
              7891094    1     0    0    1    0    0    0    1    1
              4152485    1     0    1    0    0    0    1    1    0
              7080675    1     1    1    0    1    0    0    0    0
              9178590    1     0    1    0    1    0    1    0    0
              8396157    1     0    0    1    1    1    0    0    0
              9015996    1     1    0    0    1    1    0    0    0
              6124645    1     0    1    1    1    0    0    0    0
              3435129    1     0    0    1    1    0    0    0    1
              3880357    2     0    0    0    0    0    0    0    0
              8847443    2     0    0    0    1    0    1    0    0
              4472876    2     1    0    1    0    0    0    1    0
              7303073    2     1    0    1    0    1    0    0    0
              6904076    2     1    0    1    1    0    0    0    0
              5832606    2     1    0    1    0    1    0    0    0                     
              6068406    2     0    0    0    0    0    0    0    1
              3995112    2     0    0    0    0    1    1    1    0
              5009258    2     1    0    1    0    1    0    0    0
              9582047    2     0    0    1    1    0    0    0    1
              end 
              
              reshape long q454_ , i(id year) j(which)
              
              label define year 1 "1st" 2 "2nd/3rd"
              label val year year 
              
              replace q454_ = q454_ * 100 
              
              graph hbar (mean) q454_, over(year) asyvars ///
              over(which,      ///
              relabel(1 "Meet students"     ///
              2 "Study readiness"     ///
              3 "Self-management"     ///
              4 "Academic skills"     ///
              5 "Career info"     ///
              6 "Prof. development"     ///
              7 "Learn opportunities"     ///
              8 "Learn supports" ))     ///
              ytitle("% of students chose among 3 most valuable topics/areas")      /// 
              title("Student most valuable topics") note("N=99", span) legend(ring(0) pos(5)) ///
              ysc(alt)
              Click image for larger version

Name:	student_choice.png
Views:	1
Size:	51.1 KB
ID:	1783598

              Comment


              • #8
                Thank you so much, Nick. That is much more like what I wanted. My Stata capabilities are all in data cleaning and analysis, so the graph command, colour schemes, and dataex are all things I don't use much. I apologise for where I have used them incorrectly.

                You are quite right, of course, that the results were not what I actually wanted. I hadn't wanted to mess around with reshapes, but if that's how to get it done I will go with it!

                Interestingly, when I run your code on the full sample, the legend is in one row rather than two.

                Thanks again!

                Comment


                • #9
                  I hadn't wanted to mess around with reshapes, but if that's how to get it done I will go with it!
                  You can get so far without a reshape so that this should work. If it's not what you want, there are various new options for different groupings in Stata 19, but they aren't accessible to you.

                  Code:
                  clear
                  input id year_of_study q454_1 q454_2 q454_3 q454_4 q454_5 q454_6 q454_7 q454_8
                  2842423    1     0    0    1    1    1    0    0    0
                  6109904    1     1    1    0    1    0    0    0    0
                  7891094    1     0    0    1    0    0    0    1    1
                  4152485    1     0    1    0    0    0    1    1    0
                  7080675    1     1    1    0    1    0    0    0    0
                  9178590    1     0    1    0    1    0    1    0    0
                  8396157    1     0    0    1    1    1    0    0    0
                  9015996    1     1    0    0    1    1    0    0    0
                  6124645    1     0    1    1    1    0    0    0    0
                  3435129    1     0    0    1    1    0    0    0    1
                  3880357    2     0    0    0    0    0    0    0    0
                  8847443    2     0    0    0    1    0    1    0    0
                  4472876    2     1    0    1    0    0    0    1    0
                  7303073    2     1    0    1    0    1    0    0    0
                  6904076    2     1    0    1    1    0    0    0    0
                  5832606    2     1    0    1    0    1    0    0    0                     
                  6068406    2     0    0    0    0    0    0    0    1
                  3995112    2     0    0    0    0    1    1    1    0
                  5009258    2     1    0    1    0    1    0    0    0
                  9582047    2     0    0    1    1    0    0    0    1
                  end 
                  
                  label define year 1 "1st" 2 "2nd/3rd"
                  label val year year 
                  
                  foreach v of var q454* { 
                      replace `v' = `v' * 100
                  }
                  
                  graph hbar (mean) q454*, over(year) asyvars ///
                  yvaroptions(relabel(1 "Meet students"     ///
                  2 "Study readiness"     ///
                  3 "Self-management"     ///
                  4 "Academic skills"     ///
                  5 "Career info"     ///
                  6 "Prof. development"     ///
                  7 "Learn opportunities"     ///
                  8 "Learn supports" ))     ///
                  ytitle("% of students chose among 3 most valuable topics/areas")      /// 
                  title("Student most valuable topics") note("N=99", span) legend(ring(0) pos(5)) ///
                  ysc(alt)

                  Interestingly, when I run your code on the full sample, the legend is in one row rather than two.
                  I can't see your latest code, but presuming that you aren't asking for what you got, I would put that down to whatever scheme you're using.

                  Comment


                  • #10
                    How to Handle Heteroskedasticity and Autocorrelation in a FE Panel Model
                    Hello everyone,
                    I’m working with a fixed-effects panel model (219 observations), and my diagnostic tests show both heteroskedasticity and within-panel autocorrelation. I’m unsure about the best way to address both issues.

                    Should I use vce(robust), switch to cluster(id), or consider Driscoll–Kraay (xtscc) for better correction?

                    Any guidance would be greatly appreciated. Thank you.

                    Comment


                    • #11
                      RASTI WIJAYANTI please read the FAQ Advice and then start a new thread.

                      Comment


                      • #12
                        Back to Shannon Walding s sub-thread in #6 to #9:

                        I should flag that multiplying by 100 is not the only choice here. If data are 0 and 1 then means are between 0 and 1 and it's sufficient that axis labels show what you want:

                        Code:
                        yla(0 0.2 "20" 0.4 "40" 0.6 "60" 0.8 "80")
                        or whatever. You may need two passes in order to get as many labels as you want or need (and no more).

                        See

                        Code:
                        SJ-24-1 gr0092_1  . . . . . . . . . . . . . . . . Software update for mylabels
                                (help nicelabels, mylabels, myticks if installed) . . . . .  N. J. Cox
                                Q1/24   SJ 24(1):182--184
                                fixes a bug that could bite if the options myscale() and
                                clean were specified together
                        
                        SJ-22-4 gr0092  . . . . . . . . . . . . Speaking Stata: Automating axis labels
                                (help nicelabels, mylabels, myticks if installed) . . . . .  N. J. Cox
                                Q4/22   SJ 22(4):975--995
                                provides commands to handle two common problems with graph
                                axis labels: decide in advance on some "nice" numbers to
                                use on one or both axes and show particular labels on some
                                transformed scale
                        
                        SJ-8-1  gr0032  . . . . . . .  Stata tip 59: Plotting on any transformed scale
                                . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  N. J. Cox
                                Q1/08   SJ 8(1):142--145                                 (no commands)
                                tip on how to graph data on a transformed scale
                        for more discussion, especially if the mapping is not as simple as in this case.

                        Comment


                        • #13
                          Thank you, Nick! All excellent options that have helped me learn more about graphing.
                          Yes, probably the scheme where the legend gave me one row instead of two - I just added an option for two rows of label.
                          For those searching later, the following code gave me what I was after:

                          Code:
                          preserve
                          keep if wave==1
                          keep id year q454_* 
                          reshape long q454_ , i(id year) j(which)
                          
                          replace q454_ = q454_ * 100 
                          
                          graph hbar (mean) q454_, over(year) asyvars ///
                          over(which,      ///
                          relabel(1 "Meet students"     ///
                          2 "Study readiness"     ///
                          3 "Self-management"     ///
                          4 "Academic skills"     ///
                          5 "Career info"     ///
                          6 "Prof. development"     ///
                          7 "Learn opportunities"     ///
                          8 "Learn supports" ))     ///
                          ytitle("% of students chose among 3 most valuable topics/areas")      /// 
                          note("N=99", span) legend(ring(0) pos(5) rows(2)) ///
                          ysc(alt) scheme(s2gcolor)
                          restore

                          Comment


                          • #14
                            Hello!

                            I am evaluating the quality of a treatment and need to draw bar charts showing the percentages of four categorical variables in long format.
                            A few years ago, I was able to create this type of graph using the tabplot and catplot functions. However, it seems that catplot no longer works properly with this number of variables.

                            I previously used the following commands:

                            label values treat1 origin
                            label values treat2 origin
                            label values treat3 origin
                            label values treat4 origin

                            label def origin 1 "SUB-dose-MC", modify
                            label def origin 2 "TA-MC", modify
                            label def origin 3 "SUPER-dose-MC", modify
                            label def origin 4 "TI-MC", modify
                            set scheme s1rcolor

                            tabplot treat1 treat2, percent showval name(G0, replace)
                            tabplot treat3 treat4, percent showval name(B0, replace)
                            graph combine G0 B0, name(G0, replace)

                            catplot treat1, name(G1, replace) percent
                            catplot treat2, name(G2, replace) percent
                            catplot treat3, name(G3, replace) percent
                            catplot treat4, name(G4, replace) percent
                            graph combine G1 G2 G3 G4, name(G1_4, replace)

                            gen idt = _n
                            reshape long treat, i(idt) j(treatment)
                            label val treat origin

                            catplot treat treatment, name(G5, replace) percent(treatment)
                            catplot treatment treat, asyvars name(G6, replace) percent(treatment)



                            I would be very grateful if you could help me create a new command to generate this bar chart.

                            Here are my data:
                            n treat1 treat1 treat3 treat4
                            1 2 2 1 .
                            2 2 3 3 3
                            3 2 3 3 3
                            4 2 . . .
                            5 2 2 2 2
                            6 1 1 2 1
                            7 3 3 3 3
                            8 2 2 2 .
                            9 2 2 2 2
                            10 2 . . .
                            11 2 2 1 2
                            12 2 . . .
                            13 2 3 3 3
                            14 2 2 2 .
                            15 2 . . .
                            16 2 . . .
                            17 2 2 3 .
                            18 2 . . .
                            19 2 2 2 2
                            20 2 2 1 2
                            21 0 0 1 .

                            Where:

                            treat1 = Scenario 1
                            treat2 = Scenario 2
                            treat3 = Scenario 3
                            treat4 = Scenario 4

                            Category coding:

                            0 = Inconclusive
                            1 = Undertreatment
                            2 = Adequate treatment
                            3 = Overtreatment


                            Thank you very much!
                            Attached Files
                            Last edited by Zilda Braid; 08 Jul 2026, 15:42.

                            Comment


                            • #15
                              Note that tabplot is from the Stata Journal and catplot is from SSC, as you are asked to explain in FAQ Advice #12. The latter is a wrapper for the official graph bar which you can use here directly after computing the percentages. The following assumes missing values are excluded in calculating percentages.

                              Code:
                              clear
                              input float(n treat1 treat2 treat3 treat4)
                              1 2 2 1 .
                              2 2 3 3 3
                              3 2 3 3 3
                              4 2 . . .
                              5 2 2 2 2
                              6 1 1 2 1
                              7 3 3 3 3
                              8 2 2 2 .
                              9 2 2 2 2
                              10 2 . . .
                              11 2 2 1 2
                              12 2 . . .
                              13 2 3 3 3
                              14 2 2 2 .
                              15 2 . . .
                              16 2 . . .
                              17 2 2 3 .
                              18 2 . . .
                              19 2 2 2 2
                              20 2 2 1 2
                              21 0 0 1 .
                              end
                              
                              reshape long treat, i(n) j(origin)
                              drop if missing(treat)
                              
                              label values origin origin
                              label def origin 1 "SUB-dose-MC", modify
                              label def origin 2 "TA-MC", modify
                              label def origin 3 "SUPER-dose-MC", modify
                              label def origin 4 "TI-MC", modify
                              
                              label values treat treat
                              label def treat 0 "Inconclusive", modify
                              label def treat 1 "Undertreatment", modify
                              label def treat 2 "Adequate", modify
                              label def treat 3 "Overtreatment", modify
                              
                              bys treat origin: gen percent=_N
                              bys treat: replace percent=(percent/_N)*100
                              set scheme  stcolor
                              
                              gr bar percent, over(treat, gap(10)) over(origin) asyvars ///
                              leg(row(1) pos(6)) ytitle(Percent) blab(total, format(%3.0f)) ylab("")

                              Click image for larger version

Name:	Graph.png
Views:	1
Size:	48.5 KB
ID:	1786516

                              Comment

                              Working...
                              X