Hacer cuadros/tablas para mostrar resultados es una parte importante (y time consuming) de cualquier informe o investigación. En este tutorial explicaré como presentar nuestros resultados en tablas en documentos (ya sean estos .html, .pdf etc..) creados a partir de archivos .Rmd
.
Veremos que presentar los resultados (previamente almacenados en un df) como un cuadro en el documento final es muy sencillo, sólo tenemos que llamar a la función kable()
, pero si queremos tener más flexibilidad y opciones para mejorar nuestras tablas tendremos que usar otros paquetes como kableextra
, formatable
, flextable
, DT
o más recientemente gt
y reactable
En este hilo de Twitter se habló sobre cuales eran los mejores paquetes R para hacer tablas. En este post, David Keyes hace un muy buen tutorial sobre los paquetes disponibles para hacer tablas en R.
Al igual que con los gráficos, para hacer buenas tablas 1 hace falta tener en cuenta algunos aspectos. Este post proporciona 10 reglas o consejos para generar tablas efectivas. En este otro post, Thomas Mock implementa esas 10 reglas con el paquete gt
.
En el siguiente apartado utilizaremos unos datos de ejemplo para obtener unos resultados que queremos mostrar como tablas en nuestro informe. Esto nos servirá para practicar un poco más con dplyr
y aprender a crear tablas desde documentos .Rmd, ya que una vez tengamos los resultados que queremos mostrar, presentaremos distintas posibilidades para mostrarlos en tablas/cuadros good-looking.
En esta sección utilizaremos lo que hemos aprendido de dplyr para obtener los resultados que queremos mostrar en los cuadros. Con dplyr calcularemos los estadísticos que queremos mostrar almacenándolos en un dataframe/tibble. Una vez tengamos los resultados en un dataframe nos quedará la tarea de presentarlos como cuadros en el documento final. En un primer momento (esta sección) utilizaremos la función knitr::kable()
para hacer tablas y en los siguientes apartados presentaremos paquetes especialmente diseñados para hacer tablas.
Ilustraremos la creación de tablas con datos de la segunda ronda de PIACC (Programa para la Evaluación Internacional de las Competencias de los adultos de la OCDE. Información detallada sobre el programa PIAAC puede encontrarse en su pagina web http://www.oecd.org/skills/piaac/. Los datos se obtuvieron a partir del paquete RPIAAC creado Por Jose C. Pernias, para finalmente seleccionar 10 variables de 4 países: ESP, FRA, GBR, ITA. No recuerdo el año de los datos!!
Cargamos los datos que vamos a utilizar:
my_url <- "https://raw.githubusercontent.com/perezp44/iris_data/master/data/PIAAC_data_small.csv"
df <- read_csv(my_url)
Miramos un poco los datos:
#- remotes::install_github("perezp44/pjpv2020.01")
df_aa <- pjpv2020.01::pjp_f_estadisticos_basicos(df) #- estadísticos básicos del df
df_bb <- pjpv2020.01::pjp_f_unique_values(df) #- valores únicos de cada variable de df
Para iniciar nuestra andadura con las tablas, intentaremos mostrar en tablas algunos resultados. Por ejemplo:
df_tt_1 <- df %>%
count(Country)
df_tt_1 <- df %>%
group_by(Country) %>%
summarise(NN = n())
knitr::kable(df_tt_1)
Country | NN |
---|---|
ESP | 1991 |
FRA | 3346 |
GBR | 1075 |
ITA | 1610 |
df_tt_2 <- df %>%
group_by(Country) %>%
summarise(NN = n(), percent = n()/nrow(.) )
df_tt_2 <- df %>%
group_by(Country) %>%
summarise (NN = n()) %>%
mutate(percent = NN / sum(NN))
knitr::kable(df_tt_2)
Country | NN | percent |
---|---|---|
ESP | 1991 | 0.2481925 |
FRA | 3346 | 0.4171030 |
GBR | 1075 | 0.1340065 |
ITA | 1610 | 0.2006981 |
df_tt_3 <- df %>%
count(Country, Gender) %>%
group_by(Country) %>%
mutate(percent = n / sum(n)) %>%
select(-n) %>%
pivot_wider(names_from = Gender,
values_from = percent) %>%
ungroup()
knitr::kable(df_tt_3)
Country | Female | Male |
---|---|---|
ESP | 0.4841788 | 0.5158212 |
FRA | 0.5065750 | 0.4934250 |
GBR | 0.6893023 | 0.3106977 |
ITA | 0.4850932 | 0.5149068 |
df_tt_4 <- df %>%
count(Education, Gender) %>%
group_by(Education) %>%
mutate(percent = n / sum(n)) %>%
select(-n) %>%
pivot_wider(names_from = Education,
values_from = percent) %>%
ungroup()
knitr::kable(df_tt_4)
Gender | Primary | Secondary | Tertiary | Upper_second | NA |
---|---|---|---|---|---|
Female | 0.4377224 | 0.4804831 | 0.6159875 | 0.579646 | 0.8 |
Male | 0.5622776 | 0.5195169 | 0.3840125 | 0.420354 | 0.2 |
df_tt_5 <- df %>%
group_by(Country) %>%
summarise(W_hora_medio = mean(Wage_hour , na.rm = TRUE),
W_mes_medio = mean(Wage_month, na.rm = TRUE)) %>% ungroup()
knitr::kable(df_tt_5)
Country | W_hora_medio | W_mes_medio |
---|---|---|
ESP | 9.386217 | 1437.340 |
FRA | 12.850094 | 1992.606 |
GBR | 10.624071 | 1507.472 |
ITA | 11.746581 | 1808.500 |
df_tt_6 <- df %>%
group_by(Country) %>%
summarise(W_medio = mean(Wage_month, na.rm = TRUE) ,
W_minimo = min(Wage_month, na.rm = TRUE) ,
W_maximo = max(Wage_month, na.rm = TRUE) ,
W_sd = sd(Wage_month, na.rm = TRUE) ) %>%
ungroup()
knitr::kable(df_tt_6)
Country | W_medio | W_minimo | W_maximo | W_sd |
---|---|---|---|---|
ESP | 1437.340 | 110 | 3800 | 702.6642 |
FRA | 1992.606 | 115 | 5850 | 906.5507 |
GBR | 1507.472 | 116 | 10000 | 1003.7897 |
ITA | 1808.500 | 150 | 5000 | 819.6863 |
df_tt_7 <- df %>%
filter(Country == "ESP") %>%
group_by(Gender) %>%
summarise(W_hora_medio = mean(Wage_hour, na.rm = TRUE),
W_mes_medio = mean(Wage_month, na.rm = TRUE) ) %>%
ungroup()
knitr::kable(df_tt_7)
Gender | W_hora_medio | W_mes_medio |
---|---|---|
Female | 8.789178 | 1245.076 |
Male | 9.946525 | 1617.810 |
df_tt_8 <- df %>%
group_by(Country, Gender) %>%
summarise(W_hora_medio = mean(Wage_hour, na.rm = TRUE),
W_mes_medio = mean(Wage_month, na.rm = TRUE) ) %>%
ungroup()
knitr::kable(df_tt_8)
Country | Gender | W_hora_medio | W_mes_medio |
---|---|---|---|
ESP | Female | 8.789178 | 1245.076 |
ESP | Male | 9.946525 | 1617.810 |
FRA | Female | 12.443723 | 1802.254 |
FRA | Male | 13.266840 | 2188.032 |
GBR | Female | 10.609256 | 1377.953 |
GBR | Male | 10.656991 | 1794.817 |
ITA | Female | 11.584393 | 1629.828 |
ITA | Male | 11.898988 | 1976.826 |
df_tt_9 <- df %>%
group_by(Country, Gender) %>%
summarise(W_mes_medio = mean(Wage_month, na.rm = TRUE)) %>%
ungroup() %>%
pivot_wider(names_from = Gender, values_from = W_mes_medio) %>%
mutate(dif_W = Male-Female, dif_percent_W = dif_W/Female)
knitr::kable(df_tt_9)
Country | Female | Male | dif_W | dif_percent_W |
---|---|---|---|---|
ESP | 1245.076 | 1617.810 | 372.7344 | 0.2993669 |
FRA | 1802.254 | 2188.032 | 385.7784 | 0.2140533 |
GBR | 1377.953 | 1794.817 | 416.8640 | 0.3025240 |
ITA | 1629.828 | 1976.826 | 346.9983 | 0.2129048 |
df_tt_10 <- df %>%
group_by(Country, Education) %>%
summarise(Numeracy_media = mean(Numeracy_score, na.rm = TRUE)) %>%
ungroup()
knitr::kable(df_tt_10)
Country | Education | Numeracy_media |
---|---|---|
ESP | Primary | 213.0135 |
ESP | Secondary | 245.1109 |
ESP | Tertiary | 282.1070 |
ESP | Upper_second | 265.7699 |
FRA | Primary | 184.9306 |
FRA | Secondary | 246.6609 |
FRA | Tertiary | 304.0075 |
FRA | Upper_second | 289.0088 |
FRA | NA | 197.6502 |
GBR | Primary | 207.3143 |
GBR | Secondary | 252.9237 |
GBR | Tertiary | 286.1454 |
GBR | Upper_second | 263.2052 |
GBR | NA | 213.1912 |
ITA | Primary | 200.7297 |
ITA | Secondary | 257.5339 |
ITA | Tertiary | 279.7166 |
ITA | Upper_second | 271.6400 |
df_tt_11 <- df_tt_10 %>%
pivot_wider(names_from = Education,
values_from = Numeracy_media)
knitr::kable(df_tt_11)
Country | Primary | Secondary | Tertiary | Upper_second | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 282.1070 | 265.7699 | NA |
FRA | 184.9306 | 246.6609 | 304.0075 | 289.0088 | 197.6502 |
GBR | 207.3143 | 252.9237 | 286.1454 | 263.2052 | 213.1912 |
ITA | 200.7297 | 257.5339 | 279.7166 | 271.6400 | NA |
Bien, no está mal, pero quiero ordenar la tabla de menor a mayor nivelo educativo
Resulta que en el cuadro, las categorías de niveles de estudio se han ordenado alfabéticamente pero quiero ordenarla de menor a mayor nivel de estudios. Se puede hacer de otras maneras, pero lo haremos usando factores.
La variable Education tiene 4 categorías: Primary, Secondary, Upper-second y Tertiary; ADEMÁS estas categorías están/son fijas, no cambian. En estos casos es mejor tratar esas variables como factores; PERO si miramos la clase de la variable “Education” con class(df$Education)
veremos que es character, así que vamos a convertir la variable “Education” a factor.
Podríamos usar la función as.factor()
, pero una vez más usaremos pkgs del tidyverse; en este caso el pkg “forcats” y la función as_factor()
df <- df %>%
mutate(Education = forcats::as_factor(Education))
levels(df$Education)
#> [1] "Primary" "Tertiary" "Secondary" "Upper_second"
Como vemos, la variable Education
, que ahora es un factor, tiene cuatro categorías o “levels”
Resulta que el nombre de las 4 categorías está en inglés y lo queremos en castellano. Lo haremos con forcats::fct_recode()
. Para saber un poco más sobre como manipular factores debes ir aquí
df <- df %>%
mutate(Education = forcats::fct_recode(Education,
"Primaria" = "Primary",
"Secundaria" = "Secondary",
"Secundaria_post" = "Upper_second",
"Terciaria" = "Tertiary" ))
df %>% count(Education)
Education | n |
---|---|
Primaria | 562 |
Terciaria | 1914 |
Secundaria | 4637 |
Secundaria_post | 904 |
NA | 5 |
Como volvemos a ver, la variable “Education”, que ahora es un factor tiene 4 “levels” pero están ordenados de forma que no nos conviene, queremos cambiar el orden de los factores con forcast::fct_relevel()
para que se muestren los cuatro grupos educativos de menor a mayor nivel.
df <- df %>%
mutate(Education = forcats::fct_relevel(Education,
"Primaria", "Secundaria", "Secundaria_post"))
df %>% count(Education)
Education | n |
---|---|
Primaria | 562 |
Secundaria | 4637 |
Secundaria_post | 904 |
Terciaria | 1914 |
NA | 5 |
Como se puede ver en el cuadro de arriba, ya hemos cambiado el orden de los “levels” para que se muestren de menor a mayor nivel educativo, así que volvamos a rehacer el cuadro con el Numeracy Score por países:
df_tt_12 <- df %>%
group_by(Country, Education) %>%
summarise(Numeracy_media = mean(Numeracy_score, na.rm = TRUE)) %>%
ungroup() %>%
pivot_wider(names_from = Education,
values_from = Numeracy_media)
knitr::kable(df_tt_12)
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 265.7699 | 282.1070 | NA |
FRA | 184.9306 | 246.6609 | 289.0088 | 304.0075 | 197.6502 |
GBR | 207.3143 | 252.9237 | 263.2052 | 286.1454 | 213.1912 |
ITA | 200.7297 | 257.5339 | 271.6400 | 279.7166 | NA |
Si los resultados que queremos mostrar son cosas básicas como porcentajes, frecuencias etc… obviamente podemos calcularlos nosotros con dplyr
, pero para estos casos el paquete janitor
, concretamente la función janitor::tabyl()
nos facilita mucho la tarea2. Para ver todas las posibilidades que tiene el paquete janitor
puedes ir a su página web aquí.
1 y 2. Cuadro con el número de observaciones de cada país
df_tt_1_j <- df %>% janitor::tabyl(Country)
knitr::kable(df_tt_1_j)
Country | n | percent |
---|---|---|
ESP | 1991 | 0.2481925 |
FRA | 3346 | 0.4171030 |
GBR | 1075 | 0.1340065 |
ITA | 1610 | 0.2006981 |
df_tt_3_j <- df %>% janitor::tabyl(Country, Gender)
df_tt_3_j <- df %>% janitor::tabyl(Country, Gender) %>%
janitor::adorn_percentages(denominator = "row")
df_tt_3_j
Country | Female | Male |
---|---|---|
ESP | 0.4841788 | 0.5158212 |
FRA | 0.5065750 | 0.4934250 |
GBR | 0.6893023 | 0.3106977 |
ITA | 0.4850932 | 0.5149068 |
df_tt_4_j <- df %>% janitor::tabyl(Gender, Education) %>%
janitor:: adorn_percentages(denominator = "col")
df_tt_4_j
Gender | Primaria | Secundaria | Secundaria_post | Terciaria | NA_ |
---|---|---|---|---|---|
Female | 0.4377224 | 0.4804831 | 0.579646 | 0.6159875 | 0.8 |
Male | 0.5622776 | 0.5195169 | 0.420354 | 0.3840125 | 0.2 |
Para ver todas las posibilidades que tiene el paquete janitor
puedes ir a su página web aquí
Hay otros paquetes con funcionalidades parecidas a janitor
. Por ejemplo, el paquete freqtables
o el paquete sjmisc
.
knitr::kable()
Bueno, ya sabemos, aunque en realidad lo vimos al presentar dplyr
y tidyr
, como ir generando un dataframe con los estadísticos descriptivos que nos interesa mostrar. En realidad esos df ya son tablas de datos que puedo exportar a Excel para trabajarlas a nuestro gusto; pero en el curso hacemos todo desde R.
Ya habéis visto que con la función knitr::kable()
puedo mostrarlas en mi documento final sólo tengo que ejecutar lo siguiente: knitr::kable(my_df)
.
En los próximos apartados mostraremos algunos paquetes específicos para formatear los cuadros y hacerlos BONITOS, pero también conviene saber que kable() también tiene opciones para personalizar un poco nuestras tablas. Por ejemplo:
knitr::kable(df_tt_12,
align = "c",
caption = "Numeracy Score por país",
digits = 2,
format.args = list(decimal.mark = ",", big.mark = "."))
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213,01 | 245,11 | 265,77 | 282,11 | NA |
FRA | 184,93 | 246,66 | 289,01 | 304,01 | 197,65 |
GBR | 207,31 | 252,92 | 263,21 | 286,15 | 213,19 |
ITA | 200,73 | 257,53 | 271,64 | 279,72 | NA |
kableExtra
pkgkableExtra
es un package de que permite mejorar las tablas creadas con knitr::kable()
.
La descripción de kableExtra
en CRAN es:
Build complex HTML or ‘LaTeX’ tables using ‘kable()’ from ‘knitr’ and the piping syntax from ‘magrittr’. Function ‘kable()’ is a light weight table generator coming from ‘knitr’. This package simplifies the way to manipulate the HTML or ‘LaTeX’ codes generated by ‘kable()’ and allows users to construct complex tables and customize styles using a readable syntax.
Aquí tienes la vignette para elaborar tablas para documentos html.
Para mejorar las tablas usaremos la función kableExtra::kable_styling()
:
knitr::kable(df_tt_12) %>%
kableExtra::kable_styling(bootstrap_options = c("striped", "hover"))
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 265.7699 | 282.1070 | NA |
FRA | 184.9306 | 246.6609 | 289.0088 | 304.0075 | 197.6502 |
GBR | 207.3143 | 252.9237 | 263.2052 | 286.1454 | 213.1912 |
ITA | 200.7297 | 257.5339 | 271.6400 | 279.7166 | NA |
Se le puede poner un scroll:
df_tt_12 %>%
knitr::kable() %>%
kableExtra::kable_styling(font_size = 11) %>%
kableExtra::scroll_box(width = "50%", height = "60%")
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 265.7699 | 282.1070 | NA |
FRA | 184.9306 | 246.6609 | 289.0088 | 304.0075 | 197.6502 |
GBR | 207.3143 | 252.9237 | 263.2052 | 286.1454 | 213.1912 |
ITA | 200.7297 | 257.5339 | 271.6400 | 279.7166 | NA |
Se puede incluso poner las tablas junto con el texto.
knitr::kable(df_tt_12) %>%
kableExtra::kable_styling(bootstrap_options = "striped",
full_width = F,
position = "float_right")
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 265.7699 | 282.1070 | NA |
FRA | 184.9306 | 246.6609 | 289.0088 | 304.0075 | 197.6502 |
GBR | 207.3143 | 252.9237 | 263.2052 | 286.1454 | 213.1912 |
ITA | 200.7297 | 257.5339 | 271.6400 | 279.7166 | NA |
Para ello tienes que usar la opción position = "float_right
dentro de kableExtra::kable_styling()
.
Si tenemos una tabla muy larga, podemos fijar el encabezamiento de la tabla con fixed_thead
knitr::kable(df_tt_12) %>%
kableExtra::kable_styling(fixed_thead = list(enabled = T,
background = "lightblue"))
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 265.7699 | 282.1070 | NA |
FRA | 184.9306 | 246.6609 | 289.0088 | 304.0075 | 197.6502 |
GBR | 207.3143 | 252.9237 | 263.2052 | 286.1454 | 213.1912 |
ITA | 200.7297 | 257.5339 | 271.6400 | 279.7166 | NA |
Se pueden formatear columnas por separado con kableExtra::column_spec()
, o las filas con kableExtra::row_spec()
. La tabla no nos saldrá muy chula pero nos sirve para ver las posibilidades de kable_styling()
.
library(kableExtra)
knitr::kable(df_tt_12) %>%
kableExtra::kable_styling(full_width = F) %>%
column_spec(1, bold = T, border_right = T) %>%
column_spec(3, width = "20em", background = "yellow") %>%
row_spec(3:4, bold = T, color = "white", background = "#D7261E") %>%
row_spec(0, angle = 10)
Country | Primaria | Secundaria | Secundaria_post | Terciaria | NA |
---|---|---|---|---|---|
ESP | 213.0135 | 245.1109 | 265.7699 | 282.1070 | NA |
FRA | 184.9306 | 246.6609 | 289.0088 | 304.0075 | 197.6502 |
GBR | 207.3143 | 252.9237 | 263.2052 | 286.1454 | 213.1912 |
ITA | 200.7297 | 257.5339 | 271.6400 | 279.7166 | NA |
Puedes ver las posibilidades de este pkg en su vignette. Fijate que tabla más chula hay allí:
mtcars[1:8, 1:8] %>%
kbl() %>%
kable_paper(full_width = F) %>%
column_spec(2, color = spec_color(mtcars$mpg[1:8]),
link = "https://haozhu233.github.io/kableExtra/") %>%
column_spec(6, color = "white",
background = spec_color(mtcars$drat[1:8], end = 0.7),
popover = paste("am:", mtcars$am[1:8]))
mpg | cyl | disp | hp | drat | wt | qsec | vs | |
---|---|---|---|---|---|---|---|---|
Mazda RX4 | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.620 | 16.46 | 0 |
Mazda RX4 Wag | 21.0 | 6 | 160.0 | 110 | 3.90 | 2.875 | 17.02 | 0 |
Datsun 710 | 22.8 | 4 | 108.0 | 93 | 3.85 | 2.320 | 18.61 | 1 |
Hornet 4 Drive | 21.4 | 6 | 258.0 | 110 | 3.08 | 3.215 | 19.44 | 1 |
Hornet Sportabout | 18.7 | 8 | 360.0 | 175 | 3.15 | 3.440 | 17.02 | 0 |
Valiant | 18.1 | 6 | 225.0 | 105 | 2.76 | 3.460 | 20.22 | 1 |
Duster 360 | 14.3 | 8 | 360.0 | 245 | 3.21 | 3.570 | 15.84 | 0 |
Merc 240D | 24.4 | 4 | 146.7 | 62 | 3.69 | 3.190 | 20.00 | 1 |
Uno de los paquetes que se pueden usar para generar tablas es pander
, pero no lo incluyo en el tutorial porque en realidad, su objetivo es más general, su objetivo es facilitar la exportación de objetos R a pandoc
. De hecho, pander
puede ser una alternativa a knitr
; de hecho, el título de su pagina web es: pander: An R Pandoc Writer
.
Si presento, de forma sencilla los paquetes formatable
y dos paquetes muy prometedores como flextable
y gt
, pero existen más posibilidades, más paquetes para hacer tablas en R.
formatable
pkgEl paquete formattable
is designed for applying formatting on vectors and data frames to make data presentation easier, richer, more flexible and hopefully convey more information.
En estos dos post: aquí y aquí explican con detalle las posibilidades del paquete formattable.
Para ilustrar las posibilidades de formattable
utilizo uno de los ejemplos de su página web:
library(formattable)
df %>% formattable(list(
age = color_tile("white", "orange"),
grade = formatter("span", style = x ~ ifelse(x == "A",
style(color = "green", font.weight = "bold"), NA)),
area(col = c(test1_score, test2_score)) ~ normalize_bar("pink", 0.2),
final_score = formatter("span",
style = x ~ style(color = ifelse(rank(-x) <= 3, "green", "gray")),
x ~ sprintf("%.2f (rank: %02d)", x, rank(-x))),
registered = formatter("span",
style = x ~ style(color = ifelse(x, "green", "red")),
x ~ icontext(ifelse(x, "ok", "remove"), ifelse(x, "Yes", "No")))
))
id | name | age | grade | test1_score | test2_score | final_score | registered |
---|---|---|---|---|---|---|---|
1 | Bob | 28 | C | 8.9 | 9.1 | 9.00 (rank: 06) | Yes |
2 | Ashley | 27 | A | 9.5 | 9.1 | 9.30 (rank: 03) | No |
3 | James | 30 | A | 9.6 | 9.2 | 9.40 (rank: 02) | Yes |
4 | David | 28 | C | 8.9 | 9.1 | 9.00 (rank: 06) | No |
5 | Jenny | 29 | B | 9.1 | 8.9 | 9.00 (rank: 06) | Yes |
6 | Hans | 29 | B | 9.3 | 8.5 | 8.90 (rank: 08) | Yes |
7 | Leo | 27 | B | 9.3 | 9.2 | 9.25 (rank: 04) | Yes |
8 | John | 27 | A | 9.9 | 9.3 | 9.60 (rank: 01) | No |
9 | Emily | 31 | C | 8.5 | 9.1 | 8.80 (rank: 09) | No |
10 | Lee | 30 | C | 8.6 | 8.8 | 8.70 (rank: 10) | No |
flextable
pkgEl paquete flextable
es relativamente nuevo y tiene la ventaja de que provee un entorno para crear de forma relativamente sencilla tablas estadísticas para informes. Una de sus ventajas es que puede generar tablas para documentos html, Word, Powerpoint y pdf. Aquí tienes un bookdown sobre flextable.
Las tablas hechas con flextable
pueden exportarse a :
officer
pagedown
Si quieres ver las posibilidades de este paquete tendrás que ir a su página web. Como ejemplo:
library(flextable)
ft <- flextable::flextable(head(iris), col_keys = c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width", "Species" ))
ft %>% autofit() %>% align(align = "center", part = "all")
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | Species |
5.1 | 3.5 | 1.4 | 0.2 | setosa |
4.9 | 3.0 | 1.4 | 0.2 | setosa |
4.7 | 3.2 | 1.3 | 0.2 | setosa |
4.6 | 3.1 | 1.5 | 0.2 | setosa |
5.0 | 3.6 | 1.4 | 0.2 | setosa |
5.4 | 3.9 | 1.7 | 0.4 | setosa |
gt
packageEl paquete gt
también es relativamente nuevo pero creo que se está convirtiendo en el paquete de referencia para hacer tablas en R. Su página web está aquí. Hay que entender un poco su terminología, pero una vez lo haces, hacer tablas con gt
es muy sencillo.
Podemos empezar con una web con algunos ejemplos de tablas hechas con gt
o ir a su página web o a su vignette introductoria, o a este post donde se anuncia la aparición de gt(v0.2)
.
Por su parte, Thomas Mock ha realizado una serie de fantásticos post y presentaciones acerca del paquete gt
; por ejemplo: Beautiful tables in R, 10+ Guidelines for Better Tables in R, Functions and Themes for gt tables, Embedding custom HTML in gt tables y gt - a (G)rammar of (T)ables.
Si quieres ver el material de un tutorial de gt
diseñado por su desarrollador, Richard Iannone, puedes encontrarlo en este repo.
Hacer una tabla básica con gt
es tan fácil como hacer gt(my_df)
. Veámoslo:
library(gt) #-remotes::install_github("rstudio/gt")
df_tt_4 %>% gt()
Gender | Primary | Secondary | Tertiary | Upper_second | NA |
---|---|---|---|---|---|
Female | 0.4377224 | 0.4804831 | 0.6159875 | 0.579646 | 0.8 |
Male | 0.5622776 | 0.5195169 | 0.3840125 | 0.420354 | 0.2 |
Nos queda arreglar/tunear/añadir elementos a nuestra tabla. Para ello, lo primero es conocer como se llaman los distintos elementos de una tabla hecha con gt
:
La tabla que hemos hecho antes con gt
, es una tabla muy básica, sólo tiene dos elementos: the “Column Labels” y the “Table Body” (que es donde están los valores). Vamos a añadir más elementos a nuestra tabla:
tab_header()
. Con ella podemos añadir un título y subtitulo a la tabla. Además podemos dar formato al título, usando markdown!!, con la función md()
gt_tbl <- df_tt_4 %>% gt()
gt_tbl <- gt_tbl %>% tab_header(title = md("**Genero y nivel educativo**"),
subtitle = md("Porcentaje de *H y M* en cada nivel educativo"))
gt_tbl
Genero y nivel educativo | |||||
---|---|---|---|---|---|
Porcentaje de H y M en cada nivel educativo | |||||
Gender | Primary | Secondary | Tertiary | Upper_second | NA |
Female | 0.4377224 | 0.4804831 | 0.6159875 | 0.579646 | 0.8 |
Male | 0.5622776 | 0.5195169 | 0.3840125 | 0.420354 | 0.2 |
tab_source_note()
. Se pueden poner varias notas al pie.gt_tbl <- gt_tbl %>%
tab_source_note(md("Fuente: datos de [PIAAC](http://www.oecd.org/skills/piaac/)")) %>%
tab_source_note(md("Obtenidos a partir del paquete RPIAAC"))
gt_tbl
Genero y nivel educativo | |||||
---|---|---|---|---|---|
Porcentaje de H y M en cada nivel educativo | |||||
Gender | Primary | Secondary | Tertiary | Upper_second | NA |
Female | 0.4377224 | 0.4804831 | 0.6159875 | 0.579646 | 0.8 |
Male | 0.5622776 | 0.5195169 | 0.3840125 | 0.420354 | 0.2 |
Fuente: datos de PIAAC | |||||
Obtenidos a partir del paquete RPIAAC |
tab_footnote()
. La función auxiliar cells_body()
se usa para especificar que celdas de datos tendrán la marca de la llamada al pie de página. Se puede incluso usar condiciones para seleccionar que celdas llevarán la footnote.# Añadimos la misma nota al pie a dos celdas de la tabla
# Para seleccionar las celdas que llevarán la footnote se usa la f. `cell_body()`
gt_tbl <- gt_tbl %>% tab_footnote("Por encima del 60%", cells_body(c(Tertiary, `NA`), rows = 1) )
# Show the gt Table
gt_tbl
Genero y nivel educativo | |||||
---|---|---|---|---|---|
Porcentaje de H y M en cada nivel educativo | |||||
Gender | Primary | Secondary | Tertiary | Upper_second | NA |
Female | 0.4377224 | 0.4804831 | 0.61598751 | 0.579646 | 0.81 |
Male | 0.5622776 | 0.5195169 | 0.3840125 | 0.420354 | 0.2 |
Fuente: datos de PIAAC | |||||
Obtenidos a partir del paquete RPIAAC | |||||
1
Por encima del 60%
|
gt(rowname_col = "nombre")
An easy way to generate a Stub part is by specifying a stub column in the gt() function with the rowname_col argument. Alternatively, we can have an input dataset with a column named rowname—this magic column will signal to gt that that column should be used as the stub, making row labels. Let’s add a stub with our islands_tbl dataset by modifying the call to gt():
gt_tbl <- df_tt_4 %>%
gt(rowname_col = "Gender") %>%
tab_stubhead(label = md("**Género**"))
gt_tbl
Género | Primary | Secondary | Tertiary | Upper_second | NA |
---|---|---|---|---|---|
Female | 0.4377224 | 0.4804831 | 0.6159875 | 0.579646 | 0.8 |
Male | 0.5622776 | 0.5195169 | 0.3840125 | 0.420354 | 0.2 |
Con gt
se pueden hacer muchas más cosas, puedes verlas en su página web
, o en esta secuencia de posts de Thomas Mock: 10+ Guidelines for Better Tables in R, Functions and Themes for gt tables, Embedding custom HTML in gt tables y gt - a (G)rammar of (T)ables.
Un ejemplo que me ha gustado es este de Sharla Gelfand en el que añade una columna de imágenes a su tabla. Voy a hacerlo yo con 3 imágenes de Wikimedia Commons y una tabla con datos del data.frame iris.
Para ello, primero, añado una columna con los url’s de las fotos al data.frame iris
:
urls_lirios <- c("https://upload.wikimedia.org/wikipedia/commons/thumb/2/2d/Iris_latifolia-Lac_Aule-Laruns-2522~2013_07_29.JPG/1280px-Iris_latifolia-Lac_Aule-Laruns-2522~2013_07_29.JPG",
"https://upload.wikimedia.org/wikipedia/commons/thumb/2/21/Lilium_michiganense_2.jpg/310px-Lilium_michiganense_2.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/b/bd/Nacho_Vegas_Fib.jpg/800px-Nacho_Vegas_Fib.jpg")
iris_con_urls <- iris %>% group_by(Species) %>% slice_max(Sepal.Length, n = 1) %>% add_column(urls_lirios) %>% ungroup()
Para después transformar los urls a imágenes con:
iris_con_urls %>% gt() %>%
gt::text_transform(locations = cells_body(columns = c(urls_lirios)),
fn = function(x) {gt::web_image(x, height = 30)})
Sepal.Length | Sepal.Width | Petal.Length | Petal.Width | Species | urls_lirios |
---|---|---|---|---|---|
5.8 | 4.0 | 1.2 | 0.2 | setosa | |
7.0 | 3.2 | 4.7 | 1.4 | versicolor | |
7.9 | 3.8 | 6.4 | 2.0 | virginica |
En este post se muestra como incluir, no solo imágenes, sino también gráficos ggplot en una tabla hecha con gt
. Para ello usa el paquete gtExtras
. Por ejemplo está maravilla de tabla:
tt_08_img_02_tabla-ganadores-tour.png
Aquí tienes un tutorial exclusivamente dedicado a tablas interactivas. Aquí sólo presentaré algún ejemplo de tabla interactiva con los paquetes DT
, reactable
y rpivottable
.
DT
packageEl paquete DT es “a wrapper of the JavaScript library DataTables”. La mejor forma de aprender a hacer tablas con DT es su página web.
El autor de DT
es Yihui Xie el desarrollador de knitr
. El principal atractivo del paquete es que permite hacer tablas interactivas con funcionalidades como filtrar, paginar, ordenar, etc… los valores de las tablas.
Hacer tablas básicas con DT es muy sencillo. Por ejemplo:
DT::datatable(iris)
Las tablas hechas con DT tienen muchas más posibilidades. Como ejemplo la siguiente tabla.La mejor forma de aprender a hacer tablas con DT es su página web
DT::datatable(iris, filter = 'top',
options = list(pageLength = 5, autoWidth = TRUE ))
Una posibilidad de las tablas hechas con DT
es que se pueden descargar. Lo explican en detalle aquí:
iris %>% DT::datatable(extensions = 'Buttons',
options = list(dom = 'Blfrtip',
buttons = c('copy', 'csv', 'excel', 'pdf', 'print'),
pageLength = 5, autoWidth = TRUE ))
reactable
packageEl paquete reactable
también permite hacer tablas interactivas, esta vez se basa en la librería de JS React.
El uso básico es tan sencillo como:
reactable::reactable(iris)
Pero se pueden hacer tablas tan fabulosas como esta o esta o esta.
rpivotTable
packageThe rpivotTable package is an R htmlwidget built around the pivottable JS library.
El paquete rpivotTable
genera tablas que permiten al usuario generar sus propios resultados.
Como las tablas que genera el paquete rpivotTable
pueden, según las opciones que utilice el usuario de la tabla, cambiar mucho de tamaño, voy a poner un ejemplo de esta tabla pero al final de este documento, después de la bibliografía.
Crear una tabla con rpivotTable
es muy sencillo. Podéis verla tabla al final del documento.
library(rpivotTable) #- remotes::install_github(c("ramnathv/htmlwidgets", "smartinsightsfromdata/rpivotTable"))
rpivotTable(df, rows = "Gender", cols = c("Country"), width = "100%", height = "400px")
R es un lenguaje/entorno para hacer análisis estadísticos, así que muchas veces se han de presentar los resultados de estimación de algún modelo estadístico, PERO, los resultados de estimación de estos modelos, en general, no se almacenan en data.frames, sino en listas. ¿Es esto importante? Sí, si lo queremos es presentar los resultados de estimación en una tabla. La razón consiste en que los paquetes para tablas que hemos visto anteriormente (gt
, DT
, etc …) solo generan tablas de resultados almacenados en data.frames, así que no los podremos usar, al menos directamente, para hacer tablas de resultados asociados a modelos estadísticos. Afortunadamente hay unos cuantos paquetes en R cuyo propósito es precisamente ese, presentar los resultados de estimación de algún modelo o técnica estadística como tablas.
Por ejemplo, vamos a estimar un modelo de regresión lineal. Utilizaremos estos datos:
urla <- "https://raw.githubusercontent.com/perezp44/iris_data/master/data/PIAAC_data_small.csv"
df <- read_csv(urla)
Con ellos estimamos el siguiente modelo lineal:
my_model <- lm(Wage_hour ~ Numeracy_score + Gender , data = df)
my_model
#>
#> Call:
#> lm(formula = Wage_hour ~ Numeracy_score + Gender, data = df)
#>
#> Coefficients:
#> (Intercept) Numeracy_score GenderMale
#> 3.46022 0.02988 0.52874
Generalmente hay funciones específicas para mostrar los resultados de estimación, por ejemplo la función summary()
:
summary(my_model)
#>
#> Call:
#> lm(formula = Wage_hour ~ Numeracy_score + Gender, data = df)
#>
#> Residuals:
#> Min 1Q Median 3Q Max
#> -10.903 -3.453 -1.059 2.097 104.179
#>
#> Coefficients:
#> Estimate Std. Error t value Pr(>|t|)
#> (Intercept) 3.460222 0.351822 9.835 < 0.0000000000000002 ***
#> Numeracy_score 0.029881 0.001325 22.554 < 0.0000000000000002 ***
#> GenderMale 0.528745 0.134064 3.944 0.0000808 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Residual standard error: 5.973 on 7992 degrees of freedom
#> (27 observations deleted due to missingness)
#> Multiple R-squared: 0.06324, Adjusted R-squared: 0.063
#> F-statistic: 269.8 on 2 and 7992 DF, p-value: < 0.00000000000000022
Los resultados mostrados por summary()
permiten hacerse una idea de los resultados de la estimación pero no suelen ser adecuados para presentarlos en un informe o en un artículo científico.
Hay bastantes paquetes que permiten obtener tablas con los resultados de la estimación de modelos estadísticos, por ejemplo, el paquete finalfit
y muchos otros, sin embargo, en este tutorial solo se mostrará algún ejemplo para los paquetes stargazer
, modelsummary
, gtsummary
y sjPlot
stargazer
El paquete stargazer
permite fácilmente presentar tablas con resultados de regresión. Permite crear tablas en latex, html o ascii. Aquí hay una cheatsheet sobre stargazer
stargazer::stargazer(my_model, type = "html")
Dependent variable: | |
Wage_hour | |
Numeracy_score | 0.030*** |
(0.001) | |
GenderMale | 0.529*** |
(0.134) | |
Constant | 3.460*** |
(0.352) | |
Observations | 7,995 |
R2 | 0.063 |
Adjusted R2 | 0.063 |
Residual Std. Error | 5.973 (df = 7992) |
F Statistic | 269.760*** (df = 2; 7992) |
Note: | p<0.1; p<0.05; p<0.01 |
Se pueden presentar los resultados de varios modelos en una tabla. Veámoslo. Para ello vamos a estimar tres modelos y los almacenaremos en una lista:
df <- df %>% mutate(Numeracy_score_b = ifelse(Numeracy_score > mean(Numeracy_score, na.rm = TRUE), 1, 0)) #- binary variable for logit model
my_models <- list()
my_models[['W: OLS 1']] <- lm( Wage_hour ~ Numeracy_score + Gender , df)
my_models[['Nu: OLS 2']] <- lm( Numeracy_score ~ Education + Gender , df)
my_models[['Nu: Logit 1']] <- glm( Numeracy_score_b ~ Education + Gender , df, family = binomial())
Una vez hemos estimado los 3 modelos y almacenado sus resultados de estimación en la lista my_models
stargazer::stargazer(my_models, type = "html", title = "Results", align = TRUE)
Dependent variable: | |||
Wage_hour | Numeracy_score | Numeracy_score_b | |
OLS | OLS | logistic | |
(1) | (2) | (3) | |
Numeracy_score | 0.030*** | ||
(0.001) | |||
EducationSecondary | 46.752*** | 1.788*** | |
(1.983) | (0.134) | ||
EducationTertiary | 89.531*** | 3.424*** | |
(2.137) | (0.143) | ||
EducationUpper_second | 75.903*** | 2.857*** | |
(2.388) | (0.150) | ||
GenderMale | 0.529*** | 12.947*** | 0.448*** |
(0.134) | (1.000) | (0.049) | |
Constant | 3.460*** | 196.447*** | -2.270*** |
(0.352) | (1.955) | (0.134) | |
Observations | 7,995 | 8,014 | 8,014 |
R2 | 0.063 | 0.229 | |
Adjusted R2 | 0.063 | 0.229 | |
Log Likelihood | -4,894.853 | ||
Akaike Inf. Crit. | 9,799.706 | ||
Residual Std. Error | 5.973 (df = 7992) | 44.382 (df = 8009) | |
F Statistic | 269.760*** (df = 2; 7992) | 596.336*** (df = 4; 8009) | |
Note: | p<0.1; p<0.05; p<0.01 |
modelsummary
El paquete modelsummary
es más moderno, y también permite hacer tablas resumen de estimaciones de modelos. Por ejemplo:
library(modelsummary) #- remotes::install_github('vincentarelbundock/modelsummary')
mm <- modelsummary::msummary(my_models, title = "Resultados de estimación")
mm
W: OLS 1 | Nu: OLS 2 | Nu: Logit 1 | |
---|---|---|---|
(Intercept) | 3.460 | 196.447 | −2.270 |
(0.352) | (1.955) | (0.134) | |
Numeracy_score | 0.030 | ||
(0.001) | |||
GenderMale | 0.529 | 12.947 | 0.448 |
(0.134) | (1.000) | (0.049) | |
EducationSecondary | 46.752 | 1.788 | |
(1.983) | (0.134) | ||
EducationTertiary | 89.531 | 3.424 | |
(2.137) | (0.143) | ||
EducationUpper_second | 75.903 | 2.857 | |
(2.388) | (0.150) | ||
Num.Obs. | 7995 | 8014 | 8014 |
R2 | 0.063 | 0.229 | |
R2 Adj. | 0.063 | 0.229 | |
AIC | 51272.9 | 83541.3 | 9799.7 |
BIC | 51300.8 | 83583.2 | 9834.7 |
Log.Lik. | −25632.432 | −41764.631 | −4894.853 |
F | 269.760 | 596.336 |
Tiene muchas más posibilidades.
gtsummary
El paquete gtsummary
permite, además de realizar tablas resumen de data.frames, confeccionar tablas de modelos de regresión de forma sencilla y muy configurables.
Como he dicho gtsummary
tiene muchas posibilidades, algunas de ellas puedes verlas en este video, en estas transparencias, o en este post.
Como ejemplos de su uso, una tabla resumen de un data.frame:
iris %>% gtsummary::tbl_summary()
Characteristic | N = 1501 |
---|---|
Sepal.Length | 5.80 (5.10, 6.40) |
Sepal.Width | 3.00 (2.80, 3.30) |
Petal.Length | 4.35 (1.60, 5.10) |
Petal.Width | 1.30 (0.30, 1.80) |
Species | |
setosa | 50 (33%) |
versicolor | 50 (33%) |
virginica | 50 (33%) |
1
Median (IQR); n (%)
|
Ahora una tabla de un modelo de regresión:
gtsummary::tbl_regression(my_model)
Characteristic | Beta | 95% CI1 | p-value |
---|---|---|---|
Numeracy_score | 0.03 | 0.03, 0.03 | <0.001 |
Gender | |||
Female | — | — | |
Male | 0.53 | 0.27, 0.79 | <0.001 |
1
CI = Confidence Interval
|
sjPlot
El paquete sjPlot
tiene muchas posibilidades, pero en concreto puede hacer tablas de modelos:
sjPlot::tab_model(my_model)
Wage hour | |||
---|---|---|---|
Predictors | Estimates | CI | p |
(Intercept) | 3.46 | 2.77 – 4.15 | <0.001 |
Numeracy_score | 0.03 | 0.03 – 0.03 | <0.001 |
Gender [Male] | 0.53 | 0.27 – 0.79 | <0.001 |
Observations | 7995 | ||
R2 / R2 adjusted | 0.063 / 0.063 |
Tiene otras utilidades, por ejemplo:
sjPlot::plot_model(my_model)
Hay muchos más paquetes especializados en hacer tablas con resultados de la estimación de modelos. Aquí solo mostraré algo de 3 de ellos, los paquetes report
y apaTables
, pero existen otros paquetes como:
flipRegression
. En este post explican como usar sus funciones.
papaja
, es un paquete para producir documentos y tablas conformes a la American Psychological Association (APA) manuscript guidelines (6th Edition).
academicWriteR
, está relacionado con papaja
. Es un paquete con algunas funciones útiles para escribir informes estadísticos. Por ejemplo la función count_word()
.
report
Según pone en su página web:
It automatically produces reports of models and dataframes according to best practice guidelines (e.g., APA’s style guide), ensuring standardization and quality in results reporting.
library(report) #- devtools::install_github("neuropsychology/report")
rr <- report(my_model)
as.report_table(rr)
Parameter | Coefficient | CI | CI_low | CI_high | t | df_error | p | Std_Coefficient | Std_Coefficient_CI_low | Std_Coefficient_CI_high | Fit | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | (Intercept) | 3.4602220 | 0.95 | 2.7705599 | 4.1498840 | 9.835161 | 7992 | 0.0000000 | -0.0410359 | -0.0704696 | -0.0116023 | NA |
2 | Numeracy_score | 0.0298813 | 0.95 | 0.0272842 | 0.0324783 | 22.554474 | 7992 | 0.0000000 | 0.2447969 | 0.2235210 | 0.2660727 | NA |
3 | Gender [Male] | 0.5287445 | 0.95 | 0.2659450 | 0.7915441 | 3.943984 | 7992 | 0.0000808 | 0.0856835 | 0.0430966 | 0.1282704 | NA |
4 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA |
5 | AIC | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 51272.8639967 |
6 | BIC | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 51300.8102832 |
7 | R2 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 0.0632384 |
8 | R2 (adj.) | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 0.0630039 |
10 | Sigma | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | 5.9733440 |
También permite obtener una descripción verbal de los resultados en texto:
as.report_text(rr)
#> We fitted a linear model (estimated using OLS) to predict Wage_hour with Numeracy_score and Gender (formula: Wage_hour ~ Numeracy_score + Gender). The model explains a statistically significant and weak proportion of variance (R2 = 0.06, F(2, 7992) = 269.76, p < .001, adj. R2 = 0.06). The model's intercept, corresponding to Numeracy_score = 0 and Gender = Female, is at 3.46 (95% CI [2.77, 4.15], t(7992) = 9.84, p < .001). Within this model:
#>
#> - The effect of Numeracy_score is statistically significant and positive (beta = 0.03, 95% CI [0.03, 0.03], t(7992) = 22.55, p < .001; Std. beta = 0.24, 95% CI [0.22, 0.27])
#> - The effect of Gender [Male] is statistically significant and positive (beta = 0.53, 95% CI [0.27, 0.79], t(7992) = 3.94, p < .001; Std. beta = 0.09, 95% CI [0.04, 0.13])
#>
#> Standardized parameters were obtained by fitting the model on a standardized version of the dataset.
apaTables
Según su página web:
This package creates Word files (.doc files) containing APA style tables for several types of analyses. Using this package minimizes transcription errors and reduces the number commands needed by the user.
library(apaTables) #- install.packages("apaTables",dep = TRUE)
apa.reg.table(my_model)
En este hilo de Twitter se habla sobre los mejores paquetes R para hacer tablas.
David Keyes señala aquí, que quieres hacer tablas para Word, posiblemente la mejor opción sea el paquete flextable
Si lo que necesitas es hacer tablas para documentos pdf, puedes empezar por aquí. Al final remite a este otro documento. También puedes usar este generador de tablas.
Si quieres tablas de porcentajes etc… aquí plantean varias posibilidades para hacerlas.
Si quieres hacer tablas parecidas a las que se hacen con PROC FREQ de SAS o CROSSTABS en SPSS puedes usar la función CrossTable()
del paquete gmodels
. En este post muestran otras alternativas.
Si necesitas hacer 3-way tables, puedes hacerlas con stats::xtabs()
. Para después imprimirla con ftable(my_table)
. Lo explicanaquí y aquí. Por ejemplo:
my_table <- stats::xtabs(~ Education + Country + Gender, data = df)
stats::ftable(my_table)
Otra alternativa para tablas de frecuencias de 3 variables es usar janitor::tabyl(X1, X2, X3)
. Lo explican aquí
df %>% janitor::tabyl(Education, Country, Gender)
Los paquetes rhandsontable
y excelR
permiten crear tablas editables por el usuario.
El paquete expss
hace tablas similares a las de SPSS.
En este post hacen un repaso de paquetes que permiten crear tablas con características especiales como que sus valores sean editables por el usuario, que tengan formato condicionado a los valores de las tablas, que se puedan ordenar y filtrar sus valores etc …
Un post con las tablas ganadoras del “2020 RStudio Table Contest”.
rpivotTable
El paquete rpivotTable
:
The rpivotTable package is an R htmlwidget built around the pivottable library.
rpivotTable::rpivotTable(df, rows = "Gender", cols = c("Country"), width = "100%", height = "400px")