Thanks to Kit Baum, a new package, twby, is now available on SSC. It is a prefix command for twoway graph, that creates a cross-tabulation of graphs. By aligning the graphs appropriately, one does not need to label each individual graph, so instead twby labels the columns at the top and the rows on the side. It automates the process described in (Buis & Weiss 2009) To install it, type in Stata ssc install twby

In its simplest form you just specify the two categorical variables that represent the rows and the columns of the table of graphs, followed by a colon, followed by the twoway graph that should appear in each cell. For example:

Code:
webuse auto2, clear
set scheme s1color

twby foreign rep78 : scatter price weight
Array


Most of the sub-options you would normally use in the by option can be specified in the twby prefix. So if we want to make this graph prettier, we could do something like so:

Code:
// use more sensible units
replace weight = 0.00045359237*weight
label variable weight "Weight (tonnes)"
replace price = price / 1000
label variable price "Price (1000s {c S|})"

//create the graph
twby foreign rep78, compact :    ///
    scatter price weight,        ///
    ylab(,angle(0)) xlab(1(.5)2)
Array


We could use twby to visualize a three way cross-tabulation in a way similar to (Cox 2016).

Code:
// open example data    
sysuse nlsw88, clear

// create the necessary categorical variables
gen byte urban = c_city + smsa if !missing(c_city,smsa)
label define urban 2 "central city" ///
                   1 "suburban"     ///
                   0 "rural"
label value urban urban
label variable urban "urbanicity"

gen byte marst = !never_married + married if !missing(never_married,married)
label define marst 0 "never married" ///
                   1 "widowed/divorced" ///
                   2 "married"
label value marst marst
label var marst "marital status"
                  
gen byte edcat = cond(grade <  12, 1,     ///
                 cond(grade == 12, 2,     ///
                 cond(grade <  16, 3,4))) ///
                 if !missing(grade)
label variable edcat "education"
label define edcat 1 "< highschool"    ///
                   2 "highschool"      ///
                   3 "some college"    ///
                   4 "college"            
label value edcat edcat                  

// the three way table we want to visualize
bys edcat: tab urban marst, row nofreq

// recreate that table as variables
contract edcat marst urban, zero nomiss
egen tot = total(_freq), by(urban edcat)
gen perc = _freq / tot *100

// variables that helps display the numbers in the graph
gen lab = strofreal(perc, "%5.0f")
gen y = -5

// the graph
twby urban marst ,                                             ///
        compact left xoffset(0.5) legend(off)                  ///
        title("Percentage in each marital status"              ///
              "given education and urbanicity") :              ///
    twoway bar perc edcat ,                                    ///
        xlab(1/4, val alt) yscale(range(0 75))                 ///
        ylab(none) ytitle("") barw(.5)                      || ///
    scatter y edcat ,                                          ///
        msymbol(none) mlab(lab) mlabpos(0) mlabcolor(black)
Array


Buis, Maarten L. and Weiss, Martin (2009) "Stata Tip 81: A Table of Graphs", The Stata Journal, 9(4):643-647. https://doi.org/10.1177/1536867X0900900410

Cox, Nicholas J. (2016) "Speaking Stata: Multiple bar charts in tableform", The Stata Journal, 16(2): 491-510. https://doi.org/10.1177/1536867X1601600214