Tutorial: Optimización de Tablas Anchas con gt

Técnicas avanzadas para mejorar la presentación de datos tabulares

Autor/a

Antonio de Lucas Sepúlveda / Claude.ai

Fecha de publicación

18 de octubre de 2025

Introducción

El paquete gt es una herramienta potente para crear tablas de alta calidad en R. Este tutorial se centra en técnicas específicas para optimizar la presentación de tablas anchas, un desafío común en el análisis de datos hidrológicos y ambientales.

# Cargar librerías necesarias
library(tidyverse)
library(gt)
library(paletteer)  # Para paletas de colores adicionales

# Configuración global
options(scipen = 999)  # Evitar notación científica

Datos de ejemplo: Serie temporal hidrológica

Vamos a crear un conjunto de datos que simula mediciones hidrológicas mensuales en diferentes estaciones de aforo.

# Simular datos de caudales mensuales en m³/s
set.seed(2024)

# Crear datos de ejemplo - Serie temporal hidrológica
datos_hidrologicos <- tibble(
  estacion = rep(c("EA001_Río_Tajo", "EA002_Río_Duero", "EA003_Río_Ebro", 
                   "EA004_Río_Guadalquivir", "EA005_Río_Guadiana"), each = 12),
  mes = rep(month.name, 5),
  caudal_medio = runif(60, 10, 500) |> round(2),
  caudal_max = caudal_medio * runif(60, 1.2, 2.5) |> round(2),
  caudal_min = caudal_medio * runif(60, 0.3, 0.8) |> round(2),
  precipitacion = runif(60, 0, 150) |> round(1),
  temperatura = runif(60, 5, 35) |> round(1),
  evaporacion = runif(60, 20, 180) |> round(1),
  nivel_embalse = runif(60, 30, 95) |> round(1),
  calidad_agua = sample(c("Excelente", "Buena", "Aceptable", "Deficiente"), 60, 
                        replace = TRUE, prob = c(0.3, 0.4, 0.2, 0.1))
) |>
  mutate(
    mes = factor(mes, levels = month.name),
    cuenca = str_extract(estacion, "Río_.*") |> str_remove("Río_")
  )

# Transformar a formato ancho para demostrar el problema
datos_anchos <- datos_hidrologicos |>
  select(cuenca, mes, caudal_medio, precipitacion, temperatura) |>
  pivot_wider(
    names_from = mes,
    values_from = c(caudal_medio, precipitacion, temperatura),
    names_glue = "{mes}_{.value}"
  )

glimpse(datos_anchos)
Rows: 5
Columns: 37
$ cuenca                  <chr> "Tajo", "Duero", "Ebro", "Guadalquivir", "Guad…
$ January_caudal_medio    <dbl> 420.10, 354.74, 400.70, 36.52, 330.63
$ February_caudal_medio   <dbl> 167.23, 262.73, 153.23, 483.40, 328.74
$ March_caudal_medio      <dbl> 343.38, 230.23, 311.47, 20.85, 40.05
$ April_caudal_medio      <dbl> 352.10, 422.69, 409.64, 327.21, 373.13
$ May_caudal_medio        <dbl> 233.93, 64.05, 10.26, 437.70, 231.95
$ June_caudal_medio       <dbl> 353.70, 432.98, 20.35, 456.12, 249.85
$ July_caudal_medio       <dbl> 213.70, 74.22, 226.15, 315.70, 379.08
$ August_caudal_medio     <dbl> 158.57, 154.76, 152.24, 202.05, 360.77
$ September_caudal_medio  <dbl> 439.53, 33.16, 96.34, 427.21, 19.75
$ October_caudal_medio    <dbl> 68.33, 64.88, 486.96, 229.61, 104.04
$ November_caudal_medio   <dbl> 451.92, 343.41, 340.83, 213.68, 233.65
$ December_caudal_medio   <dbl> 477.02, 426.50, 343.67, 146.82, 146.32
$ January_precipitacion   <dbl> 16.5, 18.3, 127.7, 43.1, 39.9
$ February_precipitacion  <dbl> 139.3, 12.1, 5.6, 121.1, 71.9
$ March_precipitacion     <dbl> 45.2, 17.9, 82.5, 19.1, 5.0
$ April_precipitacion     <dbl> 111.7, 6.5, 61.0, 21.8, 27.0
$ May_precipitacion       <dbl> 36.1, 73.2, 15.4, 14.7, 145.7
$ June_precipitacion      <dbl> 149.1, 72.1, 125.7, 83.5, 42.9
$ July_precipitacion      <dbl> 113.1, 131.3, 96.9, 130.4, 105.3
$ August_precipitacion    <dbl> 111.5, 137.3, 133.9, 74.5, 139.6
$ September_precipitacion <dbl> 106.2, 148.0, 63.1, 36.2, 59.3
$ October_precipitacion   <dbl> 18.9, 4.6, 69.2, 129.6, 21.2
$ November_precipitacion  <dbl> 2.9, 100.5, 131.4, 73.3, 122.7
$ December_precipitacion  <dbl> 4.0, 18.0, 67.7, 143.9, 140.5
$ January_temperatura     <dbl> 16.9, 9.2, 12.7, 23.8, 29.2
$ February_temperatura    <dbl> 6.2, 20.3, 22.5, 8.6, 6.2
$ March_temperatura       <dbl> 16.1, 9.1, 20.4, 16.5, 28.0
$ April_temperatura       <dbl> 17.2, 17.4, 10.8, 34.6, 30.7
$ May_temperatura         <dbl> 7.9, 13.5, 33.6, 26.3, 33.8
$ June_temperatura        <dbl> 9.3, 13.9, 34.6, 22.6, 27.1
$ July_temperatura        <dbl> 17.7, 25.9, 11.0, 8.2, 21.7
$ August_temperatura      <dbl> 29.7, 5.1, 7.4, 21.9, 16.5
$ September_temperatura   <dbl> 12.2, 10.0, 8.4, 11.4, 29.3
$ October_temperatura     <dbl> 28.8, 6.9, 6.7, 15.9, 27.2
$ November_temperatura    <dbl> 20.3, 21.6, 26.0, 33.8, 30.6
$ December_temperatura    <dbl> 9.3, 26.6, 18.9, 34.6, 10.7

