--- title: "Limpiar con un plan auditable" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Limpiar con un plan auditable} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") # El ancho se restaura al terminar la vinneta: knitr corre cada una en su propia # sesion, asi que no se filtra, pero el CRAN Cookbook pide devolver toda opcion # que se cambie y no cuesta nada cumplirlo. .opciones_previas <- options(width = 90) ``` Diagnosticar no autoriza a modificar datos. En `lupa`, el producto de la etapa de mejora es primero un plan inspeccionable, filtrable y editable. ```{r} library(lupa) data(datos_administrativos) perfil <- perfilar(datos_administrativos) plan <- planificar_limpieza(perfil, datos_administrativos) plan[, c( "id_accion", "grupo", "columna", "estrategia", "recomendada", "aplicar", "reversible", "estado" )] ``` ## Recomendaciones y decisiones de dominio Una acción se recomienda sólo si es correcta con independencia del dominio: por ejemplo, convertir un sentinela textual inequívoco a `NA` o quitar espacios de borde. Normalizar mayúsculas, convertir un sentinela numérico o winsorizar un valor extremo exige contexto y queda desactivado. La justificación acompaña cada alternativa. ```{r} plan[, c("estrategia", "justificacion")] ``` `normalizacion()` permite declarar qué diferencias se ignoran al comparar. El perfil cambia sólo la representación de comparación; nunca modifica los datos. Los pasos de ligaduras y ancho completo se aplican por punto de código y los bytes UTF-8 válidos se declaran antes de operar, así que cumplen igual bajo un locale UTF-8 y bajo `LC_CTYPE = "C"`. ```{r} normalizacion(acentos = FALSE, puntuacion = TRUE) ``` ## Grupos mutuamente excluyentes Las alternativas de un mismo hallazgo comparten `grupo`. Como máximo una puede tener `aplicar = TRUE`. `decision_grupo` distingue un grupo pendiente de una decisión explícita de no actuar; no se inventa una fila ficticia de “no hacer nada”. ```{r} grupos <- plan[!is.na(plan$grupo), c( "grupo", "estrategia", "recomendada", "aplicar", "decision_grupo" )] grupos ``` El modo guiado es una capa opcional. Fuera de una sesión interactiva devuelve el plan sin bloquear; en una consola recorre sólo decisiones pendientes o riesgosas. ```{r} identical(guiar_limpieza(plan, datos_administrativos), plan) ``` ## Aplicar conserva evidencia ```{r} copia <- datos_administrativos resultado <- aplicar(plan, datos_administrativos) identical(datos_administrativos, copia) resultado$registro[, c( "estrategia", "n_cambiadas", "n_filas_eliminadas", "n_columnas_eliminadas" )] ``` El plan se puede editar antes de aplicar: quitar una acción, corregir un parámetro o cambiar `n_afectadas` no altera la obligación de observar qué pasó. Si una acción seleccionada no cambia ningún valor, el registro dice `fallida` con su motivo, incluso cuando el estimado está ausente (`NA`). Las acciones destructivas nunca son recomendadas. Aunque el usuario las active en el plan, `aplicar()` exige además `permitir_eliminacion = TRUE`; lo retirado se conserva en `resultado$eliminados` salvo decisión contraria. Sin ese consentimiento la llamada se niega **antes de tocar nada**: no corre ninguna acción del plan, tampoco las que no eliminan, y el error dice cuáles son los dos caminos. `n_no_reversibles` no se lee por el nombre de la acción sino por lo que quedó en el resultado: una normalización que funde dos valores que eran distintos —pasar `ana` y `ANA` a `ANA`— cuenta esas celdas, porque lo que las separaba no quedó en ningún lado. ## Lo que el plan no cubre lo dice Leer tres acciones no significa que lo demás esté bien, así que el plan declara sus huecos en tres atributos: `cobertura_diagnosticos` —los diagnósticos que el perfil no pudo evaluar—, `hallazgos_sin_accion_por_columna_ambigua` —los hallazgos cuya columna comparte nombre con otra, así que no hay forma de saber sobre cuál actuaría la limpieza— y `hallazgos_sin_accion` —los hallazgos que el perfil midió y que ninguna acción atiende, con el motivo medido de cada uno—. La impresión del plan los anuncia y el informe HTML les da su propia sección. ```{r} sin_accion <- attr(plan, "hallazgos_sin_accion") sin_accion[, c("hallazgo", "columna", "severidad")] ``` ## Imputar por una dependencia Una dependencia funcional exacta permite deducir un valor desde los propios datos, pero sigue siendo una regularidad observada en una entrega. Puede representar una regla real o un error sistemático. Por eso la acción se ofrece con mapa y soporte, pero no queda recomendada ni activa. ```{r} datos <- data.frame( codigo = rep(1:3, each = 4), descripcion = rep(c("A", "B", "C"), each = 4), stringsAsFactors = FALSE ) datos$descripcion[c(2, 6)] <- NA perfil_fd <- perfilar(datos, muestra = Inf) plan_fd <- planificar_limpieza(perfil_fd, datos) imputacion <- grep( "^imputar_dependencia_funcional", plan_fd$estrategia ) plan_fd[imputacion, c( "estrategia", "recomendada", "aplicar", "justificacion" )] ``` Después de confirmar la regla, el usuario activa una alternativa y registra la decisión. La dependencia vuelve a validarse al aplicar el plan. ```{r} plan_fd$aplicar[imputacion[1]] <- TRUE plan_fd$decision_grupo[imputacion[1]] <- "elegida" resultado_fd <- aplicar(plan_fd, datos) resultado_fd$datos resultado_fd$registro[, c("estrategia", "n_cambiadas")] ``` El ciclo termina volviendo a perfilar. Así la mejora queda vinculada a un plan y a un registro, no a una transformación invisible. ```{r, cierre, include=FALSE} options(.opciones_previas) ```