Warning: Missing `trust` will be set to FALSE by default for RDS in 2.0.0.
Data Summarization: Tables
What are summary tables?
- Summary tables are a way to present data in a structured format
- The use is a little vague here…
- It could mean:
- A table for categorical data (like a frequency table)
- A presentable table for categorical and numeric data
- It could mean:
- We will cover two functions that can help us create summary tables for categorical and numeric data:
tabyl()from thejanitorpackagetbl_summary()from thegtsummarypackage
What does tabyl() do?
tabyl()is a simple function that creates a frequency table for a single categorical variable- It returns a data frame with three columns:
n: the count in each categorypercent: that count as a proportion of all rows (including missing values)valid_percent: that count as a proportion of only the non-missing rows
- Pretty simple to use and can be used to create two-by-two tables
- Need to feed it one variable at a time
What does tabyl() output look like?
hrs_00 |>
tabyl(degree) degree n percent valid_percent
High school diploma 512 0.18768328 0.22674934
Master's degree 487 0.17851906 0.21567759
Professional degree (Ph.D./M.D./JD) 194 0.07111437 0.08591674
None 384 0.14076246 0.17006200
Associate's degree 411 0.15065982 0.18201949
GED 118 0.04325513 0.05225864
Bachelor's degree 152 0.05571848 0.06731621
<NA> 470 0.17228739 NA
Cross-tabulations with tabyl()
- You can also cross-tabulate two variables by adding a second column name
- This will give you a table of counts for each combination of the two variables
hrs_00 |>
tabyl(degree, sex) degree Female Male
High school diploma 281 231
Master's degree 286 201
Professional degree (Ph.D./M.D./JD) 115 79
None 238 146
Associate's degree 225 186
GED 60 58
Bachelor's degree 88 64
<NA> 275 195
Dressing up a tabyl() with adorn_*()
adorn_*()functions let you format that table for presentation- These are designed to be piped together, one after another
| Function | What it does |
|---|---|
adorn_totals() |
Adds a total row and/or column (where = "row", "col", or "both") |
adorn_percentages() |
Converts counts to proportions (denominator = "row", "col", or "all") |
adorn_pct_formatting() |
Converts proportions to nicely formatted percents (control decimals with digits =) |
adorn_ns() |
Adds the raw counts back onto a table of percents, e.g. "45.2% (120)" |
Example: Formatting a frequency table
- We can add totals, convert to percentages, format as percents, and add counts back to a frequency table
- Order matters!
- You generally want: tabulate → add totals → convert to percentages → format as percents → add counts back
- 1
- Add a row for totals to the table. A little confusing because it totals each column, but “row” refers to the position of the total row, not what is being totaled
- 2
- Format the percentages to 2 decimal places
degree n percent valid_percent
High school diploma 512 18.77% 22.67%
Master's degree 487 17.85% 21.57%
Professional degree (Ph.D./M.D./JD) 194 7.11% 8.59%
None 384 14.08% 17.01%
Associate's degree 411 15.07% 18.20%
GED 118 4.33% 5.23%
Bachelor's degree 152 5.57% 6.73%
<NA> 470 17.23% -
Total 2728 100.00% 100.00%
Example: Formatting a cross-tabulation
- We can also add totals, convert to percentages, format as percents, and add counts back to a cross-tabulation
- 1
-
Create a cross-tabulation of
degreebysex - 2
- Add a row for totals to the table. A little confusing because it totals each column.
- 3
- Convert the counts to percentages, using the column totals (the added row of totals) as the denominator.
- 4
- Format the percentages to 1 decimal place
- 5
- Add the raw counts back to the table, in front of the percentages.
degree Female Male
High school diploma 281 (17.9%) 231 (19.9%)
Master's degree 286 (18.2%) 201 (17.3%)
Professional degree (Ph.D./M.D./JD) 115 (7.3%) 79 (6.8%)
None 238 (15.2%) 146 (12.6%)
Associate's degree 225 (14.3%) 186 (16.0%)
GED 60 (3.8%) 58 (5.0%)
Bachelor's degree 88 (5.6%) 64 (5.5%)
<NA> 275 (17.5%) 195 (16.8%)
Total 1,568 (100.0%) 1,160 (100.0%)
What does tbl_summary() do?
tbl_summary()builds a publication-ready summary table for all the columns you give it in a single command- It automatically picks sensible statistics based on each variable’s type:
- Numeric variables: median (interquartile range)
- Categorical variables: count (percent)
- Missing values are shown as their own “Unknown” row
- We need to use
select()to decide what goes into the table
- Great for publications!
- You can customize the statistics shown, the labels, and even stratify the table by a grouping variable
- Some of the customizations are syntax heavy
What does tbl_summary() output look like?
hrs_00 |>
1 select(-id, -hhid, -pn) |>
tbl_summary()- 1
- Only removing this because it would add thousands of rows to this table! See what happens when you don’t remove these variables!
| Characteristic | N = 2,7281 |
|---|---|
| Ever Had Diabetes | 735 (27%) |
| Highest Degree Earned | |
| High school diploma | 512 (23%) |
| Master's degree | 487 (22%) |
| Professional degree (Ph.D./M.D./JD) | 194 (8.6%) |
| None | 384 (17%) |
| Associate's degree | 411 (18%) |
| GED | 118 (5.2%) |
| Bachelor's degree | 152 (6.7%) |
| Unknown | 470 |
| Respondent Birth Year | 1,967 (1,958, 1,970) |
| Respondent Birth Month | 7.0 (3.5, 10.0) |
| Respondent Birth Date | 2,602 (-489, 3,757) |
| Proxy Interview Status | |
| Respondent | 2,721 (100%) |
| Proxy | 7 (0.3%) |
| Couple Household Status | |
| Not a couple HH | 1,093 (40%) |
| Couple HH | 1,635 (60%) |
| Sex | |
| Female | 1,568 (57%) |
| Male | 1,160 (43%) |
| Age at Interview (Months) | 671 (635, 778) |
| Age at Interview (Years) | 55 (52, 64) |
| Years of Education | 13.0 (12.0, 16.0) |
| Unknown | 507 |
| Race/Ethnicity | |
| White/Caucasian | 1,000 (45%) |
| Black/African American | 884 (39%) |
| Other | 358 (16%) |
| Unknown | 486 |
| Current Smoker | 459 (17%) |
| Unknown | 2 |
| Ever Smoked | 1,243 (46%) |
| Unknown | 1 |
| Current Drinker | 1,874 (69%) |
| Self-Reported Height | 1.68 (1.63, 1.77) |
| Unknown | 55 |
| Self-Reported Health Status | |
| Fair | 743 (27%) |
| Good | 870 (32%) |
| Very Good | 647 (24%) |
| Excellent | 277 (10%) |
| Poor | 187 (6.9%) |
| Unknown | 4 |
| Vigorous Physical Activity Frequency | |
| Never | 1,443 (53%) |
| >1 per week | 586 (22%) |
| 1 per week | 270 (9.9%) |
| 1-3 per week | 275 (10%) |
| Every day | 145 (5.3%) |
| Unknown | 9 |
| Ever Had High Blood Pressure | 1,433 (53%) |
| Unknown | 8 |
| Ever Had Cancer | 271 (9.9%) |
| Ever Had Lung Disease | 160 (5.9%) |
| Ever Had Heart Problems | 373 (14%) |
| Ever Had Stroke | 193 (7.1%) |
| Ever Had Psychiatric Problems | 540 (20%) |
| Ever Had Sleep Disorder | 534 (20%) |
| Ever Had Arthritis | 1,006 (37%) |
| Total Number of Chronic Conditions | |
| 0 | 635 (23%) |
| 1 | 675 (25%) |
| 2 | 651 (24%) |
| 3 | 443 (16%) |
| 4 | 227 (8.3%) |
| 5 | 71 (2.6%) |
| 6 | 23 (0.8%) |
| 7 | 3 (0.1%) |
| CES-D Depression Score | |
| 0 | 993 (36%) |
| 1 | 657 (24%) |
| 2 | 346 (13%) |
| 3 | 200 (7.4%) |
| 4 | 152 (5.6%) |
| 5 | 122 (4.5%) |
| 6 | 105 (3.9%) |
| 7 | 93 (3.4%) |
| 8 | 53 (1.9%) |
| Unknown | 7 |
| Total Household Income | 51,462 (19,852, 116,500) |
| 1 n (%); Median (Q1, Q3) | |
Customizing tbl_summary()
statistic =lets you change which statistics are shown, using{ }to insert stat names like{mean},{sd},{median},{n},{p}(percent)label =lets you rename variables to something more readable
- 1
-
Show mean (sd) for
age_yrinstead of the default median (IQR) - 2
-
Give
age_yra nicer display name
| Characteristic | N = 2,7281 |
|---|---|
| Age (years) | 59 (9) |
| Sex | |
| Female | 1,568 (57%) |
| Male | 1,160 (43%) |
| Ever Had Diabetes | 735 (27%) |
| 1 Mean (SD); n (%) | |
Presentation-ready tables with gt()
gt()turns any data frame or table (including atabyl()output) into a polished, presentation-ready table- It’s a general-purpose “make this table look nice” tool
- You can further customize titles, column labels, and colors using additional
gtfunctions (liketab_header()orfmt_percent())
Original table from tabyl()
hrs_00 |>
tabyl(degree) degree n percent valid_percent
High school diploma 512 0.18768328 0.22674934
Master's degree 487 0.17851906 0.21567759
Professional degree (Ph.D./M.D./JD) 194 0.07111437 0.08591674
None 384 0.14076246 0.17006200
Associate's degree 411 0.15065982 0.18201949
GED 118 0.04325513 0.05225864
Bachelor's degree 152 0.05571848 0.06731621
<NA> 470 0.17228739 NA
Using gt() to make it look nice
hrs_00 |>
tabyl(degree) |>
gt() |>
tab_header(title = "Highest Degree Earned")| Highest Degree Earned | |||
| degree | n | percent | valid_percent |
|---|---|---|---|
| High school diploma | 512 | 0.18768328 | 0.22674934 |
| Master's degree | 487 | 0.17851906 | 0.21567759 |
| Professional degree (Ph.D./M.D./JD) | 194 | 0.07111437 | 0.08591674 |
| None | 384 | 0.14076246 | 0.17006200 |
| Associate's degree | 411 | 0.15065982 | 0.18201949 |
| GED | 118 | 0.04325513 | 0.05225864 |
| Bachelor's degree | 152 | 0.05571848 | 0.06731621 |
| NA | 470 | 0.17228739 | NA |
We can change the font size for tbl_summary()
- 1
-
Convert the
tbl_summary()output to agttable so we can customize it. Need to useas_gt()instead ofgt()becausetbl_summary()already creates a table. - 2
- Change the font size to 50 pixels. Be careful: font size is relative to your document. 50px is probably too large for your standard html’s.
| Characteristic | N = 2,7281 |
|---|---|
| Age (years) | 59 (9) |
| Sex | |
| Female | 1,568 (57%) |
| Male | 1,160 (43%) |
| Ever Had Diabetes | 735 (27%) |
| 1 Mean (SD); n (%) | |
Wrap-up
- We covered a few functions for summarizing data in R:
tabyl()from thejanitorpackage- For categorical data
- Can be used for one-way or two-way tables
tbl_summary()from thegtsummarypackage- For categorical and numeric data
- Good for publications!
- Can be customized to show different statistics, labels, and stratifications
Resources
- Tutorial on
tbl_summary()from thegtsummarypackage- Includes more options than covered here!
- More on the
gtpackage- Includes more options than covered here!