Paso 1: Tabla básica con gt

Comenzamos creando una tabla básica para ver el punto de partida.

# Tabla básica sin formato
tabla_basica <- datos_anchos |>
  gt() |>
  tab_header(
    title = "Datos Hidrológicos Mensuales por Cuenca",
    subtitle = "Caudal medio (m³/s), Precipitación (mm) y Temperatura (°C)"
  )

tabla_basica
Datos Hidrológicos Mensuales por Cuenca
Caudal medio (m³/s), Precipitación (mm) y Temperatura (°C)
cuenca January_caudal_medio February_caudal_medio March_caudal_medio April_caudal_medio May_caudal_medio June_caudal_medio July_caudal_medio August_caudal_medio September_caudal_medio October_caudal_medio November_caudal_medio December_caudal_medio January_precipitacion February_precipitacion March_precipitacion April_precipitacion May_precipitacion June_precipitacion July_precipitacion August_precipitacion September_precipitacion October_precipitacion November_precipitacion December_precipitacion January_temperatura February_temperatura March_temperatura April_temperatura May_temperatura June_temperatura July_temperatura August_temperatura September_temperatura October_temperatura November_temperatura December_temperatura
Tajo 420.10 167.23 343.38 352.10 233.93 353.70 213.70 158.57 439.53 68.33 451.92 477.02 16.5 139.3 45.2 111.7 36.1 149.1 113.1 111.5 106.2 18.9 2.9 4.0 16.9 6.2 16.1 17.2 7.9 9.3 17.7 29.7 12.2 28.8 20.3 9.3
Duero 354.74 262.73 230.23 422.69 64.05 432.98 74.22 154.76 33.16 64.88 343.41 426.50 18.3 12.1 17.9 6.5 73.2 72.1 131.3 137.3 148.0 4.6 100.5 18.0 9.2 20.3 9.1 17.4 13.5 13.9 25.9 5.1 10.0 6.9 21.6 26.6
Ebro 400.70 153.23 311.47 409.64 10.26 20.35 226.15 152.24 96.34 486.96 340.83 343.67 127.7 5.6 82.5 61.0 15.4 125.7 96.9 133.9 63.1 69.2 131.4 67.7 12.7 22.5 20.4 10.8 33.6 34.6 11.0 7.4 8.4 6.7 26.0 18.9
Guadalquivir 36.52 483.40 20.85 327.21 437.70 456.12 315.70 202.05 427.21 229.61 213.68 146.82 43.1 121.1 19.1 21.8 14.7 83.5 130.4 74.5 36.2 129.6 73.3 143.9 23.8 8.6 16.5 34.6 26.3 22.6 8.2 21.9 11.4 15.9 33.8 34.6
Guadiana 330.63 328.74 40.05 373.13 231.95 249.85 379.08 360.77 19.75 104.04 233.65 146.32 39.9 71.9 5.0 27.0 145.7 42.9 105.3 139.6 59.3 21.2 122.7 140.5 29.2 6.2 28.0 30.7 33.8 27.1 21.7 16.5 29.3 27.2 30.6 10.7

Paso 2: Cambiar a fuente Arial Narrow

La fuente Arial Narrow es ideal para tablas anchas porque permite mostrar más información en menos espacio horizontal.

tabla_fuente <- datos_anchos |>
  gt() |>
  tab_header(
    title = "Datos Hidrológicos Mensuales por Cuenca",
    subtitle = "Caudal medio (m³/s), Precipitación (mm) y Temperatura (°C)"
  ) |>
  # Aplicar Arial Narrow a toda la tabla
  tab_options(
    table.font.names = c("Arial Narrow", "Arial", "sans-serif"),
    table.font.size = px(12),
    heading.title.font.size = px(16),
    heading.subtitle.font.size = px(14),
    column_labels.font.size = px(11)
  ) |>
  # Ajustar ancho de columnas
  cols_width(
    cuenca ~ px(100),
    everything() ~ px(65)
  )

tabla_fuente
Datos Hidrológicos Mensuales por Cuenca
Caudal medio (m³/s), Precipitación (mm) y Temperatura (°C)
cuenca January_caudal_medio February_caudal_medio March_caudal_medio April_caudal_medio May_caudal_medio June_caudal_medio July_caudal_medio August_caudal_medio September_caudal_medio October_caudal_medio November_caudal_medio December_caudal_medio January_precipitacion February_precipitacion March_precipitacion April_precipitacion May_precipitacion June_precipitacion July_precipitacion August_precipitacion September_precipitacion October_precipitacion November_precipitacion December_precipitacion January_temperatura February_temperatura March_temperatura April_temperatura May_temperatura June_temperatura July_temperatura August_temperatura September_temperatura October_temperatura November_temperatura December_temperatura
Tajo 420.10 167.23 343.38 352.10 233.93 353.70 213.70 158.57 439.53 68.33 451.92 477.02 16.5 139.3 45.2 111.7 36.1 149.1 113.1 111.5 106.2 18.9 2.9 4.0 16.9 6.2 16.1 17.2 7.9 9.3 17.7 29.7 12.2 28.8 20.3 9.3
Duero 354.74 262.73 230.23 422.69 64.05 432.98 74.22 154.76 33.16 64.88 343.41 426.50 18.3 12.1 17.9 6.5 73.2 72.1 131.3 137.3 148.0 4.6 100.5 18.0 9.2 20.3 9.1 17.4 13.5 13.9 25.9 5.1 10.0 6.9 21.6 26.6
Ebro 400.70 153.23 311.47 409.64 10.26 20.35 226.15 152.24 96.34 486.96 340.83 343.67 127.7 5.6 82.5 61.0 15.4 125.7 96.9 133.9 63.1 69.2 131.4 67.7 12.7 22.5 20.4 10.8 33.6 34.6 11.0 7.4 8.4 6.7 26.0 18.9
Guadalquivir 36.52 483.40 20.85 327.21 437.70 456.12 315.70 202.05 427.21 229.61 213.68 146.82 43.1 121.1 19.1 21.8 14.7 83.5 130.4 74.5 36.2 129.6 73.3 143.9 23.8 8.6 16.5 34.6 26.3 22.6 8.2 21.9 11.4 15.9 33.8 34.6
Guadiana 330.63 328.74 40.05 373.13 231.95 249.85 379.08 360.77 19.75 104.04 233.65 146.32 39.9 71.9 5.0 27.0 145.7 42.9 105.3 139.6 59.3 21.2 122.7 140.5 29.2 6.2 28.0 30.7 33.8 27.1 21.7 16.5 29.3 27.2 30.6 10.7

Paso 3: Organizar columnas por categorías

Vamos a agrupar las columnas por tipo de variable para mejorar la legibilidad.

# Reorganizar datos para mejor agrupación
datos_categorias <- datos_hidrologicos |>
  select(cuenca, mes, caudal_medio, precipitacion, temperatura) |>
  mutate(mes = as.character(mes))

tabla_categorias <- datos_categorias |>
  gt(groupname_col = "cuenca") |>
  tab_header(
    title = "Análisis Hidrológico Mensual",
    subtitle = "Principales variables por cuenca hidrográfica"
  ) |>
  # Formato de fuente
  tab_options(
    table.font.names = c("Arial Narrow", "Arial", "sans-serif"),
    table.font.size = px(11),
    heading.title.font.size = px(18),
    heading.subtitle.font.size = px(14),
    row_group.font.weight = "bold",
    row_group.background.color = "#E8F4F8"
  ) |>
  # Etiquetas de columnas
  cols_label(
    mes = "Mes",
    caudal_medio = "Caudal (m³/s)",
    precipitacion = "Precip. (mm)",
    temperatura = "Temp. (°C)"
  ) |>
  # Formato de números
  fmt_number(
    columns = c(caudal_medio, precipitacion, temperatura),
    decimals = 1,
    sep_mark = ".",
    dec_mark = ","
  )

tabla_categorias
Análisis Hidrológico Mensual
Principales variables por cuenca hidrográfica
Mes Caudal (m³/s) Precip. (mm) Temp. (°C)
Tajo
January 420,1 16,5 16,9
February 167,2 139,3 6,2
March 343,4 45,2 16,1
April 352,1 111,7 17,2
May 233,9 36,1 7,9
June 353,7 149,1 9,3
July 213,7 113,1 17,7
August 158,6 111,5 29,7
September 439,5 106,2 12,2
October 68,3 18,9 28,8
November 451,9 2,9 20,3
December 477,0 4,0 9,3
Duero
January 354,7 18,3 9,2
February 262,7 12,1 20,3
March 230,2 17,9 9,1
April 422,7 6,5 17,4
May 64,0 73,2 13,5
June 433,0 72,1 13,9
July 74,2 131,3 25,9
August 154,8 137,3 5,1
September 33,2 148,0 10,0
October 64,9 4,6 6,9
November 343,4 100,5 21,6
December 426,5 18,0 26,6
Ebro
January 400,7 127,7 12,7
February 153,2 5,6 22,5
March 311,5 82,5 20,4
April 409,6 61,0 10,8
May 10,3 15,4 33,6
June 20,4 125,7 34,6
July 226,2 96,9 11,0
August 152,2 133,9 7,4
September 96,3 63,1 8,4
October 487,0 69,2 6,7
November 340,8 131,4 26,0
December 343,7 67,7 18,9
Guadalquivir
January 36,5 43,1 23,8
February 483,4 121,1 8,6
March 20,9 19,1 16,5
April 327,2 21,8 34,6
May 437,7 14,7 26,3
June 456,1 83,5 22,6
July 315,7 130,4 8,2
August 202,1 74,5 21,9
September 427,2 36,2 11,4
October 229,6 129,6 15,9
November 213,7 73,3 33,8
December 146,8 143,9 34,6
Guadiana
January 330,6 39,9 29,2
February 328,7 71,9 6,2
March 40,0 5,0 28,0
April 373,1 27,0 30,7
May 231,9 145,7 33,8
June 249,8 42,9 27,1
July 379,1 105,3 21,7
August 360,8 139,6 16,5
September 19,8 59,3 29,3
October 104,0 21,2 27,2
November 233,7 122,7 30,6
December 146,3 140,5 10,7

Paso 4: Efecto cebra o pijama

Aplicamos diferentes estilos de sombreado para mejorar la legibilidad.

