9 Estilos y formatos de tabla

En cuanto a operativa de trabajo, esta es nuestra última sección, puesto que la última es complementaria. En esta sección aprenderemos a combinar gráficos y tablas con visualizaciones especiales, de la forma más diversa.

9.1 Introducción

Los paquetes que hemos utilizado en nuestro trabajo con las tablas ya han sido referido en multitud de ocasiones. expss nos permite además de obtener el cuadro preciso, operar con las filas y columnas de la tabla, puesto que el objeto creado es como un dataframe especial, ya que su clase de objeto es etable. Por tanto, vamos a tener la capacidad de poder operar con las columnas (variable) así como también con las filas (registros de ese dataframe).

Existen multitud de paquetes con los que poder interactuar para poder obtener los resultados deseados. Los paquetes DT o _datatable_, formattable, y también kable y kableExtra que son nativos para utilizar junto con rmarkdown, lo que conforma un amplio espectro de soluciones. Deberemos buscar aquella que responda a nuestras expectativas y que haga nos sintamos más cómodos. Nuestra elección será kableExtra.

Por el lado de las operaciones en la tabla, comenzaremos sentando las bases de como se realizan las operaciones, para luego trabajar la salida aportando estilos y formatos condicionales a las mismas.

9.2 Operaciones con tablas

Como hemos indicado en la presentación anterior, nuestro primer paso será la realización de operaciones muy simples con las tablas, que seguro en nuestro trabajo estamos acostumbrados a hacer. también vamos a adentramos un poco en la creación de los elementos con los que vamos a trabajar. Ahora ya no trabajaremos sobre lo que ha sido nuestro fichero base, sino que iremos aportando nuestros propios ejemplos que se adecuen a lo que deseamos hacer.

Imaginemos que disponemos datos sobre las cifras de ventas anuales de cinco empresas muy conocidas: Apple, Amazon, Microsoft, Google y Facebook. Estas empresas ofrecen información sobre Ingresos , Beneficio Operativo y Beneficio Neto de cuatro trimestre de 2018 y primer trimestre de 2019 en millones de dólares. Con estos datos creamos un dataframe.

data <-
  data.frame(
    emp = c(
      "Apple",
      "Apple",
      "Microsoft",
      "Microsoft",
      "Amazon",
      "Amazon",
      "Google",
      "Google",
      "Facebook",
      "Facebook"
    ),
    ing = c(
      84310,
      91819,
      32471,
      36906,
      72400,
      87400,
      39276,
      46075,
      16914,
      21082
    ),
    bfo = c(23346, 25569, 10258, 13891, 3786, 3879, 8221, 9266, 7820, 8858),
    bfn = c(19965, 22236, 8420, 11649, 3027, 3268, 8948, 10671, 6882, 7349),
    per = c(
      "IV-2018",
      "I-2019",
      "IV-2018",
      "I-2019",
      "IV-2018",
      "I-2019",
      "IV-2018",
      "I-2019",
      "IV-2018",
      "I-2019"
    )
  )
data
##          emp   ing   bfo   bfn     per
## 1      Apple 84310 23346 19965 IV-2018
## 2      Apple 91819 25569 22236  I-2019
## 3  Microsoft 32471 10258  8420 IV-2018
## 4  Microsoft 36906 13891 11649  I-2019
## 5     Amazon 72400  3786  3027 IV-2018
## 6     Amazon 87400  3879  3268  I-2019
## 7     Google 39276  8221  8948 IV-2018
## 8     Google 46075  9266 10671  I-2019
## 9   Facebook 16914  7820  6882 IV-2018
## 10  Facebook 21082  8858  7349  I-2019

Ahora vamos a obtener la tabla con la que deseamos trabajar, que sería comparar los ingresos de las cinco compañías en los dos períodos. Para ello creamos una tabla de la siguiente forma.

as.datatable_widget(data %>% 
    tab_cols(per) %>% 
    tab_rows(emp) %>% 
    tab_cells(ing) %>% 
    tab_stat_sum() %>%
    tab_pivot())

Figura 9.1: Tabla con formato estándar de expss

Este sería el formato estándar de salida, pero como ya hemos visto en anteriores secciones, podemos adaptar esa salida a nuestras necesidades. Nótese que aun no siendo necesario, vamos a guardar la tabla en un output llamado tab01 que luego vamos a publicar. Utilizamos la función class()para mostrarte que tras hacer la tabla, el output almacenado es un etable dataframe.

tab01 <- data %>% 
    tab_cols("|"=unvr(per)) %>% 
    tab_rows("|"=unvr(emp)) %>% 
    tab_cells("|"=unvr(ing)) %>% 
    tab_stat_sum(label="|") %>%
    tab_pivot()
class(tab01)
## [1] "etable"     "data.frame"
as.datatable_widget(tab01)

Figura 9.2: Tabla con formato adaptado de expss

Podemos ver que esta tabla ya tiene un formato más adecuado a nuestra necesidad. Hemos quitado aquellos textos que en esta ocasión eran innecesarios.

Si convertimos (aunque ya lo es) y mostramos esta tabla como dataframe el resultado sería éste.

tab01 <- as.data.frame(tab01)
class(tab01)
## [1] "data.frame"
tab01
##   row_labels I-2019 IV-2018
## 1     Amazon  87400   72400
## 2      Apple  91819   84310
## 3   Facebook  21082   16914
## 4     Google  46075   39276
## 5  Microsoft  36906   32471

Nótese que ahora ya el output no es un objeto de tipo etable, solo es dataframe. Ya estamos en condiciones de poder operar. Calculemos la diferencia entre los dos trimestres.

tab01$dif <- tab01[,2]-tab01[,3]
tab01
##   row_labels I-2019 IV-2018   dif
## 1     Amazon  87400   72400 15000
## 2      Apple  91819   84310  7509
## 3   Facebook  21082   16914  4168
## 4     Google  46075   39276  6799
## 5  Microsoft  36906   32471  4435

Esta información puede ser presentada en un gráfico tal como veíamos en la sección anterior. Aprovechemos también para cambiar el nombre de las columnas …

colnames(tab01) <- c('empresa', '2019 (1º)', '2018 (4º)', 'dif')
highchart() %>%
  hc_chart(type = 'column') %>%
  hc_title(text = 'Cifra de negocio de las 5 mayores tecnológicas') %>%
  hc_xAxis(categories = tab01[, 1]) %>%
  hc_yAxis(min = 0, max = 100000) %>%
  hc_add_series(
    data = tab01[, 2],
    name = "IV de 2018",
    dataLabels = list(enabled = TRUE),
    color = "#FA8072"
  ) %>%
  hc_add_series(
    data = tab01[, 3],
    name = "I de 2019",
    dataLabels = list(enabled = TRUE),
    color = "#E9967A"
  ) %>%
  hc_add_series(
    data = tab01[, 4],
    name = "Diferencia",
    dataLabels = list(enabled = TRUE),
    color = "teal"
  )

Figura 9.3: Gráfico del dataframe

Añadamos más información a la tabla. Vamos a calcular el porcentaje de incremento. Habrían muchas formas de hacerlo, pero vamos a tratar de hacerlo de la forma más simple.

tab01$difpct <- round(((tab01[,2]/tab01[,3])-1)*100,1) #cociente entre valores y paso a porcentaje con redondeo a un decimal
tab01
##     empresa 2019 (1º) 2018 (4º)   dif difpct
## 1    Amazon     87400     72400 15000   20.7
## 2     Apple     91819     84310  7509    8.9
## 3  Facebook     21082     16914  4168   24.6
## 4    Google     46075     39276  6799   17.3
## 5 Microsoft     36906     32471  4435   13.7

Esto nos va a permitir probar algo que no vimos en la sección anterior, presentar en función de otro eje Y la información. Se puede observar que en el script abajo referido, estamos creando en la función hc_yAxis() una lista de dos ejes a los que referenciar los datos, mientras que las cifras absolutas se miran con el eje Y de la izquierda, el dato relativo se mira con el eje Y a la derecha del gráfico. El resto de opciones ya fueron analizadas

