3-way crosstabs

library(pollster)
library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(knitr)
library(ggplot2)

It’s common to want to view a crosstab of two variables by a third variable, for instance educational attainment by sex and marital status. The function crosstab_3way accomplishes this. Row and cell percents are both supported; column percents are not.

illinois %>%
  # filter for recent years & limited ages
  filter(year > 2009,
         age > 39) %>%
  crosstab_3way(x = sex, y = educ6, z = maritalstatus, weight = weight,
                remove = c("widow/divorced/sep"),
                n = FALSE) %>%
  kable(digits = 0, caption = "Educational attainment by sex and marital status among Illinois residents ages 35+",
        format = "html")
Educational attainment by sex and marital status among Illinois residents ages 35+
sex maritalstatus LT HS HS Some Col AA BA Post-BA
Male Married 7 28 16 8 24 17
Male Never Married 13 35 19 11 15 8
Female Married 6 28 16 10 24 16
Female Never Married 11 27 21 8 17 15

Three-way crosstabs plot well as small multiples using ggplot facets.

illinois %>%
  # filter for recent years & limited ages
  filter(year > 2009,
         age > 34) %>%
  crosstab_3way(x = sex, y = educ6, z = maritalstatus, weight = weight,
                remove = c("widow/divorced/sep"), 
                format = "long") %>%
  ggplot(aes(educ6, pct, fill = maritalstatus)) +
  geom_bar(stat = "identity", position = position_dodge()) +
  facet_wrap(facets = vars(sex)) +
  labs("Educational attainment by sex and marital status",
       subtitle = "Illinois residents ages 40+") +
  theme(legend.position = "top")

The same plot can be made with margin of errors as well. (See the “crosstabs” vignette for a more detailed discussion of margin of errors.)

illinois %>%
  # filter for recent years & limited ages
  filter(year > 2009,
         age > 34) %>%
  moe_crosstab_3way(x = sex, y = educ6, z = maritalstatus, weight = weight,
                remove = c("widow/divorced/sep"), format = "long") %>%
  ggplot(aes(educ6, pct, fill = maritalstatus)) +
  geom_bar(stat = "identity", position = position_dodge(),
           alpha = 0.5) +
  geom_errorbar(aes(ymin = (pct - moe), ymax = (pct + moe),
                    color = maritalstatus),
                position = position_dodge()) +
  facet_wrap(facets = vars(sex)) +
  labs(title = "Educational attainment by sex and marital status",
       subtitle = "Illinois residents ages 35+",
       caption = "Current Population Survey, 2010-2018") +
  theme(legend.position = "top")

Special case, when the z-variable identifies survey waves

If the x-variable in your crosstab uniquely identifies survey waves for which the weights were independently generated, it is best practice to calculate the design effect independently for each wave. moe_wave_crosstab_3way does just that. All of the arguments remain the same as in moe_crosstab_3way.

moe_wave_crosstab_3way(df = illinois, x = sex, y = educ6, z = year, weight = weight)
#> Joining, by = "year"
#> # A tibble: 144 x 6
#>    year  sex    educ6      pct   moe     n
#>    <fct> <fct>  <fct>    <dbl> <dbl> <int>
#>  1 1996  Male   LT HS    15.1   1.80  1647
#>  2 1996  Male   HS       32.5   2.35  1647
#>  3 1996  Male   Some Col 20.3   2.02  1647
#>  4 1996  Male   AA        6.11  1.20  1647
#>  5 1996  Male   BA       17.7   1.91  1647
#>  6 1996  Male   Post-BA   8.38  1.39  1647
#>  7 1996  Female LT HS    14.2   1.65  1864
#>  8 1996  Female HS       34.8   2.25  1864
#>  9 1996  Female Some Col 22.8   1.98  1864
#> 10 1996  Female AA        6.72  1.18  1864
#> # … with 134 more rows