# Versión 1: Cebra simple (filas alternas)
tabla_cebra_simple <- datos_categorias |>
  gt(groupname_col = "cuenca") |>
  tab_header(
    title = "Análisis Hidrológico - Estilo Cebra Simple",
    subtitle = "Sombreado en filas alternas"
  ) |>
  # Fuente Arial Narrow
  tab_options(
    table.font.names = c("Arial Narrow", "Arial", "sans-serif"),
    table.font.size = px(11),
    row_group.font.weight = "bold",
    row_group.background.color = "#2C5F7C",
  )  |>
# Usar tab_style() para el color del texto:
  tab_style(
    style = cell_text(color = "white"),
    locations = cells_row_groups()  # ✅ Aplicar a los grupos de filas
  ) |>
  # Efecto cebra en filas pares
  tab_style(
    style = cell_fill(color = "#F0F8FC"),
    locations = cells_body(
      rows = seq(2, nrow(datos_categorias), 2)
    )
  ) |>
  cols_label(
    mes = "Mes",
    caudal_medio = "Caudal (m³/s)",
    precipitacion = "Precip. (mm)",
    temperatura = "Temp. (°C)"
  ) |>
  fmt_number(
    columns = c(caudal_medio, precipitacion, temperatura),
    decimals = 1
  )

tabla_cebra_simple
Análisis Hidrológico - Estilo Cebra Simple
Sombreado en filas alternas
Mes Caudal (m³/s) Precip. (mm) Temp. (°C)
Tajo
January 420.1 16.5 16.9
February 167.2 139.3 6.2
March 343.4 45.2 16.1
April 352.1 111.7 17.2
May 233.9 36.1 7.9
June 353.7 149.1 9.3
July 213.7 113.1 17.7
August 158.6 111.5 29.7
September 439.5 106.2 12.2
October 68.3 18.9 28.8
November 451.9 2.9 20.3
December 477.0 4.0 9.3
Duero
January 354.7 18.3 9.2
February 262.7 12.1 20.3
March 230.2 17.9 9.1
April 422.7 6.5 17.4
May 64.0 73.2 13.5
June 433.0 72.1 13.9
July 74.2 131.3 25.9
August 154.8 137.3 5.1
September 33.2 148.0 10.0
October 64.9 4.6 6.9
November 343.4 100.5 21.6
December 426.5 18.0 26.6
Ebro
January 400.7 127.7 12.7
February 153.2 5.6 22.5
March 311.5 82.5 20.4
April 409.6 61.0 10.8
May 10.3 15.4 33.6
June 20.4 125.7 34.6
July 226.2 96.9 11.0
August 152.2 133.9 7.4
September 96.3 63.1 8.4
October 487.0 69.2 6.7
November 340.8 131.4 26.0
December 343.7 67.7 18.9
Guadalquivir
January 36.5 43.1 23.8
February 483.4 121.1 8.6
March 20.9 19.1 16.5
April 327.2 21.8 34.6
May 437.7 14.7 26.3
June 456.1 83.5 22.6
July 315.7 130.4 8.2
August 202.1 74.5 21.9
September 427.2 36.2 11.4
October 229.6 129.6 15.9
November 213.7 73.3 33.8
December 146.8 143.9 34.6
Guadiana
January 330.6 39.9 29.2
February 328.7 71.9 6.2
March 40.0 5.0 28.0
April 373.1 27.0 30.7
May 231.9 145.7 33.8
June 249.8 42.9 27.1
July 379.1 105.3 21.7
August 360.8 139.6 16.5
September 19.8 59.3 29.3
October 104.0 21.2 27.2
November 233.7 122.7 30.6
December 146.3 140.5 10.7

Paso 5: Efecto cebra en intervalo específico

Aplicamos el efecto cebra solo a un rango determinado de filas y añadimos resaltado condicional.

# Preparar datos con resumen
datos_con_resumen <- datos_hidrologicos |>
  group_by(cuenca) |>
  summarise(
    caudal_anual = mean(caudal_medio, na.rm = TRUE),
    precip_anual = sum(precipitacion, na.rm = TRUE),
    temp_media = mean(temperatura, na.rm = TRUE),
    .groups = "drop"
  ) |>
  arrange(desc(caudal_anual))

tabla_cebra_intervalo <- datos_con_resumen |>
  gt() |>
  tab_header(
    title = "Resumen Anual por Cuenca",
    subtitle = "Efecto cebra aplicado a intervalo específico (filas 2-4)"
  ) |>
  # Configuración de fuente
  tab_options(
    table.font.names = c("Arial Narrow", "Arial", "sans-serif"),
    table.font.size = px(12),
    heading.title.font.size = px(16),
    heading.subtitle.font.size = px(14)
  ) |>
  # Etiquetas
  cols_label(
    cuenca = "Cuenca",
    caudal_anual = "Caudal Medio Anual (m³/s)",
    precip_anual = "Precipitación Total (mm)",
    temp_media = "Temperatura Media (°C)"
  ) |>
  # Formato de números
  fmt_number(
    columns = c(caudal_anual, precip_anual, temp_media),
    decimals = 1,
    sep_mark = ".",
    dec_mark = ","
  ) |>
  # Efecto cebra solo en filas 2 a 4
  tab_style(
    style = cell_fill(color = "#E8F4F8"),
    locations = cells_body(rows = c(2, 4))
  ) |>
  # Resaltar valores máximos
  tab_style(
    style = list(
      cell_fill(color = "#FFE5B4"),
      cell_text(weight = "bold")
    ),
    locations = cells_body(
      columns = caudal_anual,
      rows = caudal_anual == max(caudal_anual)
    )
  )

