Pipe-oriented
Beautiful tables easy
Spanner heads/grouping used to be a total pain - not so anymore
Renders to HTML/PDF without even thinking about it
Probably my favorite package for creating static tables, although kableExtra is great too.
My experience is that fewer people are generally familiar with gt, which is why I cover it here.
Getting your data in the format you want a table in
Utilize your pivot_*
skills regularly
library(fivethirtyeight)flying
## # A tibble: 1,040 x 27## respondent_id gender age height children_under_18 household_income ## <dbl> <chr> <ord> <ord> <lgl> <ord> ## 1 3436139758 <NA> <NA> <NA> NA <NA> ## 2 3434278696 Male 30-44 "6'3\"" TRUE <NA> ## 3 3434275578 Male 30-44 "5'8\"" FALSE $100,000 - $149,999## 4 3434268208 Male 30-44 "5'11\"" FALSE $0 - $24,999 ## 5 3434250245 Male 30-44 "5'7\"" FALSE $50,000 - $99,999 ## 6 3434245875 Male 30-44 "5'9\"" TRUE $25,000 - $49,999 ## # … with 1,034 more rows, and 21 more variables: education <ord>,## # location <chr>, frequency <ord>, recline_frequency <ord>,## # recline_obligation <lgl>, recline_rude <ord>, recline_eliminate <lgl>,## # switch_seats_friends <ord>, switch_seats_family <ord>,## # wake_up_bathroom <ord>, wake_up_walk <ord>, baby <ord>,## # unruly_child <ord>, two_arm_rests <chr>, middle_arm_rest <chr>,## # shade <chr>, unsold_seat <ord>, talk_stranger <ord>, get_up <ord>,## # electronics <lgl>, smoked <lgl>
flying %>% count(gender, age, recline_frequency)
## # A tibble: 53 x 4## gender age recline_frequency n## <chr> <ord> <ord> <int>## 1 Female 18-29 Never 24## 2 Female 18-29 Once in a while 36## 3 Female 18-29 About half the time 10## 4 Female 18-29 Usually 13## 5 Female 18-29 Always 10## 6 Female 18-29 <NA> 19## # … with 47 more rows
smry <- flying %>% count(gender, age, recline_frequency) %>% drop_na(age,recline_frequency) %>% pivot_wider(names_from = "age", values_from = "n") smry
## # A tibble: 10 x 6## gender recline_frequency `18-29` `30-44` `45-60` `> 60`## <chr> <ord> <int> <int> <int> <int>## 1 Female Never 24 21 19 23## 2 Female Once in a while 36 25 30 36## 3 Female About half the time 10 22 18 17## 4 Female Usually 13 22 26 28## 5 Female Always 10 21 29 12## 6 Male Never 24 17 20 18## # … with 4 more rows
gender | recline_frequency | 18-29 | 30-44 | 45-60 | > 60 |
---|---|---|---|---|---|
Female | Never | 24 | 21 | 19 | 23 |
Female | Once in a while | 36 | 25 | 30 | 36 |
Female | About half the time | 10 | 22 | 18 | 17 |
Female | Usually | 13 | 22 | 26 | 28 |
Female | Always | 10 | 21 | 29 | 12 |
Male | Never | 24 | 17 | 20 | 18 |
Male | Once in a while | 19 | 39 | 40 | 29 |
Male | About half the time | 11 | 11 | 16 | 11 |
Male | Usually | 14 | 30 | 15 | 27 |
Male | Always | 11 | 14 | 21 | 14 |
recline_frequency | 18-29 | 30-44 | 45-60 | > 60 |
---|---|---|---|---|
Female | ||||
Never | 24 | 21 | 19 | 23 |
Once in a while | 36 | 25 | 30 | 36 |
About half the time | 10 | 22 | 18 | 17 |
Usually | 13 | 22 | 26 | 28 |
Always | 10 | 21 | 29 | 12 |
Male | ||||
Never | 24 | 17 | 20 | 18 |
Once in a while | 19 | 39 | 40 | 29 |
About half the time | 11 | 11 | 16 | 11 |
Usually | 14 | 30 | 15 | 27 |
Always | 11 | 14 | 21 | 14 |
This is an example of a table that looks better with the default CSS
recline_frequency | Age Range | |||
---|---|---|---|---|
18-29 | 30-44 | 45-60 | > 60 | |
Female | ||||
Never | 24 | 21 | 19 | 23 |
Once in a while | 36 | 25 | 30 | 36 |
About half the time | 10 | 22 | 18 | 17 |
Usually | 13 | 22 | 26 | 28 |
Always | 10 | 21 | 29 | 12 |
Male | ||||
Never | 24 | 17 | 20 | 18 |
Once in a while | 19 | 39 | 40 | 29 |
About half the time | 11 | 11 | 16 | 11 |
Usually | 14 | 30 | 15 | 27 |
Always | 11 | 14 | 21 | 14 |
smry %>% group_by(gender) %>% gt() %>% tab_spanner( label = "Age Range", columns = vars(`18-29`, `30-44`, `45-60`, `> 60`) ) %>% cols_label(recline_frequency = "Recline") %>% cols_align(align = "left", columns = vars(recline_frequency))
They are already left-aligned because of the CSS, but that's not typical
smry %>% group_by(gender) %>% gt() %>% tab_spanner( label = "Age Range", columns = vars(`18-29`, `30-44`, `45-60`, `> 60`) ) %>% cols_label(recline_frequency = "Recline") %>% cols_align(align = "left", columns = vars(recline_frequency)) tab_header( title = "Airline Passengers", subtitle = "Leg space is limited, what do you do?" )
Airline Passengers | ||||
---|---|---|---|---|
Leg space is limited, what do you do? | ||||
Recline | Age Range | |||
18-29 | 30-44 | 45-60 | > 60 | |
Female | ||||
Never | 24 | 21 | 19 | 23 |
Once in a while | 36 | 25 | 30 | 36 |
About half the time | 10 | 22 | 18 | 17 |
Usually | 13 | 22 | 26 | 28 |
Always | 10 | 21 | 29 | 12 |
Male | ||||
Never | 24 | 17 | 20 | 18 |
Once in a while | 19 | 39 | 40 | 29 |
About half the time | 11 | 11 | 16 | 11 |
Usually | 14 | 30 | 15 | 27 |
Always | 11 | 14 | 21 | 14 |
smry %>% mutate(across(c(`18-29`, `30-44`, `45-60`, `> 60`), ~.x/100)) %>% group_by(gender) %>% gt() %>% tab_spanner( label = "Age Range", columns = vars(`18-29`, `30-44`, `45-60`, `> 60`) ) %>% fmt_percent( vars(`18-29`, `30-44`, `45-60`, `> 60`), decimals = 0 ) %>% cols_label(recline_frequency = "Recline") %>% cols_align(align = "left", columns = vars(recline_frequency)) %>% tab_header( title = "Airline Passengers", subtitle = "Leg space is limited, what do you do?" )
Airline Passengers | ||||
---|---|---|---|---|
Leg space is limited, what do you do? | ||||
Recline | Age Range | |||
18-29 | 30-44 | 45-60 | > 60 | |
Female | ||||
Never | 24% | 21% | 19% | 23% |
Once in a while | 36% | 25% | 30% | 36% |
About half the time | 10% | 22% | 18% | 17% |
Usually | 13% | 22% | 26% | 28% |
Always | 10% | 21% | 29% | 12% |
Male | ||||
Never | 24% | 17% | 20% | 18% |
Once in a while | 19% | 39% | 40% | 29% |
About half the time | 11% | 11% | 16% | 11% |
Usually | 14% | 30% | 15% | 27% |
Always | 11% | 14% | 21% | 14% |
smry %>% mutate(across(c(`18-29`, `30-44`, `45-60`, `> 60`), ~.x/100)) %>% group_by(gender) %>% gt() %>% tab_spanner( label = "Age Range", columns = vars(`18-29`, `30-44`, `45-60`, `> 60`) ) %>% fmt_percent( vars(`18-29`, `30-44`, `45-60`, `> 60`), decimals = 0 ) %>% cols_label(recline_frequency = "Recline") %>% cols_align(align = "left", columns = vars(recline_frequency)) %>% tab_header( title = "Airline Passengers", subtitle = "Leg space is limited, what do you do?" ) %>% tab_source_note( source_note = md("Data from [fivethirtyeight](https://fivethirtyeight.com/features/airplane-etiquette-recline-seat/)") )
Airline Passengers | ||||
---|---|---|---|---|
Leg space is limited, what do you do? | ||||
Recline | Age Range | |||
18-29 | 30-44 | 45-60 | > 60 | |
Female | ||||
Never | 24% | 21% | 19% | 23% |
Once in a while | 36% | 25% | 30% | 36% |
About half the time | 10% | 22% | 18% | 17% |
Usually | 13% | 22% | 26% | 28% |
Always | 10% | 21% | 29% | 12% |
Male | ||||
Never | 24% | 17% | 20% | 18% |
Once in a while | 19% | 39% | 40% | 29% |
About half the time | 11% | 11% | 16% | 11% |
Usually | 14% | 30% | 15% | 27% |
Always | 11% | 14% | 21% | 14% |
Data from fivethirtyeight |
Airline Passengers | ||||
---|---|---|---|---|
Leg space is limited, what do you do? | ||||
Recline | Age Range | |||
18-29 | 30-44 | 45-60 | > 60 | |
Female | ||||
Never | 24% | 21% | 19% | 23% |
Once in a while | 36% | 25% | 30% | 36% |
About half the time | 10% | 22% | 18% | 17% |
Usually | 13% | 22% | 26% | 28% |
Always | 10% | 21% | 29% | 12% |
Male | ||||
Never | 24% | 17% | 20% | 18% |
Once in a while | 19% | 39% | 40% | 29% |
About half the time | 11% | 11% | 16% | 11% |
Usually | 14% | 30% | 15% | 27% |
Always | 11% | 14% | 21% | 14% |
Data from fivethirtyeight |
Thomas Mock does a lot of great work with tables and often has tutorials showing your how to go further (e.g., see here and here and here).
Make sure to specify results = "asis"
in your chunk options.
library(knitr)library(kableExtra)dt <- mtcars[1:5, 1:6]kable(dt) %>% kable_styling("striped") %>% column_spec(5:7, bold = TRUE)
mpg | cyl | disp | hp | drat | wt | |
---|---|---|---|---|---|---|
Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.62 |
Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.88 |
Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.32 |
Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.21 |
Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.44 |
kable(dt) %>% kable_styling("striped") %>% column_spec(5:7, bold = TRUE) %>% row_spec(c(2, 4), bold = TRUE, color = "#EFF3F7", background = "#71B0DE")
mpg | cyl | disp | hp | drat | wt | |
---|---|---|---|---|---|---|
Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.62 |
Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.88 |
Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.32 |
Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.21 |
Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.44 |
kable(dt) %>% kable_styling("striped", full_width = FALSE) %>% pack_rows( "Group 1", 1, 3, label_row_css = "background-color: #666; color: #fff;" ) %>% pack_rows( "Group 2", 4, 5, label_row_css = "background-color: #666; color: #fff;")
mpg | cyl | disp | hp | drat | wt | |
---|---|---|---|---|---|---|
Group 1 | ||||||
Mazda RX4 | 21.0 | 6 | 160 | 110 | 3.90 | 2.62 |
Mazda RX4 Wag | 21.0 | 6 | 160 | 110 | 3.90 | 2.88 |
Datsun 710 | 22.8 | 4 | 108 | 93 | 3.85 | 2.32 |
Group 2 | ||||||
Hornet 4 Drive | 21.4 | 6 | 258 | 110 | 3.08 | 3.21 |
Hornet Sportabout | 18.7 | 8 | 360 | 175 | 3.15 | 3.44 |
library(palmerpenguins)library(reactable)reactable(penguins)
reactable(penguins, filterable = TRUE)
reactable(penguins, searchable = TRUE)
library(sparkline)table_data <- penguins %>% group_by(species) %>% summarize(bill_length = list(bill_length_mm)) %>% mutate(boxplot = NA, sparkline = NA)table_data
## # A tibble: 3 x 4## species bill_length boxplot sparkline## * <fct> <list> <lgl> <lgl> ## 1 Adelie <dbl [152]> NA NA ## 2 Chinstrap <dbl [68]> NA NA ## 3 Gentoo <dbl [124]> NA NA
table_data %>% reactable( columns = list( bill_length = colDef(cell = function(value) { sparkline(value, type = "bar") }), boxplot = colDef(cell = function(value, index) { sparkline(table_data$bill_length[[index]], type = "box") }), sparkline = colDef(cell = function(value, index) { sparkline(table_data$bill_length[[index]]) }) ) )
Idea of today is not to teach you everything, but to give you an idea of what's possible. Check out the documentation for more information.
Use different fonts to distinguish things
Always choose a sans-serif font for code
Explore and try - it makes a big impact on the overall look/feel (bigger than you may expect if you haven't played with fonts much before)
Try not to get sucked into too deep of a rabbit hole
Brand new pacakge - just learning about it myself.
Alternative device to Cairo, png, etc.
See the announcement here
After install, be sure to set Global Options > General > Graphics to AGG
Use with RMarkdown with knitr::opts_chunk$set(dev = "ragg_png")
Will automatically detect fonts you have installed on your computer
Open source, designed for the web
Good place to explore fonts
Can be incorporated via the {showtext}
package!
devtools::install_github("yixuan/showtext")library(showtext)font_add_google('Monsieur La Doulaise', "mld")font_add_google('Special Elite', "se")showtext_auto()quartz()ggplot(mtcars, aes(disp, mpg)) + geom_point() + labs(title = "An amazing title", subtitle = "with the world's most boring dataset") + theme(plot.subtitle = element_text(size = 18, family = "se"), plot.title = element_text(size = 22, family = "mld"), axis.title = element_text(size = 18, family = "mld"), axis.text.x = element_text(size = 12, family = "se"), axis.text.y = element_text(size = 12, family = "se"))
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |