I have a dataset with three categorical variables and I want to visualize the relationship between all three in one graph. Any ideas?

Currently I am using the following three graphs: enter image description here

Each graph is for a level of baseline depression (Mild, Moderate, Severe). Then within each graph I look at the relationship between treatment (0,1) and Depression improvement (none, moderate, substantial).

These 3 graphs work to see the 3-way relationship, but is there a known way to do this with one graph?

  • 4
    $\begingroup$ Posting the data would let people play. $\endgroup$ – Nick Cox Apr 27 '15 at 18:01
  • 1
    $\begingroup$ You have 3 baseline categories, 2 treatment categories and 3 depression outcomes. Given the last. the proportions of each depression type could be displayed by 6 points on a triangular (trilinear, ternary) plot. $\endgroup$ – Nick Cox Apr 27 '15 at 18:04
  • 4
    $\begingroup$ What's wrong with these graphs? $\endgroup$ – Aksakal Apr 27 '15 at 19:03
  • $\begingroup$ Can you provide the data, as @NickCox requests? I gather it's only 18 numbers. $\endgroup$ – gung - Reinstate Monica Apr 27 '15 at 19:38

This is an interesting data set to try to represent graphically, partly because it's not really categorical. Both 3-level factors are ordinal and there is possible interplay between them (presumably, it's harder for a mild baseline to have substantial improvement -- or maybe substantial improvement means something different for each baseline).

With multiple variables, there isn't usually a single view that shows all the features you might care about. Some factors will be easier to compare than others. I think your original view is good and would be better with Nick Cox's suggestions: removing duplicate legends and using an ordinal color scale.

If you're most interesting in seeing the difference between treatments, you can emphasize the change by using a stacked area plot instead of stacked bars.

enter image description here

I'm usually wary of stacking in general because it's harder to read the middle values, but it does re-enforce the fixed-sum nature of this data. And it makes it easy to read the sum moderate+substantial if that's relevant. I've flipped the order of the improvement levels so that higher is better for the frequency.

Without stacking, the equivalent is a slope graph.

enter image description here

It's easier to read each level, but harder to understand the interplay. You have to keep in mind that the third line is directly dependent on the other two.

Given the ordinal nature of the data, it may be helpful to convert the improvement value into a numeric score, as is often done with Likert data. For instance, none=0, moderate=1, substantial=2. Then you can graph that variable on a continuous scale. The downside is that you have to find a reasonable scoring (e.g., maybe 0, 1 and 5 would be a truer representation).

enter image description here

Colophon: These plots were made with the Graph Builder feature in the software package JMP (which I help develop). Though made interactively, a script, for instance, for the area plot, without the coloring customizations, is:

Graph Builder(
    Graph Spacing( 15 ),
    Variables( X( :treatment ), Y( :frequency ),
        Group X( :baseline ), Overlay( :improvement )
    Elements( Area( X, Y ) )
  • 2
    $\begingroup$ +1. Some excellent ideas here. Much though I am queasy about stacking, I think the first graph works best. It brings out the interesting interaction: treatment 1 always produces more instances of substantial improvement and more of none! $\endgroup$ – Nick Cox Apr 30 '15 at 14:48
  • $\begingroup$ Great post. Is there anyway one could build the 1st graph you display in R? I haven't used JMP in a while. $\endgroup$ – Alejandro Ochoa May 2 '15 at 5:06
  • 1
    $\begingroup$ @AlejandroOchoa ggplot has an area geom. See Making a stacked area plot using ggplot2. $\endgroup$ – xan May 3 '15 at 0:00

First, here is my reading from the graph provided of the data for those who wish to play (experiment, if you like). NB off-by-one errors are certainly possible, as are gross errors.

    improvement  treatment   baseline   frequency  
           none          0       mild          5  
       moderate          0       mild         41  
    substantial          0       mild          4  
           none          1       mild         19  
       moderate          1       mild         19  
    substantial          1       mild         12  
           none          0   moderate         19  
       moderate          0   moderate         24  
    substantial          0   moderate          7  
           none          1   moderate         20  
       moderate          1   moderate         14  
    substantial          1   moderate         16  
           none          0     severe          7  
       moderate          0     severe         21  
    substantial          0     severe         22  
           none          1     severe         12  
       moderate          1     severe         15  
    substantial          1     severe         23  

Here is a reworking of the original design. One detail of the original data makes things simple: the number of people in each of the predictor combinations is the same, so plotting frequencies and plotting percents are the same. Here instead of a stacked (subdivided, segmented) bar chart, we separate out bars in a two-way bar chart or table plot design.

Much of the detail in graphics is just that, detail. Several small weaknesses in a graph can undermine its effectiveness and several small improvements can help too.

enter image description here

To spell it out:

  1. Three panels are not needed here, with their repetition of axes, legend and text.

  2. A legend is always curse as well as blessing, obliging the reader to go "back and forth" mentally (or memorise the legend, not something that appeals, however easy it might be). Informative text right by the bars is easier to follow.

  3. The fruit salad colour coding is dispensable. It seems arbitrary too: "substantial" improvement is a big deal, but I find even strong yellow a subdued colour. But we don't need colour when we have text to explain.

  4. Although some will shriek with horror at violating the distinction between Figure and Table, we can show the frequencies too. It's helpful to be able to think "4 people in this category".

  5. There is homage here to the traditional plotting of response on the vertical axis, just as in the original.

All that said, it is hard to see much structure in these data. When that's so, it is also hard to share the blame between (a) data without much structure and (b) the weaknesses of a graphical design for picking out not only predictor effects but also possible interactions. Treatment seems less important than baseline condition. But then, if the baseline was "mild", how much scope was there for "substantial" improvement? I'll stop there to stop making a fool of myself when the study of mental health data is certainly not a specialism, especially if the data turn out to be fake. But if they are real, we could do with a much larger sample size. (We usually say that, but there you go.)

EDIT The graph may naturally be complicated by an ordinal colour scheme if so desired:

enter image description here

For the record: the graphs used Stata code, including my own program tabplot downloadable using ssc inst tabplot.

tabplot improvement group [w=frequency] , showval ///
xmla(1.5 "mild" 3.5 "moderate" 5.5 "severe", noticks labgap(*4) labsize(medsmall)) ///
xla(1 "0" 2 "1" 3 "0" 4 "1" 5 "0" 6 "1") ///
xtitle(baseline and treatment) xsc(titlegap(*4)) bfcolor(emerald*0.2)

tabplot improvement group [w=frequency] , showval ///
xmla(1.5 "mild" 3.5 "moderate" 5.5 "severe", noticks labgap(*4) labsize(medsmall)) ///
xla(1 "0" 2 "1" 3 "0" 4 "1" 5 "0" 6 "1") ///
xtitle(baseline and treatment) xsc(titlegap(*2)) ///
sep(improvement2) bar3(bfcolor(emerald*0.2)) bar2(bfcolor(emerald*0.6)) ///
bar1(bfcolor(emerald)) barall(blcolor(green)) 
  • $\begingroup$ Is there anyway you could upload your graph with a color scheme that reflects the ordinal nature of the data? Also what software did you use to create the visual? $\endgroup$ – Alejandro Ochoa May 2 '15 at 5:10
  • $\begingroup$ These are very handsome plots $\endgroup$ – shadowtalker May 4 '15 at 13:00

I'm fond of using a 2-level x-axis for data like this. So your x-axis categories for a single chart might be:

  • Treatment=0, Baseline=Mild
  • Treatment=0, Baseline=Moderate
  • Treatment=0, Baseline=Severe
  • Treatment=1, Baseline=Mild
  • Treatment=1, Baseline=Moderate
  • Treatment=1, Baseline=Severe

...with the same counts by categories [none/moderate/substantial] histogram bars.

  • $\begingroup$ +1. I agree with the main idea here, as implemented in my answer. I can't tell how close my bar chart design is close to what you were imagining. $\endgroup$ – Nick Cox Apr 28 '15 at 19:05
  • $\begingroup$ Thanks, your chart looks great. Did you try looking at it with Treatment 0/1 as the outer category, and Baseline=Mild/Moderate/Severe as the category closer to the x-axis? I think if you presented it that way, you'd see a clearer pattern of - for within treatment=0, the "substantial" improvement bars rise steadily as baseline rises from Mild/Moderate/Severe. And that you'd see the same pattern (to a lesser extent) within treatment=1. In general I put the variable with fewer categories (e.g. treatment here) on the outside. But maybe you looked at it that way already. $\endgroup$ – Max Power Apr 29 '15 at 0:45
  • $\begingroup$ I didn't try the other way, but I did have in mind that the researcher might most want to compare the effects of treatments given baseline, which should be easier the way I did it. $\endgroup$ – Nick Cox Apr 29 '15 at 8:33
  • $\begingroup$ That makes sense to me. $\endgroup$ – Max Power Apr 29 '15 at 14:42

Isn't Mosaic plot specially designed for this purpose?

In R it would be like

d = read.table("data.dat", header=TRUE)
tab = xtabs(frequency ~ treatment+baseline+improvement, data=d)
mosaic(data=tab,~ treatment+baseline+improvement, shade=TRUE, cex=2.5)

Each categorical variables goes to one edge of the square, which is subdivided by its labels. (Thus, if you subdivide each edge at one level only, at most 4 categorical variables can be represented. IMHO, beyond 3 it becomes messy and harder to interpret). The size of the rectangles is proportional to frequency. This is the main idea behind mosaic plot and it is the same in this answer and the answer of Paweł Kleka.

The differences are in layouts of those rectangles and "niceties" provided by a specific R-package used for this type of plot. As you see from the answer of Paweł Kleka, the graphics package subdivides the upper edge at 2 levels instead of using the right edge. I used vcd package with default options, so that color indicates the degree of association between the variables. Grey means that data are consistent with (you cannot reject the hypothesis of) variable independence. Blue means that positive association exist between "severe" baseline and "substantial" improvement for both "0" and "1" treatment. (Surprise, surprise! I translate it as follows: if you have a severe depression, you will likely get substantially better whether you have a treatment or not. Correct me if I am wrong.)

One can adjust the plot according to one's needs, see, for example, here. The package also has several vignettes, google "vcd mosaic example" (as I just did). Wikipedia article cited at the very beginning also explains how to construct this type of plot and intuition behind it.

enter image description here

When you compare my picture with the picture in the answer of Paweł Kleka, it does not matter, that 'treatment' is on the left edge of each picture. You can easily change the edge location by changing the last line of my code and adjust the layout according to your needs. The common practice is that to the left goes the most important variable or the variable with the least number of labels. You can also change the order of labels (for example, so, that at the right edge the order is "none moderate substantial") by making the corresponding factor variable in R ordered and adjusting its levels.

  • $\begingroup$ There are at the time of writing two answers on mosaic plots. It would be helpful if each of you would expand on what your plot shows and why it is helpful, not least because the plots are quite different. $\endgroup$ – Nick Cox Apr 30 '15 at 20:44
  • $\begingroup$ @NickCox this one certainly looks different from the others. They're hardly the same display $\endgroup$ – shadowtalker May 4 '15 at 13:02
  • $\begingroup$ They both have treatment on the y axis. What would be gold from their proponents is commentary on the advantages and limitations of each display. $\endgroup$ – Nick Cox May 4 '15 at 13:10
  • $\begingroup$ Thanks for expanding your answer. I think the interest here is likely to in comparing responses given treatment and baseline. I naturally agree that you can tinker with which variable goes where, but did you try the other possibilities, and which works best? In looking at the response here the reader has to compare two rows of blocks simultaneously. $\endgroup$ – Nick Cox May 4 '15 at 14:54
  • $\begingroup$ @Nick Cox Thanks for your comments. It was the only thing that motivated me to expand. I did not try other possibilities. Actually, I think, if the author of the question finds this type of plot useful, he should try everything, then post and explain the results for the community. By the way, I am not saying that this type of plot is better than others. The point is: it was specially designed for categorical variables and for visualizing the independence and/or violation of independence. $\endgroup$ – lanenok May 4 '15 at 15:35

I sugest use mosaic plot

mosaicplot(table(moz), sort = c(3,1,2), color = T)


  • $\begingroup$ There are at the time of writing two answers on mosaic plots. It would be helpful if each of you would expand on what your plot shows and why it is helpful, not least because the plots are quite different. $\endgroup$ – Nick Cox Apr 30 '15 at 20:44

An option I'd consider is to use parallel sets. Some of the comparisons will be easier than others, but you can still see the relations among three categorical variables.

Here it is an example with Titanic Survival data:

Here is an example with Titanic survival data.

In R (given your tags) I have used ggparallel for implementing it. Some folks have discussed here on CV how to implement it in other ways.

  • $\begingroup$ I'm having trouble imagining this. Any chance you'd be able mock up an example? $\endgroup$ – shadowtalker May 4 '15 at 13:03
  • $\begingroup$ A line in the plot has its width proportional to the frequency of coocurrences of two categories. For the data used in the plots of the original question, there would be three horizontal axis: baseline depression, treatment and depression improvement. In each there are separate areas for each level of that category. Coocurrences are linked, with a width representing their frequency. $\endgroup$ – nazareno May 6 '15 at 23:03

The information can also be conveyed using following simple line chart:

enter image description here

The improvement is shown by different line types while the baseline group is shown in colors. These and the x-axis parameter (treatment here) can also be interchanged if desired.


Similar to parallel sets, as posted by nazareno above, you can use alluvial plots which are available from the alluvial R package. http://www.r-bloggers.com/alluvial-diagrams/


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.