tabla_cebra_intervalo
Resumen Anual por Cuenca
Efecto cebra aplicado a intervalo específico (filas 2-4)
Cuenca Caudal Medio Anual (m³/s) Precipitación Total (mm) Temperatura Media (°C)
Tajo 306,6 854,5 16,0
Guadalquivir 274,7 891,2 21,5
Ebro 246,0 980,1 17,8
Duero 238,7 739,8 15,0
Guadiana 233,2 921,0 24,2

Paso 6: Tabla avanzada con múltiples mejoras

Combinamos todas las técnicas anteriores y añadimos elementos adicionales.

# Crear datos más complejos para demostración
datos_completos <- datos_hidrologicos |>
  group_by(cuenca, trimestre = quarter(match(mes, month.name))) |>
  summarise(
    caudal_medio = mean(caudal_medio, na.rm = TRUE),
    caudal_max_trim = max(caudal_max, na.rm = TRUE),
    caudal_min_trim = min(caudal_min, na.rm = TRUE),
    precipitacion = sum(precipitacion, na.rm = TRUE),
    temperatura = mean(temperatura, na.rm = TRUE),
    evaporacion = sum(evaporacion, na.rm = TRUE),
    nivel_embalse = mean(nivel_embalse, na.rm = TRUE),
    .groups = "drop"
  ) |>
  mutate(
    trimestre = paste0("T", trimestre),
    balance_hidrico = precipitacion - evaporacion,
    indice_sequia = case_when(
      balance_hidrico < -100 ~ "Sequía severa",
      balance_hidrico < 0 ~ "Déficit",
      balance_hidrico < 100 ~ "Normal",
      TRUE ~ "Excedente"
    )
  )

tabla_avanzada <- datos_completos |>
  gt(groupname_col = "cuenca") |>
  # Encabezado
  tab_header(
    title = md("**Sistema de Información Hidrológica**"),
    subtitle = md("*Análisis trimestral con indicadores de gestión*")
  ) |>
  # Spanners para agrupar columnas relacionadas
  tab_spanner(
    label = "CAUDALES (m³/s)",
    columns = c(caudal_medio, caudal_max_trim, caudal_min_trim)
  ) |>
  tab_spanner(
    label = "BALANCE HÍDRICO",
    columns = c(precipitacion, evaporacion, balance_hidrico)
  ) |>
  tab_spanner(
    label = "INDICADORES",
    columns = c(nivel_embalse, indice_sequia)
  ) |>
  # Etiquetas personalizadas
  cols_label(
    trimestre = "Periodo",
    caudal_medio = "Media",
    caudal_max_trim = "Máx",
    caudal_min_trim = "Mín",
    precipitacion = "P (mm)",
    evaporacion = "E (mm)",
    balance_hidrico = "P-E",
    temperatura = "T (°C)",
    nivel_embalse = "Embalse (%)",
    indice_sequia = "Estado"
  ) |>
  # Formato de números
  fmt_number(
    columns = c(caudal_medio, caudal_max_trim, caudal_min_trim, 
                precipitacion, evaporacion, balance_hidrico,
                temperatura, nivel_embalse),
    decimals = 1,
    sep_mark = ".",
    dec_mark = ","
  ) |>
  # Ocultar columna de temperatura (ejemplo de optimización)
  cols_hide(columns = temperatura) |>
  # Estilos y colores
  tab_options(
    table.font.names = c("Arial Narrow", "Arial", "sans-serif"),
    table.font.size = px(11),
    heading.title.font.size = px(18),
    heading.subtitle.font.size = px(14),
    heading.background.color = "#1E3A5F",
    column_labels.background.color = "#4A7BA7",
    column_labels.font.weight = "bold",
    row_group.background.color = "#2C5F7C",
    row_group.font.weight = "bold",
    table.border.top.width = px(3),
    table.border.top.color = "#1E3A5F",
    table.border.bottom.width = px(3),
    table.border.bottom.color = "#1E3A5F"
  ) |>
  tab_style(
    style = cell_text(color = "white"),
    locations = cells_title(groups = c("title", "subtitle"))
  ) |> 
  tab_style(
    style = cell_text(color = "white"),
    locations = cells_column_labels()
  ) |> 
# Efecto cebra en todas las filas de datos
  tab_style(
    style = cell_fill(color = "#F5FBFF"),
    locations = cells_body(rows = seq(2, nrow(datos_completos), 2))
  ) |>
  # Colorear según el índice de sequía
  tab_style(
    style = list(
      cell_fill(color = "#FFD6D6"),
      cell_text(weight = "bold", color = "#8B0000")
    ),
    locations = cells_body(
      columns = indice_sequia,
      rows = indice_sequia == "Sequía severa"
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#FFF4E6"),
      cell_text(color = "#CC6600")
    ),
    locations = cells_body(
      columns = indice_sequia,
      rows = indice_sequia == "Déficit"
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#E6F7E6"),
      cell_text(color = "#006400")
    ),
    locations = cells_body(
      columns = indice_sequia,
      rows = indice_sequia == "Excedente"
    )
  ) |>
  # Resaltar valores extremos de caudal
  tab_style(
    style = list(
      cell_text(weight = "bold", color = "#0066CC")
    ),
    locations = cells_body(
      columns = caudal_max_trim,
      rows = caudal_max_trim > quantile(caudal_max_trim, 0.9)
    )
  ) |>
  # Nota al pie
  tab_footnote(
    footnote = "P: Precipitación, E: Evapotranspiración, P-E: Balance hídrico",
    locations = cells_column_spanners(spanners = "BALANCE HÍDRICO")
  ) |>
  tab_footnote(
    footnote = "Datos simulados para demostración",
    locations = cells_title(groups = "title")
  )

