Skip to contents

Diagnosticar no autoriza a modificar datos. En lupa, el producto de la etapa de mejora es primero un plan inspeccionable, filtrable y editable.

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"
)]
#>      id_accion      grupo          columna                     estrategia recomendada
#> 1  accion-0001       <NA>           cedula           revisar_cardinalidad       FALSE
#> 2  accion-0002       <NA>           cedula  convertir_ausencias_textuales        TRUE
#> 3  accion-0003       <NA> fecha_nacimiento           revisar_cardinalidad       FALSE
#> 4  accion-0004       <NA> fecha_nacimiento  convertir_ausencias_textuales       FALSE
#> 5  accion-0005       <NA> fecha_nacimiento     convertir_fecha_confirmada       FALSE
#> 6  accion-0006       <NA>             sexo  convertir_ausencias_textuales        TRUE
#> 7  accion-0007 grupo-0010          ingreso convertir_sentinelas_numericos       FALSE
#> 8  accion-0008       <NA>     departamento           revisar_cardinalidad       FALSE
#> 9  accion-0009 grupo-0012             pais     eliminar_columna_constante       FALSE
#> 10 accion-0010       <NA>           correo           revisar_cardinalidad       FALSE
#> 11 accion-0011 grupo-0017             <NA>        marcar_filas_duplicadas        TRUE
#> 12 accion-0012 grupo-0017             <NA>    conservar_primera_duplicada       FALSE
#> 13 accion-0013 grupo-0017             <NA>         conservar_mas_completa       FALSE
#> 14 accion-0014 grupo-0018       id_persona     marcar_columnas_duplicadas        TRUE
#> 15 accion-0015 grupo-0018       id_persona     eliminar_columna_duplicada       FALSE
#>    aplicar reversible      estado
#> 1    FALSE         NA informativa
#> 2     TRUE      FALSE       lista
#> 3    FALSE         NA informativa
#> 4    FALSE      FALSE       lista
#> 5    FALSE      FALSE   bloqueada
#> 6     TRUE      FALSE       lista
#> 7    FALSE      FALSE       lista
#> 8    FALSE         NA informativa
#> 9    FALSE      FALSE       lista
#> 10   FALSE         NA informativa
#> 11    TRUE       TRUE       lista
#> 12   FALSE      FALSE       lista
#> 13   FALSE      FALSE   bloqueada
#> 14    TRUE       TRUE       lista
#> 15   FALSE      FALSE       lista

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.

plan[, c("estrategia", "justificacion")]
#>                        estrategia
#> 1            revisar_cardinalidad
#> 2   convertir_ausencias_textuales
#> 3            revisar_cardinalidad
#> 4   convertir_ausencias_textuales
#> 5      convertir_fecha_confirmada
#> 6   convertir_ausencias_textuales
#> 7  convertir_sentinelas_numericos
#> 8            revisar_cardinalidad
#> 9      eliminar_columna_constante
#> 10           revisar_cardinalidad
#> 11        marcar_filas_duplicadas
#> 12    conservar_primera_duplicada
#> 13         conservar_mas_completa
#> 14     marcar_columnas_duplicadas
#> 15     eliminar_columna_duplicada
#>                                                                                                                                                                                                                                             justificacion
#> 1                                                                                                                                      El perfil señala el problema, pero no contiene conocimiento suficiente del dominio para elegir una transformación.
#> 2   Las representaciones textuales del catálogo son marcadores habituales de ausencia. El cambio no es reversible: confirmar que ninguno de esos textos sea un valor legítimo de la columna -`NA` es el código de Namibia, `NULL` puede ser un apellido-.
#> 3                                                                                                                                      El perfil señala el problema, pero no contiene conocimiento suficiente del dominio para elegir una transformación.
#> 4    Uno de los textos detectados (null) podría ser un valor legítimo de la columna: `NA` es el código de Namibia, `NULL` puede ser un apellido. El cambio no es reversible y el paquete no puede decidirlo, así que la acción queda para activar a mano.
#> 5                                                                        La conversión no es ejecutable sobre los datos completos: Hay valores presentes que no responden a los formatos confirmados. Se conserva como acción destructiva no recomendada.
#> 6   Las representaciones textuales del catálogo son marcadores habituales de ausencia. El cambio no es reversible: confirmar que ninguno de esos textos sea un valor legítimo de la columna -`NA` es el código de Namibia, `NULL` puede ser un apellido-.
#> 7                                                                                                                                                 Un sentinela numérico también puede ser un valor legítimo; requiere confirmar el diccionario del campo.
#> 8                                                                                                                                      El perfil señala el problema, pero no contiene conocimiento suficiente del dominio para elegir una transformación.
#> 9                                                                                                                             Eliminarla pierde contexto potencial; dejarla es la recomendación hasta confirmar que no aporta significado administrativo.
#> 10                                                                                                                                     El perfil señala el problema, pero no contiene conocimiento suficiente del dominio para elegir una transformación.
#> 11 Marcar conserva todas las filas, identifica las repeticiones y asigna un grupo a todos los registros que participan. No elimina filas; mientras las marcas esten incluidas, un perfil posterior no vuelve a contar esas filas como duplicadas exactas.
#> 12                                                                                                                           Conserva la primera aparición exacta y elimina las siguientes; el orden de entrada pasa a determinar qué registro sobrevive.
#> 13                                                                                                      Requiere configurar una clave: entre duplicados exactos todas las filas tienen la misma completitud y esta opción sería equivalente a la primera.
#> 14                                                                                                                                                                         La anotación conserva ambas columnas y registra explícitamente la redundancia.
#> 15                                                                                                                                           Eliminar una columna puede romper consumidores que dependan de su nombre aunque el contenido sea redundante.

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".