highchart() %>%
  hc_chart(type = "column") %>%
  hc_title(text = "Cifra de negocio de las 5 mayores tecnológicas") %>%
  hc_xAxis(categories = tab01[, 1]) %>%
  hc_yAxis_multiples(
    list(
      title = list(text = "Millones de dólares"),
      min = 0,
      max = 100000
    ),
    list(
      title = list(text = "Porcentaje"),
      min = 0,
      max = 100,
      opposite = TRUE
    )
  ) %>%
  hc_add_series(
    data = tab01[, 2],
    name = "IV de 2018",
    dataLabels = list(enabled = TRUE),
    color = "#FA8072"
  ) %>%
  hc_add_series(
    data = tab01[, 3],
    name = "I de 2019",
    dataLabels = list(enabled = TRUE),
    color = "#E9967A"
  ) %>%
  hc_add_series(
    data = tab01[, 4],
    name = "Diferencia",
    dataLabels = list(enabled = TRUE),
    color = "teal"
  ) %>%
  hc_add_series(
    data = tab01[, 5],
    name = "Diferencia porcentual",
    dataLabels = list(enabled = TRUE, format = "{point.y} %"),
    color = "darkblue",
    yAxis = 1
  )

Figura 9.4: Gráfico del dataframe

9.3 Estilo de las tablas

9.3.1 Estilo y posición

Vamos ahora a trabajar con la con la tabla y realizaremos algunos cambios sobre ella. Utilizaremos el paquete kableExtra. Este paquete permite trabajar con los dataframe para formatear la manera en que se van a mostrar en la pantalla. Existen multitud de paquetes que nos permitirían hacer cosas semejantes, entre los que podríamos destacar formattable, DT o flextable entre otros.

Nuestro primer cambio va a ser mostrar de forma diferente nuestra tabla. Con un estilo diferente a lo que ha aprecido hasta ahora. Le vamos a aplicar el estilo típico de Bootstrap, conocida librería JS con la que fue desarrollada Twitter. Te recomendamos visitar la viñeta del paquete en este sitio si quieres ver todas sus posibilidades.

kbl(tab01) %>%
    kable_styling()
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Se puede apreciar que el aspecto es diferente al mostrado por las tablas hasta ahora. Este sería el formato más básico de Bootstrap. Existen otras opciones entre las que vamos a ir añadiendo algunas características, aplicables a los formatos.

Usando paper

kbl(tab01) %>%
  kable_paper("hover")
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Usando classic

kbl(tab01) %>%
  kable_classic("hover")
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Usando minimal

kbl(tab01) %>%
  kable_minimal("hover")
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Añadimos además del efecto hover con el ratón, el oscurecimiento o striped de las filas para facilitar la lectura y usabilidad.

kbl(tab01) %>%
  kable_material(c("striped","hover"))
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Finalizamos de nuevo con el primer formato que será el que mantendremos a partir de este momento.

kbl(tab01) %>% 
    kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"))
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Una de las características que en algunos casos puede ser muy valiosa, es la posibilidad de establecer la tabla como flotante a derecha o izquierda del texto.

kbl(tab01) %>%
  kable_styling(bootstrap_options = c("striped", "hover", "condensed", "responsive"), full_width = F, position = "float_right")
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Este texto que estamos escribiendo, se ubicaría a la izquierda de la tabla, ya que hemos indicado que ésta debe ser situada a la derecha como elemento flotante. Al mismo tiempo se le ha incluido la posibilidad de que la tabla no ocupe el 100% del espacio sino que se autoajuste al tamaño de las columna. Este efecto puede ser muy utilizado para incluir comentarios sobre la tabla que estamos publicando. Como es lógico, la tabla puede ser flotante a la derecha o a la izquierda del texto.

Existen otras muchas características que pueden ser aplicadas para conseguir posicionar la tabla de la forma deseada:

  • fijar la cabecera
  • incluir la tabla en una caja
  • aplicar tamaños de fuente
  • … y más.

9.3.2 Formato específico de fila o columna