tabla_avanzada
Sistema de Información Hidrológica1
Análisis trimestral con indicadores de gestión
Periodo
CAUDALES (m³/s)
BALANCE HÍDRICO2
INDICADORES
Media Máx Mín P (mm) E (mm) P-E Embalse (%) Estado
Duero
T1 282,6 716,6 110,0 48,3 277,9 −229,6 56,6 Sequía severa
T2 306,6 896,1 41,0 151,8 266,8 −115,0 50,4 Sequía severa
T3 87,4 230,6 23,0 416,6 319,5 97,1 54,1 Normal
T4 278,3 635,3 31,1 123,1 389,5 −266,4 54,9 Sequía severa
Ebro
T1 288,5 841,5 122,6 215,8 353,4 −137,6 73,1 Sequía severa
T2 146,8 655,4 6,1 202,1 300,8 −98,7 71,4 Déficit
T3 158,2 434,2 73,2 293,9 347,2 −53,3 68,8 Déficit
T4 390,5 739,6 178,7 268,3 268,3 0,0 66,5 Normal
Guadalquivir
T1 180,3 899,1 11,5 183,3 504,6 −321,3 71,6 Sequía severa
T2 407,0 944,2 124,3 120,0 359,3 −239,3 76,6 Sequía severa
T3 315,0 657,9 76,8 241,1 389,0 −147,9 71,0 Sequía severa
T4 196,7 521,2 68,4 346,8 277,5 69,3 56,1 Normal
Guadiana
T1 233,1 736,4 29,2 116,8 390,5 −273,7 68,1 Sequía severa
T2 285,0 750,0 118,3 215,6 411,5 −195,9 54,0 Sequía severa
T3 253,2 855,0 9,9 304,2 308,2 −4,0 77,1 Déficit
T4 161,3 427,6 31,2 284,4 360,5 −76,1 54,7 Déficit
Tajo
T1 310,2 576,9 55,2 201,0 258,5 −57,5 60,9 Déficit
T2 313,2 799,4 109,6 296,9 345,8 −48,9 78,7 Déficit
T3 270,6 703,2 112,6 330,8 334,5 −3,7 55,7 Déficit
T4 332,4 926,4 54,7 25,8 238,0 −212,2 81,5 Sequía severa
1 Datos simulados para demostración
2 P: Precipitación, E: Evapotranspiración, P-E: Balance hídrico

Paso 7: Técnicas adicionales para tablas muy anchas

Para tablas extremadamente anchas, aquí hay técnicas adicionales:

# Crear una tabla muy ancha con datos mensuales completos
datos_muy_anchos <- datos_hidrologicos |>
  filter(str_detect(estacion, "EA001|EA002")) |>
  select(cuenca, mes, caudal_medio, precipitacion) |>
  pivot_wider(
    names_from = mes,
    values_from = c(caudal_medio, precipitacion),
    names_glue = "{mes}_{.value}"
  )

tabla_optimizada <- datos_muy_anchos |>
  gt() |>
  tab_header(
    title = "Optimización para Tablas Muy Anchas",
    subtitle = "Técnicas de compresión visual"
  ) |>
  # Configuración extrema para ahorro de espacio
  tab_options(
    table.font.names = c("Arial Narrow", "Helvetica Narrow", "sans-serif"),
    table.font.size = px(9),
    data_row.padding = px(2),  # Reducir padding
    column_labels.font.size = px(8),
    column_labels.padding = px(3),
    heading.title.font.size = px(14),
    heading.subtitle.font.size = px(11)
  ) |>
  # Ancho mínimo de columnas
  cols_width(
    cuenca ~ px(80),
    everything() ~ px(45)
  ) |>
  # Rotar etiquetas de columnas para ahorrar espacio horizontal
  tab_style(
    style = css(transform = "rotate(-45deg)", 
                "white-space" = "nowrap",
                "text-align" = "left"),
    locations = cells_column_labels(
      columns = -cuenca
    )
  ) |>
  # Formato compacto de números
  fmt_number(
    columns = -cuenca,
    decimals = 0,
    suffixing = TRUE  # Usar notación K, M para números grandes
  ) |>
  # Colores alternados por grupos de columnas
  tab_style(
    style = cell_fill(color = "#E8F0FE"),
    locations = cells_body(
      columns = contains("January") | contains("March") | 
                contains("May") | contains("July") |
                contains("September") | contains("November")
    )
  ) |>
  tab_style(
    style = cell_fill(color = "#FFF3E0"),
    locations = cells_body(
      columns = contains("February") | contains("April") | 
                contains("June") | contains("August") |
                contains("October") | contains("December")
    )
  )

tabla_optimizada
Optimización para Tablas Muy Anchas
Técnicas de compresión visual
cuenca January_caudal_medio February_caudal_medio March_caudal_medio April_caudal_medio May_caudal_medio June_caudal_medio July_caudal_medio August_caudal_medio September_caudal_medio October_caudal_medio November_caudal_medio December_caudal_medio January_precipitacion February_precipitacion March_precipitacion April_precipitacion May_precipitacion June_precipitacion July_precipitacion August_precipitacion September_precipitacion October_precipitacion November_precipitacion December_precipitacion
Tajo 420 167 343 352 234 354 214 159 440 68 452 477 16 139 45 112 36 149 113 112 106 19 3 4
Duero 355 263 230 423 64 433 74 155 33 65 343 426 18 12 18 6 73 72 131 137 148 5 100 18

Paso 8: Función personalizada para aplicar estilos

Creamos una función reutilizable para aplicar nuestros estilos preferidos.