normalizacion(acentos = FALSE, puntuacion = TRUE)
#> Perfil de normalizacion de lupa
#>   minusculas = TRUE
#>   espacios = TRUE
#>   acentos = FALSE
#>   comillas = TRUE
#>   puntuacion = TRUE
#>   ligaduras = FALSE
#>   ancho = FALSE
#>   proteger = ñ, ü, g̃

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”.

grupos <- plan[!is.na(plan$grupo), c(
  "grupo", "estrategia", "recomendada", "aplicar", "decision_grupo"
)]
grupos
#>         grupo                     estrategia recomendada aplicar decision_grupo
#> 7  grupo-0010 convertir_sentinelas_numericos       FALSE   FALSE      pendiente
#> 9  grupo-0012     eliminar_columna_constante       FALSE   FALSE    recomendada
#> 11 grupo-0017        marcar_filas_duplicadas        TRUE    TRUE    recomendada
#> 12 grupo-0017    conservar_primera_duplicada       FALSE   FALSE    recomendada
#> 13 grupo-0017         conservar_mas_completa       FALSE   FALSE    recomendada
#> 14 grupo-0018     marcar_columnas_duplicadas        TRUE    TRUE    recomendada
#> 15 grupo-0018     eliminar_columna_duplicada       FALSE   FALSE    recomendada

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.

identical(guiar_limpieza(plan, datos_administrativos), plan)
#> [1] TRUE

Aplicar conserva evidencia

copia <- datos_administrativos
resultado <- aplicar(plan, datos_administrativos)
identical(datos_administrativos, copia)
#> [1] TRUE
resultado$registro[, c(
  "estrategia", "n_cambiadas", "n_filas_eliminadas", "n_columnas_eliminadas"
)]
#>                      estrategia n_cambiadas n_filas_eliminadas n_columnas_eliminadas
#> 1       marcar_filas_duplicadas           2                  0                     0
#> 2 convertir_ausencias_textuales           1                  0                     0
#> 3 convertir_ausencias_textuales           2                  0                     0
#> 4    marcar_columnas_duplicadas           1                  0                     0

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.

sin_accion <- attr(plan, "hallazgos_sin_accion")
sin_accion[, c("hallazgo", "columna", "severidad")]
#>                  hallazgo          columna  severidad
#> 1 tipo_declarado_distinto fecha_nacimiento sospechoso
#> 2               faltantes             sexo sospechoso

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.

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"
)]
#>                              estrategia recomendada aplicar
#> 6 imputar_dependencia_funcional__codigo       FALSE   FALSE
#>                                                                                                                                                                   justificacion
#> 6 La relación codigo -> descripcion es exacta en 10 filas presentes y cada valor usado tiene al menos 2 observaciones de soporte. Debe confirmarse como regla antes de imputar.

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.

plan_fd$aplicar[imputacion[1]] <- TRUE
plan_fd$decision_grupo[imputacion[1]] <- "elegida"
resultado_fd <- aplicar(plan_fd, datos)
resultado_fd$datos
#>    codigo descripcion .fila_duplicada .grupo_duplicado
#> 1       1           A           FALSE                1
#> 2       1           A           FALSE               NA
#> 3       1           A            TRUE                1
#> 4       1           A            TRUE                1
#> 5       2           B           FALSE                2
#> 6       2           B           FALSE               NA
#> 7       2           B            TRUE                2
#> 8       2           B            TRUE                2
#> 9       3           C           FALSE                3
#> 10      3           C            TRUE                3
#> 11      3           C            TRUE                3
#> 12      3           C            TRUE                3
resultado_fd$registro[, c("estrategia", "n_cambiadas")]
#>                              estrategia n_cambiadas
#> 1               marcar_filas_duplicadas          10
#> 2 imputar_dependencia_funcional__codigo           2

El ciclo termina volviendo a perfilar. Así la mejora queda vinculada a un plan y a un registro, no a una transformación invisible.