hydrochem Hidroquímica reproducible en R, de la tabla del laboratorio al diagrama de Piper

install.packages("hydrochem") library(hydrochem) data(hc_data); data(data_qc)

Versión 0.1.0 en CRAN (junio de 2026), de G. Cinkus y R. Bondu. Chuleta de referencia rápida; los detalles y los ejemplos completos están en la guía Quarto.

Cationes
Ca27 %
Mg23 %
Na48 %
Aniones
HCO333 %
SO426 %
Cl41 %
Una muestra real de data_qc (S72) en % de meq/L, como la dibuja plot_stabler(): 16,2 meq/L de cationes frente a 17,1 de aniones, facies Na-Cl. Casi todo el paquete gira en torno a esta conversión.

1Prepara la tabla

  • Una fila por muestra y una columna por parámetro (formato ancho).
  • Nombres exactos, con mayúsculas: Ca, no calcio ni CA.
  • Una sola unidad para todos los iones; se declara en base_unit (por defecto, mg/L).
  • Los valores bajo el límite de detección pueden venir como texto: "<0.5".
  • Se pueden añadir otras columnas (punto, fecha, acuífero, grupo).
idCaMgNaKHCO3ClSO4NO3
P-0185.221.418.92.129031.562<1
P-0212035954.531016018028

Columnas que reconoce

CaMgNaK HCO3CO3ClSO4NO3 pHSiO2NO3_NNH4_NPO4_PFeMnSrBrF

cationes mayoritarios aniones mayoritarios otros. Hay 72 en total: hydrochem:::ions_meta.

Los sufijos _N, _P indican concentración expresada como elemento: 10 mg/L de NO3-N son 44,3 mg/L de NO3.

Del formato largo del laboratorio al ancho

datos <- lab_largo |>
  # equiv: c(Calcio = "Ca", ...)
  mutate(parametro = equiv[parametro]) |>
  pivot_wider(names_from = parametro,
              values_from = valor) |>
  replace_bdl()

Datos de ejemplo

hc_data: 59 muestras con los iones mayoritarios en mg/L y un grupo type (A, B, C). No tiene CO3 ni pH.
data_qc: 146 aguas subterráneas de Quebec, con 55 columnas: pH, iones traza con valores censurados, isótopos, condición hidráulica (Hydro_cond) y clúster.

2Depura y controla

replace_bdl(data, method = "DL/2")

Convierte "<0.01" en número: LD/2, LD/√2, LD, 0 o NA. Solo toca las columnas con algún «<».

Devuelve un data.table.
convert_unit(data, ions, to_unit = "meq/L")

Pasa de mg/L a mmol/L o meq/L y viceversa. Añade columnas Ca_meq, Ca_mmol… Con convert_to_species = TRUE, NO3_N pasa a NO3.

meq/L = mg/Lmasa molar × |carga| 80 mg/L de Ca2+ son 4,0 meq/L; 80 mg/L de Na+, 3,5 meq/L.
ionic_balance(data, method = "major")

Error de balance iónico (%). Por electroneutralidad, cationes y aniones en meq/L deberían igualarse. Es el primer filtro antes de interpretar.

EB = 100 × Σcat − ΣanΣcat + Σan Criterio habitual: |EB| ≤ 5 % (hasta 10 % en aguas poco mineralizadas). "major_minor" suma Fe, Mn, Sr, Br, F…
hydrochem::stat_summary(data, params, group, stats)

Resumen descriptivo completo: percentiles, media geométrica, CV, MAD, n.º de datos y de NA, por grupos si se pide.

Usa siempre el prefijo: choca con ggplot2::stat_summary().

3Caracteriza el agua

water_type(data, format = "short")

Facies: catión y anión dominantes en % meq/L, como "Ca-HCO3". Con "long" lista todos los que superan threshold (20 %): "Ca-Mg-HCO3-SO4".

tds(data, method = "major")

Sólidos disueltos como suma de iones en mg/L. Otros métodos: "major_si" (+SiO2) y "all_ions".

No es el residuo seco: al evaporar, el HCO3 conserva solo el 49 % de su masa.
total_hardness(data, output = "mg/L_CaCO3")

Dureza = 50 × (Ca + Mg) en meq/L, en mg/L de CaCO3. También en "fH", "dH" o "e". Por encima de 180 mg/L, agua muy dura.

total_alkalinity(data, output = "meq/L")

HCO3 + 2 CO3 (+ OH − H). Necesita pH, por eso se prueba con data_qc.

Todo en un pipe

qc <- data_qc |>
  replace_bdl() |>
  as_tibble() |>
  mutate(
    eb = ionic_balance(pick(everything())),
    facies = water_type(pick(everything())),
    SAR = sar(pick(everything()))
  ) |>
  filter(abs(eb) <= 5)

plot_piper(qc, group = "Hydro_cond")

Los cálculos devuelven un vector por fila: encajan en mutate() con pick(everything()).

4Índices

Todos se calculan en meq/L (la conversión es interna) y se llaman igual: f(data, base_unit).

ÍndiceFórmula
SAR sar()Na / √((Ca+Mg)/2)
Sodificación del suelo. < 10 excelente; > 26 inadecuada.
%Na percent_sodium()100·Na / (Ca+Mg+Na+K)
Wilcox. < 20 excelente; > 60 dudosa.
Kelly kelly_ratio()Na / (Ca+Mg)
> 1: exceso de sodio.
RSC rsc()(HCO3+CO3) − (Ca+Mg)
Carbonato sódico residual. > 2,5 meq/L inadecuada.
PI permeability_index()100·(Na+√HCO3) / (Ca+Mg+Na)
Doneen. < 25 %: clase III, no apta.
MH magnesium_hazard()100·Mg / (Ca+Mg)
> 50 % perjudicial.
CAI-1 cai1()(Cl − (Na+K)) / Cl
> 0: el agua cede Na al terreno (intercambio inverso, intrusión salina). < 0: lo toma (intercambio directo).
CAI-2 cai2()(Cl − (Na+K)) / (SO4+HCO3+CO3+NO3)
Misma lectura. Necesita NO3 (o NO3_N).

Umbrales orientativos de la bibliografía clásica (Richards 1954, Wilcox 1955, Eaton 1950, Doneen 1964…); no los da el paquete.

Todos de una vez con purrr

ind <- list(SAR = sar, KR = kelly_ratio,
  MH = magnesium_hazard, CAI1 = cai1)
hc_data |>
  select(id, type) |>
  bind_cols(map(ind, \(f) f(hc_data)))

5DibujaTodos devuelven un objeto ggplot: se retocan con + labs() o + theme()