--- title: "Empezar con lupa" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Empezar con lupa} %\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) ``` `lupa` acompaña un recorrido: examinar una entrega, decidir qué medir, medir, evaluar, proponer mejoras y dejar evidencia. Ninguna etapa modifica los datos por el solo hecho de diagnosticarlos. ## Una entrega tabular El conjunto incluido es pequeño y completamente sintético. Tiene fechas mezcladas, sentinelas, duplicados y formatos discordantes. `analizar()` es la puerta de entrada: por omisión mide las propuestas en estado `lista`, las agrega y devuelve un tablero. Las métricas las propuso `lupa` y nadie las confirmó; se puede apagar la medición con `medir_propuesta = FALSE`. ```{r} library(lupa) data(datos_operativos) dim(datos_operativos) ``` La lista amplia de sentinelas numéricos de `naniar` se solicita de manera explícita porque valores como `66`, `77` y `88` pueden ser datos legítimos. ```{r} perfil_sentinelas <- perfilar( data.frame(codigo = c(1, 66, 9999)), sentinelas_numericos = sentinelas_naniar, analizar_dependencias = FALSE ) perfil_sentinelas$columnas[, c("columna", "n_faltantes_disfrazados")] ``` El marco contra el que se informa la cobertura también es declarable. En un data frame, `perfil_mide` señala los factores para los que el profiling aporta evidencia suficiente por sí solo. La forma abreviada con una lista deja ese campo en `FALSE`: nunca presume que examinar equivale a medir. ```{r} factores <- data.frame( dimension = c("Estructura", "Estructura", "Trazabilidad"), factor = c( "Ausencias observadas", "Duplicación exacta", "Origen documentado" ), perfil_mide = c(TRUE, TRUE, FALSE), como_resolverlo = c( "Revisar ausencias detectadas por el perfil.", "Revisar duplicados exactos detectados por el perfil.", "Declarar una métrica sobre el sistema de origen." ) ) marco_operativo <- marco_calidad("Marco operativo del ejemplo", factores) analisis <- analizar( datos_operativos, nombre = "entrega de ejemplo", fecha = as.POSIXct("2026-01-15", tz = "UTC"), marco = marco_operativo ) perfil <- analisis$perfil ``` ```{r} analisis$tablero ``` Los hallazgos son datos. Se pueden filtrar, ordenar o exportar sin extraer texto de un reporte. ```{r} perfil$hallazgos[, c( "columna", "tipo_hallazgo", "severidad", "evidencia" )] ``` Los conteos y la traza tienen unidades relacionadas, pero no necesariamente iguales. `mayusculas_inconsistentes` y `normalizacion_unicode` cuentan valores distintos (`unidad_conteo = "valor_distinto"`); su `trazabilidad` sigue enumerando por fila todas las filas que contienen esos valores, no sólo las filas defectuosas. `casi_duplicados_vocabulario` usa la misma forma: cuenta valores variantes y traza las filas que contienen los valores del grupo seleccionado, incluida la forma dominante. Como es una señal heurística, esa traza sirve para revisar el grupo y no afirma que todas sus filas sean errores. Las filas de las formas no dominantes aparecen primero y las de las formas dominantes después; la evidencia informa cuántas filas mostradas pertenecen a cada grupo. Para `patron_raro`, la evidencia conserva como máximo seis nombres, pero la trazabilidad usa todos los nombres raros hasta su límite separado de 5.000; un recorte de ese límite aparece en `cobertura_diagnosticos`. Cada hallazgo informa además la proporción del patrón dominante y cuántas filas quedaron en patrones no dominantes excluidos por superar `umbral_patron_raro`. Si el dominante no alcanza `umbral_patron_dominante`, no se emite el hallazgo: la no medición, la proporción observada y cómo ajustar ese argumento quedan en `cobertura_diagnosticos`. En `filas_duplicadas`, tanto el conteo afectado como la traza incluyen todas las filas participantes; la evidencia informa aparte los excedentes. Un cero es una medición de cero, mientras que un `NA` declara que el conteo no pudo medirse. Si se separan el conteo y la traza, `perfilar()` conserva el hallazgo y emite una advertencia de clase `lupa_trazabilidad_incoherente`. La tabla por columna mantiene todas las proporciones en `[0, 1]`. ```{r} perfil$columnas[, c( "columna", "tipo_declarado", "tipo_inferido", "prop_faltantes_totales", "n_distintos" )] ``` El mismo objeto incluye técnicas complementarias con sus límites declarados: frecuencias acotadas, cuantiles, asociaciones, calendario observado y una clasificación confirmable de escalas. La cobertura evita confundir ausencia de hallazgos con calidad demostrada. Si una columna se clasifica como posible dato personal, los estadísticos que podrían ser valores de una persona se suprimen y la tabla lo marca. Se suprimen los de orden —mínimo, máximo, mediana, moda— y también la media, porque sobre pocas filas se acerca a un valor y porque publicarla junto a otras cifras del mismo perfil permite despejar las que sí se protegieron. Y el desvío, que es una magnitud de los mismos valores. La supresión se declara, y distingue el estadístico de orden de los momentos. ```{r} analisis$distribuciones$cuantiles analisis$asociaciones analisis$temporal$resumen analisis$variables[, c( "columna", "tipo_almacenamiento", "tipo_implicito", "escala_propuesta", "confianza", "confirmada", "n_niveles_ausentes" )] analisis$cobertura[, c("marco", "dimension", "factor", "estado")] perfil$cobertura_diagnosticos ``` `cobertura_diagnosticos` es la tabla que sostiene la regla central de `lupa`: un diagnóstico que no pudo evaluarse queda identificado y no se confunde con la ausencia de hallazgos. En una columna `integer64`, `bit64` se carga de manera diferida si está instalado, para registrar sus métodos antes de medir la secuencia. Si no está instalado, las cinco medidas públicas de la secuencia quedan en `NA` y la tabla declara `secuencia_entera` como no evaluada, con `bit64` como dependencia. Cuando una cifra pasa a texto publicado, `lupa` usa notación fija independiente de `scipen` y de `digits` —se publica el valor completo— y conserva `OutDec`, que sigue siendo la marca decimal elegida por la sesión. Las sugerencias que propone el perfil para declarar una ausencia estructural marcan los nombres sólo en la copia usada para componer texto. El nombre de la tabla se conserva tal como llegó y el código publicado se puede copiar y pegar igual bajo `LC_CTYPE = "C"`, también cuando contiene tildes. La misma frontera de entrada se aplica a los valores: los bytes UTF-8 válidos que llegan sin marca se declaran UTF-8 antes de ejecutar las métricas. Las comparaciones textuales de `NoNulo`, `Formato` y dominios posibles se hacen por bytes, así que una medición no cambia entre un locale UTF-8 y `C`; los nombres publicados siguen conservando los bytes y la marca con que llegó la tabla. ## Del diagnóstico al modelo `proponer_modelo()` no ejecuta nada. Devuelve una propuesta editable que explica de dónde salió cada métrica. Los dominios y patrones aprendidos de una sola entrega quedan inactivos hasta que alguien confirme que son requisitos. ```{r} propuesta <- analisis$propuesta_modelo propuesta[, c( "metrica", "atributos", "origen", "prioridad", "incluir", "estado" )] ``` Las filas activas se convierten en un modelo y se miden. En este ejemplo hay dos clases de métricas activas: la estructural detecta las filas que participan en duplicados, y `NoNulo` mide la completitud de cuatro columnas contando como faltantes los valores disfrazados que el perfil encontró. ```{r} modelo_calidad <- modelo_desde_propuesta(propuesta) medidas <- medir( modelo_calidad, datos_operativos, id_medicion = "ejemplo-001", fecha = as.POSIXct("2026-01-15", tz = "UTC") ) head(medidas[, c("metrica", "fila", "resultado")]) ``` La medida booleana por fila de los duplicados puede agregarse a la entidad mediante `ratio`; `agregar()` sólo combina medidas de una misma métrica, así que se toma esa. Después se evalúa una regla explícita; no aparece un puntaje global inventado. ```{r} duplicados <- medidas[medidas$metrica == "EntidadDuplicada", ] medida_entidad <- agregar(duplicados, "entidad", "ratio") regla <- regla_evaluacion( "Duplicación menor al 20 %", function(x) x < 0.2 ) evaluacion <- evaluar( medida_entidad, perfil_evaluacion("Control operativo", regla) ) evaluacion$perfiles[, c("perfil", "resultado")] ``` ## Mejorar sin perder trazabilidad El plan propone acciones y sólo activa las que no dependen del dominio. Puede editarse como cualquier otro `data.frame`. ```{r} plan <- analisis$plan_limpieza plan[, c( "grupo", "columna", "estrategia", "recomendada", "aplicar", "estado" )] resultado <- aplicar(plan, datos_operativos) resultado$registro[, c("estrategia", "n_cambiadas")] ``` El original permanece intacto. El plan es editable: si se cambia una estimación o se la deja ausente, `aplicar()` sigue verificando el efecto observado. Una acción seleccionada que no cambia ningún valor queda `fallida` con su motivo; no se publica como ejecutada por falta de estimación. Para verificar el efecto se vuelve a perfilar el resultado. ```{r} perfil_despues <- perfilar(resultado$datos, nombre = "entrega normalizada") c( antes = nrow(perfil$hallazgos), despues = nrow(perfil_despues$hallazgos) ) ``` ## Un archivo para compartir `guardar_analisis()` conserva el recorrido entre sesiones. Por omisión excluye la tabla de entrada y sustituye las reglas funcionales por declaraciones pequeñas, evitando serializar sus entornos. `reportar()` acepta el objeto entero y produce un HTML autocontenido. Este ejemplo crea ambos archivos en el directorio temporal y luego los retira. ```{r} archivo_rds <- tempfile(fileext = ".rds") guardar_analisis(analisis, archivo_rds) analisis_recuperado <- leer_analisis(archivo_rds) is.null(analisis_recuperado$datos) archivo <- reportar( analisis, medidas, evaluacion, plan, archivo = tempfile(fileext = ".html"), titulo = "Calidad de la entrega de ejemplo" ) basename(archivo) unlink(c(archivo, archivo_rds)) ``` Para profundizar en la arquitectura consulte `vignette("definir-la-calidad")`; para revisar decisiones de remediación, `vignette("limpiar-con-un-plan")`. ```{r, cierre, include=FALSE} options(.opciones_previas) ```