ggplot2
is an application of the grammar of graphics for R ggplot2
is an application of the grammar of graphics for R
A default dataset and set of mappings from variables to aesthetics
Images from John-Burn Murdoch's presentation: ggplot2 as a creativity engine
Easy enough to rapidly prototype graphics at the "speed of thought"
Images from John-Burn Murdoch's presentation: ggplot2 as a creativity engine
Easy enough to rapidly prototype graphics at the "speed of thought"
Powerful enough for final "publication"
Construct a wide variety of useful tables with a cohesive set of table parts. These include the table header, the stub, the column labels and spanner column labels, the table body and the table footer.
Construct a wide variety of useful tables with a cohesive set of table parts. These include the table header, the stub, the column labels and spanner column labels, the table body and the table footer.
Easy enough to rapidly prototype
Easy enough to rapidly prototype
Powerful enough for final "publication"
H/t to Jon Schwabish!
Adapted to gt
in my blogpost.
#TidyTuesday
datalibrary(tidyverse)library(gt)url_in <- 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-09-01/key_crop_yields.csv'raw_yields <- readr::read_csv(url_in)yield_data <- raw_yields %>% janitor::clean_names() %>% rename_with(~str_remove(., "_tonnes_per_hectare")) %>% select(entity:beans, -code) %>% pivot_longer(cols = wheat:beans, names_to = "crop", values_to = "yield") %>% rename(Country = entity)yield_data
## # A tibble: 78,450 x 4## Country year crop yield## <chr> <dbl> <chr> <dbl>## 1 Afghanistan 1961 wheat 1.02 ## 2 Afghanistan 1961 rice 1.52 ## 3 Afghanistan 1961 maize 1.4 ## 4 Afghanistan 1961 soybeans NA ## 5 Afghanistan 1961 potatoes 8.67 ## 6 Afghanistan 1961 beans NA ## 7 Afghanistan 1962 wheat 0.974## 8 Afghanistan 1962 rice 1.52 ## 9 Afghanistan 1962 maize 1.4 ## 10 Afghanistan 1962 soybeans NA ## # … with 78,440 more rows
#TidyTuesday
datacountry_sel <- c( "China", "India", "United States", "Indonesia", "Mexico", "Pakistan" )yield_data_wide <- raw_yields %>% janitor::clean_names() %>% rename_with( ~str_remove(., "_tonnes_per_hectare") ) %>% select(entity:beans, -code) %>% pivot_longer( cols = wheat:beans, names_to = "crop", values_to = "yield" ) %>% rename(Country = entity) %>% filter( crop %in% c("potatoes", "maize"), year %in% c(2014:2016), Country %in% country_sel ) %>% pivot_wider( names_from = year, values_from = yield )
#TidyTuesday
datayield_data_wide
## # A tibble: 12 x 5## Country crop `2014` `2015` `2016`## <chr> <chr> <dbl> <dbl> <dbl>## 1 China maize 5.81 5.89 5.97## 2 China potatoes 17.1 17.3 17.7 ## 3 India maize 2.61 2.60 2.62## 4 India potatoes 22.9 23.1 20.5 ## 5 Indonesia maize 4.95 5.18 5.31## 6 Indonesia potatoes 17.7 18.2 18.3 ## 7 Mexico maize 3.30 3.48 3.72## 8 Mexico potatoes 27.3 27.1 27.9 ## 9 Pakistan maize 4.32 4.42 4.55## 10 Pakistan potatoes 18.2 23.4 22.4 ## 11 United States maize 10.7 10.6 11.7 ## 12 United States potatoes 47.2 46.9 48.6
gt
tableyield_data_wide %>% gt()
gt
tableyield_data_wide %>% gt()
yield_data_wide %>% head() %>% # respects grouping from dplyr group_by(Country) %>% gt(rowname_col = "crop")
yield_data_wide %>% head() %>% # respects grouping from dplyr group_by(Country) %>% gt(rowname_col = "crop")
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" )
yield_data_wide %>% mutate(crop = str_to_title(crop)) %>% group_by(crop) %>% gt( rowname_col = "Country" ) %>% fmt_number( columns = 2:5, # reference cols by pos decimals = 2 # decrease decimal places ) %>% summary_rows( groups = TRUE, # reference cols by name columns = vars(`2014`, `2015`, `2016`), fns = list( # add summary stats avg = ~mean(.), sd = ~sd(.) ) )
yield_data_wide %>% mutate(crop = str_to_title(crop)) %>% group_by(crop) %>% gt( rowname_col = "Country" ) %>% fmt_number( columns = 2:5, # reference cols by pos decimals = 2 # decrease decimal places ) %>% summary_rows( groups = TRUE, # reference cols by name columns = vars(`2014`, `2015`, `2016`), fns = list( # add summary stats avg = ~mean(.), sd = ~sd(.) ) )
Table spanners can be added quickly with tab_spanner()
and again use either position (column number) or + vars(name)
.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_spanner( label = "Yield in Tonnes/Hectare", columns = 2:5 )
Table spanners can be added quickly with tab_spanner()
and again use either position (column number) or + vars(name)
.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_spanner( label = "Yield in Tonnes/Hectare", columns = 2:5 )
Footnotes can be added with tab_footnote()
. Note that this is our first use of the locations
argument. Locations is used with things like cells_column_labels()
or cells_body()
, cells_summary()
to offer very tight control of where to place certain changes.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_footnote( footnote = "Yield in Tonnes/Hectare", locations = cells_column_labels( columns = 1:3 ) )
Footnotes can be added with tab_footnote()
. Note that this is our first use of the locations
argument. Locations is used with things like cells_column_labels()
or cells_body()
, cells_summary()
to offer very tight control of where to place certain changes.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_footnote( footnote = "Yield in Tonnes/Hectare", locations = cells_column_labels( columns = 1:3 ) )
Footnotes can be added with tab_footnote()
. Note that this is our first use of the locations
argument. Locations is used with things like cells_column_labels()
or cells_body()
, cells_summary()
to offer very tight control of where to place certain changes.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_footnote( footnote = "Yield in Tonnes/Hectare", locations = cells_column_labels( columns = 1:3 # note ) ) %>% # Adding a `source_note()` tab_source_note( source_note = "Data: OurWorldInData" )
Footnotes can be added with tab_footnote()
. Note that this is our first use of the locations
argument. Locations is used with things like cells_column_labels()
or cells_body()
, cells_summary()
to offer very tight control of where to place certain changes.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_footnote( footnote = "Yield in Tonnes/Hectare", locations = cells_column_labels( columns = 1:3 # note ) ) %>% # Adding a `source_note()` tab_source_note( source_note = "Data: OurWorldInData" )
Adding a title or subtitle with tab_header()
and notice that I used md()
around the title and html()
around subtitle to adjust their appearance.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_header( title = md("**Crop Yields between 2014 and 2016**"), subtitle = html("<em>Countries limited to Asia</em>") )
Adding a title or subtitle with tab_header()
and notice that I used md()
around the title and html()
around subtitle to adjust their appearance.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_header( title = md("**Crop Yields between 2014 and 2016**"), subtitle = html("<em>Countries limited to Asia</em>") )
You can customize large chunks of the table appearance all at once via tab_options()
. The full reference to ALL the options you can customize are in the gt
packagedown site.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_header( title = "Crop Yields between 2014 and 2016", subtitle = "Countries limited to Asia" ) %>% tab_options( heading.subtitle.font.size = 12, heading.align = "left", table.border.top.color = "red", column_labels.border.bottom.color = "red", column_labels.border.bottom.width= px(3) )
You can customize large chunks of the table appearance all at once via tab_options()
. The full reference to ALL the options you can customize are in the gt
packagedown site.
yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_header( title = "Crop Yields between 2014 and 2016", subtitle = "Countries limited to Asia" ) %>% tab_options( heading.subtitle.font.size = 12, heading.align = "left", table.border.top.color = "red", column_labels.border.bottom.color = "red", column_labels.border.bottom.width= px(3) )
Because gt
is built up by a series of piped examples, you can also pass along additional changes/customization as a function almost like a ggplot2
theme!
my_theme <- function(data) { tab_options( data = data, heading.subtitle.font.size = 12, heading.align = "left", table.border.top.color = "red", column_labels.border.bottom.color = "red", column_labels.border.bottom.width= px(3) )}yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_header( title = "Crop Yields between 2014 and 2016", subtitle = "Countries limited to Asia" ) %>% my_theme()
Because gt
is built up by a series of piped examples, you can also pass along additional changes/customization as a function almost like a ggplot2
theme!
my_theme <- function(data) { tab_options( data = data, heading.subtitle.font.size = 12, heading.align = "left", table.border.top.color = "red", column_labels.border.bottom.color = "red", column_labels.border.bottom.width= px(3) )}yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% tab_header( title = "Crop Yields between 2014 and 2016", subtitle = "Countries limited to Asia" ) %>% my_theme()
tab_style()
yield_data_wide %>% head() %>% gt() %>% tab_style( style = list( cell_text(weight = "bold") ), locations = cells_column_labels(everything()) ) %>% tab_style( style = list( cell_fill(color = "black", alpha = 0.2), cell_borders( side = c("left", "right"), color = "black", weight = px(2) ) ), locations = cells_body( columns = vars(crop) ) ) %>% tab_style( style = list( cell_text(color = "red", style = "italic") ), locations = cells_body( columns = 3:5, rows = Country == "China" ) )
tab_style()
yield_data_wide %>% head() %>% gt() %>% tab_style( style = list( cell_text(weight = "bold") ), locations = cells_column_labels(everything()) ) %>% tab_style( style = list( cell_fill(color = "black", alpha = 0.2), cell_borders( side = c("left", "right"), color = "black", weight = px(2) ) ), locations = cells_body( columns = vars(crop) ) ) %>% tab_style( style = list( cell_text(color = "red", style = "italic") ), locations = cells_body( columns = 3:5, rows = Country == "China" ) )
my_pal <- scales::col_numeric( paletteer::paletteer_d( palette = "ggsci::red_material" ) %>% as.character(), domain = NULL )yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% data_color( columns = vars(`2014`, `2015`, `2016`), colors = my_pal )
my_pal <- scales::col_numeric( paletteer::paletteer_d( palette = "ggsci::red_material" ) %>% as.character(), domain = NULL )yield_data_wide %>% head() %>% gt( groupname_col = "crop", rowname_col = "Country" ) %>% data_color( columns = vars(`2014`, `2015`, `2016`), colors = my_pal )
Themes are really just functions with a lot of the gt
customizations baked in.
gt
objects, add styling/themes and then output the themed table gt_theme_espn <- function(data, ...){ data %>% opt_all_caps() %>% opt_table_font( font = list( google_font("Lato"), default_fonts() ) ) %>% opt_row_striping() %>% tab_options( row.striping.background_color = "#fafafa", table_body.hlines.color = "#f6f7f7", source_notes.font.size = 12, table.font.size = 16, table.width = px(700), heading.align = "left", heading.title.font.size = 24, table.border.top.color = "transparent", table.border.top.width = px(3), data_row.padding = px(7), ... ) }
gt_theme_538 <- function(data,...) { data %>% opt_all_caps() %>% opt_table_font(font = list(google_font("Chivo"),default_fonts())) %>% tab_style(style = cell_borders(sides = "bottom", color = "transparent", weight = px(2)), locations = cells_body(columns = TRUE,rows = nrow(data$`_data`))) %>% tab_options( column_labels.background.color = "white", table.border.top.width = px(3), table.border.top.color = "transparent", table.border.bottom.color = "transparent", table.border.bottom.width = px(3), column_labels.border.top.width = px(3), column_labels.border.top.color = "transparent", column_labels.border.bottom.width = px(3), column_labels.border.bottom.color = "black", data_row.padding = px(3), source_notes.font.size = 12, table.font.size = 16, heading.align = "left", ... ) }
Not going to show the code, because it really only applies to this table, but you can see it on my blog.
bar_chart <- function(label, height = "16px", fill = "#00bfc4", background = "white") { bar <- glue::glue("<div style='background:{fill};width:{label}%;height:{height};'></div>") chart <- glue::glue("<div style='flex-grow:1;margin-left:8px;background:{background};'>{bar}</div>") glue::glue("<div style='display:flex;align-items:left';>{chart}</div>") %>% gt::html()}head(mtcars) %>% mutate( mpg_val = mpg/max(mpg) * 100, mpg_plot = purrr::map(mpg_val, ~bar_chart(label = .x, fill = "#1696d2")), mpg_plot2 = purrr::map(mpg_val, ~bar_chart(label = .x, fill = "#fdbf11", background = "#d2d2d2")), ) %>% select(cyl, hp, disp, mpg, mpg_plot, mpg_plot2) %>% gt() %>% cols_align(align = "left", columns = vars(mpg_plot))
gt_plot <- function(table_data, column, plot_data, plot_fun, color = "#1696d2",...){ text_transform( table_data, # note the use of {{}} here - this is tidy eval # that allows you to indicate specific columns locations = cells_body(columns = vars({{column}})), fn = function(x){ plot <- purrr::map( plot_data, plot_fun, width = 300, height = 70, same_lim = TRUE, col = color, minmax = list(pch = 18, col = "#fdbf11"), ...) plot_svg <- purrr::map(plot, "svg_text") purrr::map(plot_svg, gt::html) } )}
mpg_list <- split(mtcars$mpg, mtcars$cyl)tibble(cyl = c(4,6,8), boxplot = "") %>% gt() %>% gt_plot( column = boxplot, # column to create plot in plot_data = mpg_list, # external data to reference plot_fun = kableExtra::spec_plot, # which plot fun ylim = range(mtcars$mpg), # range applied, color = "#1696d2", # change color of line cex = 5 # change size of points, )
library(kableExtra)mpg_rng <- range(mtcars$mpg)tibble(cyl = c(4, 6, 8), boxplot = "", mpg_hist = "", mpg_line1 = "", mpg_line2 = "", mpg_points1 = "", mpg_points2 = "", mpg_poly = "") %>% gt() %>% gt_plot(column = boxplot, plot_data = mpg_list, plot_fun = spec_boxplot, lim = mpg_rng) %>% gt_plot(column = mpg_hist, plot_data = mpg_list, plot_fun = spec_hist, lim = mpg_rng) %>% gt_plot(column = mpg_line1, plot_data = mpg_list, plot_fun = spec_plot, ylim = mpg_rng) %>% gt_plot(column = mpg_line2, plot_data = mpg_list, plot_fun = spec_plot) %>% gt_plot(column = mpg_points1, plot_data = mpg_list, plot_fun = spec_plot, type = "p", ylim = mpg_rng, cex = 4) %>% gt_plot(column = mpg_points2, plot_data = mpg_list, plot_fun = spec_plot, type = "p", cex = 4) %>% gt_plot(column = mpg_poly, plot_data = mpg_list, plot_fun = spec_plot, polymin = 5, ylim = mpg_rng)
library(kableExtra)mpg_rng <- range(mtcars$mpg)tibble(cyl = c(4, 6, 8), boxplot = "", mpg_hist = "", mpg_line1 = "", mpg_line2 = "", mpg_points1 = "", mpg_points2 = "", mpg_poly = "") %>% gt() %>% gt_plot(column = boxplot, plot_data = mpg_list, plot_fun = spec_boxplot, lim = mpg_rng) %>% gt_plot(column = mpg_hist, plot_data = mpg_list, plot_fun = spec_hist, lim = mpg_rng) %>% gt_plot(column = mpg_line1, plot_data = mpg_list, plot_fun = spec_plot, ylim = mpg_rng) %>% gt_plot(column = mpg_line2, plot_data = mpg_list, plot_fun = spec_plot) %>% gt_plot(column = mpg_points1, plot_data = mpg_list, plot_fun = spec_plot, type = "p", ylim = mpg_rng, cex = 4) %>% gt_plot(column = mpg_points2, plot_data = mpg_list, plot_fun = spec_plot, type = "p", cex = 4) %>% gt_plot(column = mpg_poly, plot_data = mpg_list, plot_fun = spec_plot, polymin = 5, ylim = mpg_rng)
dimnames <- list(start(nottem)[1]:end(nottem)[1], month.abb)temps <- matrix(nottem, ncol = 12, byrow = TRUE, dimnames = dimnames) %>% data.frame() %>% tibble::rownames_to_column() %>% head(10)temps %>% gt() %>% data_color( columns = vars(month.abb), colors = scales::col_numeric( c("#0a4c6a", "#73bfe2", "#cfe8f3", "#fff2cf", "#fdd870", "#fdbf11", "#ca5800"), domain = range(nottem)) )
icon_fun <- function(icon, fill, val){ fontawesome::fa(icon, fill = fill) %>% rep(., val) %>% gt::html()}head(mtcars) %>% select(mpg:drat) %>% mutate(cylinder = paste(cyl, "cyl"), .before = cyl) %>% gt() %>% text_transform( locations = cells_body(columns = vars(cyl), rows = cyl == 4), fn = function(x){icon_fun(icon = "truck-pickup", fill = "black", val = x)} ) %>% text_transform( locations = cells_body(columns = vars(cyl), rows = cyl == 6), fn = function(x){gt::html(rep(fontawesome::fa("truck", fill = "#fdbf11"), 6))} ) %>% text_transform( locations = cells_body(columns = vars(cyl), rows = cyl == 8), fn = function(x){icon_fun(icon = "truck-monster", fill = "#1696d2", val = x)} ) %>% cols_align(align = "left", columns = vars(cyl)) %>% cols_width(vars(cyl)~px(170)) %>% tab_source_note( source_note = html("<img height='30px' src='https://nchousing.org/wp-content/uploads/2017/04/logo.png' style='float: right;'></img>") )
gt
reactable
) reactable
) Avoid the "CVS receipt"!
LaTeX support in gt
is still in progress, so while it can "just work" in PDF, HTML content is still where it shines
webshot
, see gtsave("TAB-NAME.pdf")
kableExtra
or kable
for now gt
tables, but I still think that flextable
wins the "Office" wars pagedown
and pagedreport
are very powerful for HTML -> PDF7 Different Table Guides on TheMockUp.blog
ALL code for this specific presentation in written form on my github
{gt}
documentation
The {gt}
Cookbook and the Advanced Cookbook
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 |