# How can you visualize the relationship between 3 categorical variables?

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:

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?

• Posting the data would let people play. Commented Apr 27, 2015 at 18:01
• 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. Commented Apr 27, 2015 at 18:04
• What's wrong with these graphs? Commented Apr 27, 2015 at 19:03
• Can you provide the data, as @NickCox requests? I gather it's only 18 numbers. Commented Apr 27, 2015 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.

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.

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).

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 ) )
);

• +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! Commented Apr 30, 2015 at 14:48
• Great post. Is there anyway one could build the 1st graph you display in R? I haven't used JMP in a while. Commented May 2, 2015 at 5:06
• @AlejandroOchoa ggplot has an area geom. See Making a stacked area plot using ggplot2.
– xan
Commented May 3, 2015 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.

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:

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))

• 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? Commented May 2, 2015 at 5:10
• These are very handsome plots Commented May 4, 2015 at 13:00

Isn't Mosaic plot specially designed for this purpose?

In R it would be like

library(vcd)
tab = xtabs(frequency ~ treatment+baseline+improvement, data=d)


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.

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.

• 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. Commented Apr 30, 2015 at 20:44
• @NickCox this one certainly looks different from the others. They're hardly the same display Commented May 4, 2015 at 13:02
• 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. Commented May 4, 2015 at 13:10
• 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. Commented May 4, 2015 at 14:54
• @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. Commented May 4, 2015 at 15:35

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.

• +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. Commented Apr 28, 2015 at 19:05
• 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. Commented Apr 29, 2015 at 0:45
• 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. Commented Apr 29, 2015 at 8:33
• That makes sense to me. Commented Apr 29, 2015 at 14:42

I sugest use mosaic plot

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


• 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. Commented Apr 30, 2015 at 20:44

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

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.

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:

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.

• I'm having trouble imagining this. Any chance you'd be able mock up an example? Commented May 4, 2015 at 13:03
• 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. Commented May 6, 2015 at 23:03

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/