Veamos algunas de las posibilidades que nos ofrece kableExtrapara formatear partes de la tabla. Para ello debemos saber que las columnas se identifican por número secuencial (1 a n) al igual que las filas. Se pueden aplicar formatos tanto a filas como a columnas. Para ilustrar un ejemplo, vamos a utilizar una función de R muy frecuente denominada ifelse() que es el equivalente a la función SI() de Excel y con una estructura idéntica. Esta función admite la concatenación y sus parámetros son por este orden:

  • condición que debe cumplirse
  • resultado si verdadero
  • resultado si falso

En nuestro caso nuestro resultado es la aplicación de un color verde o rojo según estemos por encima o por debajo de la media de la columna. Del mismo modo, también se puede aplicar como podemos observar un color directo a una columna determinada. Usaremos la función column_spec().

kbl(tab01) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = F
  ) %>%
  column_spec(2, color = "teal", bold = TRUE) %>%
  column_spec(
    5,
    color = "white",
    background = ifelse(tab01$difpct > mean(tab01$difpct), "#3CB371", "#FA8072"),
    popover = paste(tab01[, 1])
  )
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Otra posibilidad es la de poder marcar celdas cell_spec() o filas con row_spec()en particular …

kbl(tab01) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = F
  ) %>%
  row_spec(3:5,
           bold = T,
           color = "white",
           background = "teal")
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Un caso particular para la fila 0, la de encabezado.

kbl(tab01) %>%
  kable_styling(c("striped", "hovered"), full_width = F) %>%
  row_spec(
    0,
    angle = -20,
    align = "left",
    background = "teal",
    color = "white",
    bold = TRUE
  ) %>%
  row_spec(1:5, align = "center", background = "light#FA8072")
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

9.4 Adición de imágenes y de otros elementos

Ya para finalizar, es muy habitual querer utilizar imágenes para ilustrar la tabla, darle un aspecto más impactante. Vemos como hacerlo. Esta imagen puede sustituir el contenido completo de la columna o añadirse al final de la misma. En nuestro ejemplo queremos sustituir, por lo que nuestra primera instrucción limpia la columna de tab01 denominada empresa. Posteriormente se crea un vector con las cinco imágenes que aprovecharemos en la columna 1. Para no perder nuestra tabla original, hacemos un copia de la misma en tab02.

tab02 <- tab01
tab02$empresa <- ""
vector.img <-
  c(
    "https://download.tesigandia.com/test/20201112113121.png",
    "https://download.tesigandia.com/test/20201112112927.png",
    "https://download.tesigandia.com/test/20201112113240.png",
    "https://download.tesigandia.com/test/20201112113259.png",
    "https://download.tesigandia.com/test/20201112113048.png"
  )
kbl(tab02) %>%
  kable_styling(bootstrap_options = c("hover"),
                full_width = FALSE) %>%
  column_spec(1, image = vector.img) %>%
  column_spec(2, color = "teal", bold = TRUE) %>%
  column_spec(5,
              color = "white",
              background = ifelse(tab02$difpct > mean(tab02$difpct), "#3CB371", "#FA8072"))
empresa 2019 (1º) 2018 (4º) dif difpct
87400 72400 15000 20.7
91819 84310 7509 8.9
21082 16914 4168 24.6
46075 39276 6799 17.3
36906 32471 4435 13.7

9.4.1 Adición de iconos de FontAwesome

En la siguiente tabla, vamos a añadir las marcas de icono llamadas fontawesome que permite reproducir en modo texto los iconos más típicos. En nuestro caso y siguiendo el uso de la función ifelse que hemos visto antes, añadiremos una flecha arriba verde si la media de la cifra de negocio es mayor que la media o una flecha abajo roja si es menor.

tab02 <- tab01
tab02[, 2] <-
  ifelse(tab02[, 2] > mean(tab02[, 2]), paste(tab02[, 2], fa("arrow-circle-up", fill =
                                                               "#3CB371")), paste(tab02[, 2], fa("arrow-circle-down", fill = "red")))