# Función para aplicar estilo hidrológico estándar
aplicar_estilo_hidrologico <- function(tabla_gt, 
                                       efecto_cebra = TRUE,
                                       filas_cebra = "pares",
                                       fuente = "Arial Narrow",
                                       tamano_fuente = 11) {
  
  # Aplicar configuración base
  tabla_estilizada <- tabla_gt |>
    tab_options(
      table.font.names = c(fuente, "Arial", "sans-serif"),
      table.font.size = px(tamano_fuente),
      heading.title.font.size = px(tamano_fuente + 5),
      heading.subtitle.font.size = px(tamano_fuente + 2),
      heading.background.color = "#1E3A5F",
      column_labels.background.color = "#4A7BA7",
      column_labels.font.weight = "bold",
      table.border.top.width = px(2),
      table.border.top.color = "#1E3A5F"
      ) |> 
    tab_style(
      style = cell_text(color = "white"),
      locations = cells_title(groups = c("title", "subtitle"))
      ) |> 
    tab_style(
      style = cell_text(color = "white"),
      locations = cells_column_labels()
      )  

  n_filas <- tabla_gt$`_data` |> nrow()
  # Aplicar efecto cebra si está activado
  if (efecto_cebra) {
    if (all(filas_cebra == "pares")) {
      filas_a_colorear <- seq(2, n_filas * 2, 2)  # toma el número de filas. Multiplico por 2 por asegurar en caso de encabezados de grupo
    } else if (all(filas_cebra == "impares")) {
      filas_a_colorear <- seq(1, n_filas * 2, 2)
    } else if (is.numeric(filas_cebra)) {
      filas_a_colorear <- filas_cebra
    }
    
    tabla_estilizada <- tabla_estilizada |>
      tab_style(
        style = cell_fill(color = "#F0F8FC"),
        locations = cells_body(rows = filas_a_colorear)
      )
  }
  
  return(tabla_estilizada)
}

# Ejemplo de uso de la función
datos_ejemplo <- datos_hidrologicos |>
  filter(mes %in% c("January", "February", "March")) |>
  group_by(cuenca) |>
  summarise(
    caudal_medio = mean(caudal_medio),
    precipitacion = sum(precipitacion),
    temperatura = mean(temperatura),
    .groups = "drop"
  )

tabla_con_funcion <- datos_ejemplo |>
  gt() |>
  tab_header(
    title = "Tabla con Estilo Personalizado",
    subtitle = "Usando función reutilizable"
  ) |>
  cols_label(
    cuenca = "Cuenca",
    caudal_medio = "Q medio (m³/s)",
    precipitacion = "P total (mm)",
    temperatura = "T media (°C)"
  ) |>
  fmt_number(
    columns = where(is.numeric),
    decimals = 1,
    sep_mark = ".",
    dec_mark = ","
  ) |>
  aplicar_estilo_hidrologico(
    efecto_cebra = TRUE,
    filas_cebra = c(1, 3, 5),  # Solo filas impares
    tamano_fuente = 10
  )

tabla_con_funcion
Tabla con Estilo Personalizado
Usando función reutilizable
Cuenca Q medio (m³/s) P total (mm) T media (°C)
Duero 282,6 48,3 12,9
Ebro 288,5 215,8 18,5
Guadalquivir 180,3 183,3 16,3
Guadiana 233,1 116,8 21,1
Tajo 310,2 201,0 13,1

Consejos finales para tablas anchas

1. Optimización del espacio horizontal

  • Usar fuentes condensadas (Arial Narrow, Helvetica Narrow)
  • Reducir el padding de las celdas
  • Abreviar nombres de columnas cuando sea posible
  • Considerar transponer la tabla si tiene pocas filas y muchas columnas

2. Mejora de la legibilidad

  • Aplicar efectos cebra para distinguir filas
  • Usar colores sutiles para agrupar columnas relacionadas
  • Implementar bordes verticales solo cuando sea necesario
  • Destacar valores importantes con formato condicional

3. Organización de la información

  • Agrupar columnas relacionadas con tab_spanner()
  • Usar filas de grupo para categorizar datos
  • Ocultar columnas menos importantes con cols_hide()
  • Considerar crear múltiples tablas más pequeñas en lugar de una muy ancha

4. Exportación y visualización

# Guardar como HTML
gtsave(tabla_avanzada, "tabla_hidrologica.html")

# Guardar como imagen PNG (requiere webshot2)
# install.packages("webshot2")
# webshot2::install_chromote()
gtsave(tabla_avanzada, "tabla_hidrologica.png", 
       expand = 10)  # Margen adicional

# Guardar como Word (a través de flextable)
# library(flextable)
# tabla_avanzada |>
#   as_flex_table() |>
#   save_as_docx(path = "tabla_hidrologica.docx")

Ejemplo integrado: Dashboard de cuenca

Para finalizar, aquí hay un ejemplo que combina todas las técnicas para crear una tabla tipo dashboard:

# Preparar datos de resumen ejecutivo
resumen_cuencas <- datos_hidrologicos |>
  group_by(cuenca) |>
  summarise(
    q_medio_anual = mean(caudal_medio),
    q_max_anual = max(caudal_max),
    q_min_anual = min(caudal_min),
    p_total = sum(precipitacion),
    t_media = mean(temperatura),
    nivel_medio = mean(nivel_embalse),
    meses_sequia = sum(precipitacion < 20),
    .groups = "drop"
  ) |>
  mutate(
    estado = case_when(
      nivel_medio > 70 ~ "Óptimo",
      nivel_medio > 50 ~ "Normal",
      nivel_medio > 30 ~ "Alerta",
      TRUE ~ "Crítico"
    ),
    tendencia = sample(c("↑", "↓", "→"), n(), replace = TRUE)
  )