kbl(tab02, escape = FALSE) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = F
  ) %>%
  column_spec(2, color = "teal", bold = TRUE) %>%
  column_spec(
    5,
    color = "white",
    background = ifelse(tab02$difpct > mean(tab01$difpct), "#3CB371", "#FA8072"),
    popover = paste(tab02[, 1])
  )
empresa 2019 (1º) 2018 (4º) dif difpct
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

9.4.2 Adición de gráficos de tipo sparkline

Dado que no tenemos datos para poder representar, vamos a suponer una serie de datos para cada empresa y vamos a hacer un gráfico de líneas de tipo sparkline, que vamos a integra como una columna más de nuestra tabla. sparkline es un paquete de R que utiliza la librería jquery sparklines para presentar gráficos minimalistas y ser integrados en nuestros dataframe, y por tanto, publicados en tablas.

En primer lugar creamos la serie de datos para cada empresa y lo vamos a hacer usando la función sample() que nos aportará 15 valores aleatorios entre 1 y 100 con posibilidad de repetición. Esta es una fórmula muy sencilla que se utiliza mucho en R para crear datos de test.

sparkline(0) #inicializamos la función sparkline

Figura 9.5: Tabla con elementos de imagen y sparkline

amz <- sample(1:100, 15, replace = TRUE)
app <- sample(1:100, 15, replace = TRUE)
fac <- sample(1:100, 15, replace = TRUE)
goo <- sample(1:100, 15, replace = TRUE)
mic <- sample(1:100, 15, replace = TRUE)
df <-
  data.frame(amz, app, fac, goo, mic) # los unimos para mostrarlos en forma de tabla, aunque no sería necesario.
df
   amz app fac goo mic
1   64  85  71  43  20
2   70  85  80  28  55
3   86  17   7  30  21
4   76  42  42  85  72
5   54  35  91  57   9
6   65  29   4  23  25
7   71  75  26  46  72
8   82  88  75  93 100
9    4  13  66   3  84
10  82  18  44  47  18
11  79  75  21  54  30
12  17   4  73  27   6
13  56  27   9  96  72
14  49  27  65   4  99
15  91  59  36  17  95

Una vez tenemos esto Lo hemos juntado en un dataframe para que se vea mejor, vamos a añadir una nueva columna a nuestra tabla, con la información del gráfico.

tab03 <- tab01
tab03$sparkline = c(spk_chr(amz),
                    spk_chr(app),
                    spk_chr(fac),
                    spk_chr(goo),
                    spk_chr(mic))
kbl(tab03, escape = F) %>%
  kable_styling(full_width = TRUE)
empresa 2019 (1º) 2018 (4º) dif difpct sparkline
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

La última columna de la tabla muestra los gráficos obtenidos. Nótese que los datos de la última columna han sido calculados con una fuente de datos diferente, por lo que esta tabla está presentando inputs obtenidos de dos fuentes. Por una lado nuestra vieja tabla y por otro lado la nueva información que le incorporamos.

Y finalmente, mostramos casi todo lo que hemos hecho junto, no son cosas separadas.

tab02 <- tab03
tab02[, 2] <-
  ifelse(tab02[, 2] > mean(tab02[, 2]), paste(tab02[, 2], fa("arrow-circle-up", fill =
                                                               "#3CB371")), paste(tab02[, 2], fa("arrow-circle-down", fill = "red")))
kbl(tab02, escape = FALSE) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = F
  ) %>%
  column_spec(2, color = "teal", bold = TRUE) %>%
  column_spec(5,
              color = "white",
              background = ifelse(tab02$difpct > mean(tab01$difpct), "#3CB371", "#FA8072"))
empresa 2019 (1º) 2018 (4º) dif difpct sparkline
Amazon 87400 72400 15000 20.7
Apple 91819 84310 7509 8.9
Facebook 21082 16914 4168 24.6
Google 46075 39276 6799 17.3
Microsoft 36906 32471 4435 13.7

Si deseas consultar más información sobre la librería jQuery Sparklines accede al enlace. Enlace antiguo, pero con muchísima utilidad.