dashboard_final <- resumen_cuencas |>
  gt() |>
  tab_header(
    title = md("**DASHBOARD HIDROLÓGICO ANUAL**"),
    subtitle = md("*Sistema integrado de monitoreo de cuencas - Año 2024*")
  ) |>
  tab_spanner(
    label = md("**CAUDALES** (m³/s)"),
    columns = c(q_medio_anual, q_max_anual, q_min_anual)
  ) |>
  tab_spanner(
    label = md("**METEOROLOGÍA**"),
    columns = c(p_total, t_media)
  ) |>
  tab_spanner(
    label = md("**GESTIÓN**"),
    columns = c(nivel_medio, meses_sequia, estado, tendencia)
  ) |>
  cols_label(
    cuenca = "",
    q_medio_anual = "Media",
    q_max_anual = "Máximo",
    q_min_anual = "Mínimo",
    p_total = "P anual (mm)",
    t_media = "T media (°C)",
    nivel_medio = "Embalse (%)",
    meses_sequia = "Meses sequía",
    estado = "Estado",
    tendencia = "Tend."
  ) |>
  # Formato de números
  fmt_number(
    columns = c(q_medio_anual, q_max_anual, q_min_anual, p_total, t_media, nivel_medio),
    decimals = 1,
    sep_mark = ".",
    dec_mark = ","
  ) |>
  # Estilos base
  tab_options(
    table.font.names = c("Arial Narrow", "Arial", "sans-serif"),
    table.font.size = px(11),
    heading.title.font.size = px(20),
    heading.subtitle.font.size = px(14),
    heading.background.color = "#0D47A1",
    heading.padding = px(10),
    column_labels.background.color = "#1976D2",
    column_labels.font.weight = "bold",
    column_labels.padding = px(8),
    data_row.padding = px(5),
    table.border.top.width = px(3),
    table.border.top.color = "#0D47A1",
    table.border.bottom.width = px(3),
    table.border.bottom.color = "#0D47A1"
  ) |> 
  tab_style(
    style = cell_text(color = "white"),
    locations = cells_title(groups = c("title", "subtitle"))
  ) |> 
  tab_style(
    style = cell_text(color = "white"),
    locations = cells_column_labels()
  ) |>
  # Efecto cebra sutil
  tab_style(
    style = cell_fill(color = "#F5F9FF"),
    locations = cells_body(rows = seq(2, nrow(resumen_cuencas), 2))
  ) |>
  # Colorear columna de estado
  tab_style(
    style = list(
      cell_fill(color = "#C8E6C9"),
      cell_text(weight = "bold", color = "#1B5E20")
    ),
    locations = cells_body(
      columns = estado,
      rows = estado == "Óptimo"
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#FFF9C4"),
      cell_text(color = "#F57C00")
    ),
    locations = cells_body(
      columns = estado,
      rows = estado == "Normal"
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#FFE0B2"),
      cell_text(weight = "bold", color = "#E65100")
    ),
    locations = cells_body(
      columns = estado,
      rows = estado == "Alerta"
    )
  ) |>
  tab_style(
    style = list(
      cell_fill(color = "#FFCDD2"),
      cell_text(weight = "bold", color = "#B71C1C")
    ),
    locations = cells_body(
      columns = estado,
      rows = estado == "Crítico"
    )
  ) |>
  # Resaltar valores extremos
  tab_style(
    style = list(
      cell_text(color = "#0D47A1", weight = "bold")
    ),
    locations = cells_body(
      columns = q_max_anual,
      rows = q_max_anual == max(q_max_anual)
    )
  ) |>
  tab_style(
    style = list(
      cell_text(color = "#B71C1C", weight = "bold")
    ),
    locations = cells_body(
      columns = meses_sequia,
      rows = meses_sequia > 3
    )
  ) |>
  # Notas al pie
  tab_footnote(
    footnote = md("**Nota:** Datos actualizados al último día del mes"),
    locations = cells_title(groups = "subtitle")
  ) |>
  tab_source_note(
    source_note = md("*Fuente: Sistema Nacional de Información Hidrológica | Procesado con R + gt*")
  )

dashboard_final
DASHBOARD HIDROLÓGICO ANUAL
Sistema integrado de monitoreo de cuencas - Año 20241
CAUDALES (m³/s)
METEOROLOGÍA
GESTIÓN
Media Máximo Mínimo P anual (mm) T media (°C) Embalse (%) Meses sequía Estado Tend.
Duero 238,7 896,1 23,0 739,8 15,0 54,0 6 Normal
Ebro 246,0 841,5 6,1 980,1 17,8 70,0 2 Normal
Guadalquivir 274,7 944,2 11,5 891,2 21,5 68,8 2 Normal
Guadiana 233,2 855,0 9,9 921,0 24,2 63,5 1 Normal
Tajo 306,6 926,4 54,7 854,5 16,0 69,2 4 Normal
Fuente: Sistema Nacional de Información Hidrológica | Procesado con R + gt
1 Nota: Datos actualizados al último día del mes

Conclusión

El paquete gt ofrece una amplia gama de opciones para optimizar la presentación de tablas anchas. Las técnicas clave incluyen:

  1. Tipografía condensada para maximizar el uso del espacio
  2. Agrupación lógica de columnas y filas
  3. Efectos visuales como el sombreado cebra para mejorar la legibilidad
  4. Formato condicional para destacar información crítica
  5. Personalización avanzada mediante funciones reutilizables

Estas herramientas son especialmente valiosas en el contexto de la gestión hidrológica, donde frecuentemente trabajamos con series temporales extensas y múltiples variables que requieren una presentación clara y profesional.


Tutorial desarrollado para la aplicación de ciencia de datos en hidrología y gestión de recursos hídricos.