Examina un data.frame, tibble o data.table y devuelve estadísticas
generales, métricas por columna, patrones, formatos de fecha y hallazgos
accionables. Todas las proporciones se expresan en [0, 1].
Usage
perfilar(
datos,
nombre = .nombre_de_los_datos(substitute(datos)),
fecha = Sys.time(),
muestra = 1e+05,
max_patrones = 20,
distinguir_mayusculas = TRUE,
expandir = FALSE,
umbral_alta_cardinalidad = 0.5,
umbral_faltantes_sospechoso = 0.1,
clave = NULL,
umbral_faltantes_error = 0.4,
umbral_patron_raro = 0.05,
umbral_patron_dominante = 0.5,
columnas_sin_ceros = character(),
columnas_no_negativas = character(),
columnas_opcionales = character(),
aplicabilidad = NULL,
ausencia_estructural = TRUE,
sentinelas_numericos = c(-9, -99, -999, -9999, 999),
cadenas_ausencia = NULL,
analizar_dependencias = TRUE,
umbral_dependencia = 0.995,
umbral_casi_clave_dependencia = 0.8,
max_columnas_dependencias = 100L,
datos_personales_permitidos = TRUE,
proteger_datos_personales = TRUE,
columnas_personales = character(),
validadores_personales = NULL,
umbral_documento_verificado = 0.9,
muestra_validadores = 1000L,
duplicados_aproximados = FALSE,
normalizar = TRUE,
max_filas_hallazgo = 1000L,
umbral_orden_columnas = 0.95,
max_columnas_orden = 20L,
umbral_solapamiento_orden = 0.1,
umbral_aritmetica = 0.9,
min_filas_aritmetica = 3L,
tolerancia_aritmetica = 1e-08,
max_columnas_aritmetica = 20L,
casi_duplicados_vocabulario = TRUE,
max_proporcion_grupo_vocabulario = 0.5,
umbral_variante_rara_vocabulario = 0.05,
min_asimetria_vocabulario_corto = 10,
min_asimetria_vocabulario = 2,
min_participacion_dominante_vocabulario_corto = 0.5,
variantes_equifrecuentes_vocabulario = FALSE,
max_asimetria_equifrecuente_vocabulario = 2,
max_comparaciones_dependencias = 200000L,
max_trabajo_vocabulario = 2e+10,
max_trabajo_dependencias = 1e+08,
max_largo_valor_vocabulario = .MAX_LARGO_VALOR_CASI_DUPLICADOS,
max_celdas_muestra = .MAX_CELDAS_MUESTRA,
max_bytes_muestra = .MAX_BYTES_MUESTRA,
avisar_costo_tabla_ancha = TRUE,
umbral_celdas_aviso_tabla_ancha = .UMBRAL_CELDAS_AVISO_TABLA_ANCHA
)Arguments
- datos
Objeto que hereda de
data.frame—tibbleydata.tableentran por ahí— o una matriz de dos dimensiones, que se convierte conas.data.frame(). La conversión queda declarada enmeta$entrada_convertidapara que el perfil no aparente haber recibido lo que no recibió.- nombre
Nombre descriptivo del objeto.
- fecha
Fecha y hora de la corrida. Se puede fijar para construir series reproducibles; se normaliza a UTC.
- muestra
Máximo de filas usadas para patrones, inferencia de tipos, formatos de fecha y dependencias funcionales. Use
Infpara analizar todas las filas en esos análisis.- max_patrones
Máximo de patrones mostrados por columna.
- distinguir_mayusculas
Si se distinguen mayúsculas y minúsculas.
- expandir
Si se emite un token por carácter en los patrones.
- umbral_alta_cardinalidad
Umbral sobre la tasa de valores distintos de una columna categórica. No alcanza por sí solo: el hallazgo exige además al menos diez valores distintos, porque con pocos la tasa está dominada por el tamaño de la tabla —dos valores en tres filas dan 0,67 y superan cualquier umbral razonable— y una columna de dos valores no puede tener cardinalidad alta.
- umbral_faltantes_sospechoso
Umbral inferior de faltantes. El hallazgo se activa al superarlo en sentido estricto.
- clave
Nombres de las columnas que identifican una fila. Cuando se declaran, la trazabilidad de cada hallazgo trae además el valor de esas columnas para las filas señaladas, de modo que el caso se pueda verificar en el sistema de origen sin abrir la tabla. El índice de fila se conserva siempre. La clave declarada se trata como sensible: si la protección de datos personales está activa y alguna de esas columnas se clasifica como personal, sus valores salen enmascarados igual que la evidencia. La comprobación de una clave tiene dos ejes independientes:
meta$clave$unicidadcomprueba si sus combinaciones son únicas con la semántica de R, ymeta$clave$ausencia_nuloscomprueba que sus componentes no tengan valores ausentes. Cada eje declaraverificada,refutadaono_verificada; una clave con ambos ejes verificados conserva exactamente el objeto histórico y no agrega metadatos. Cuando un eje falla o no se puede comprobar,meta$clave$trazabilidadexplica que la localización agrupa con la semántica de R, incluso si el motor SQL trata dosNULLcomo distintos.hallazgossepara esos ejes:clave_con_ausentesenumera las filas que impiden la garantíaNOT NULL, mientrasclave_no_unicasólo informa repeticiones entre filas con la clave completa, el mismo universo quemeta$clave$unicidad.- umbral_faltantes_error
Umbral por encima del cual los faltantes son un error; la igualdad conserva la severidad sospechosa.
- umbral_patron_raro
Máxima frecuencia de un patrón raro.
- umbral_patron_dominante
Frecuencia mínima del patrón dominante.
- columnas_sin_ceros
Nombres de columnas donde cero no es admisible.
- columnas_no_negativas
Nombres de columnas que deben ser no negativas.
- columnas_opcionales
Nombres de columnas donde la ausencia no es un defecto. Su universo de completitud son las celdas presentes, y
cobertura_diagnosticosdeclara el recorte. Sirve para el vacío por diseño: un historial con vigencia abierta, una columna que sólo corresponde a algunas filas.- aplicabilidad
Lista con nombre por columna, donde cada elemento es una fórmula de un solo lado evaluada sobre
datos— por ejemplolist(marca_auto = ~ tiene_auto == "Si"). Las filas donde el predicado no se cumple salen del universo de esa columna: no cuentan como ausencia. Las filas donde el predicado no se puede determinar se declaran aparte, sin contarse ni como aplicables ni como no aplicables. Un valor presente fuera del universo produce el hallazgovalor_fuera_de_aplicabilidad, que es el error simétrico y hoy no tiene otra forma de aparecer.lupano infiere el universo: si nadie lo declara, toda la columna aplica y el resultado es el de siempre. Declararlo es lo que distingue el vacío por diseño del vacío por error, y sin esa distinción una tabla sana puede informar completitud baja siendo completa.Hasta dónde llega el universo. Gobierna todo el perfilado: los conteos, las proporciones, los hallazgos y las acciones que propone
planificar_limpieza(). Medido sobre una columna condicionada de mil filas con universo de trescientas, treinta de ellas vacías: declarándolo, la proporción de faltantes es0,100, no hay hallazgo y el plan no propone nada; sin declararlo son0,730, salefaltantesy el plan propone dos acciones.El universo tambien llega a la limpieza. El perfil guarda la regla, el plan la lleva y
aplicar()no toca las celdas que quedan fuera: unaS/Den una fila donde la columna no corresponde no se convierte en ausencia, y las acciones que marcan o eliminan filas solo consideran ese universo. Si la regla da un valor indeterminado, la celda tampoco se toca. Las conversiones de tipo son la excepción, porque una columna tiene un solo tipo y no se puede convertir a medias: el plan declara enn_afectadasy en la justificación que cambia la columna entera. Si el plan se aplica a otros datos donde la regla no se puede evaluar, la acción falla y lo dice, en vez de operar sobre toda la columna.La medición contra un marco recibe la misma declaración:
medir()aceptaaplicabilidady recorta las filas antes de medir, así que el histórico y la deriva —que consumen mediciones, no perfiles— heredan el número correcto. Elaplicabilidaddemarco_calidad()es otra cosa: dice si un factor aplica a datos temporales o geométricos, no qué filas entran al universo.- ausencia_estructural
Si se busca evidencia de que la ausencia de una columna es por diseño.
lupano infiere el universo ni lo cambia por su cuenta, pero declararaplicabilidadexige saber que existe: quien perfila una tabla con columnas condicionadas sin declarar nada recibe el mismo informe engañoso que si el vacío fuera un defecto. ConTRUE, cuando el valor de otra columna decide qué filas tienen ésta, o cuando dos columnas se reparten las filas sin pisarse, se emiteposible_ausencia_estructuralcon severidadok, la evidencia medida y la línea exacta que habría que escribir para declararlo. Sugiere; no decide. Las columnas ya declaradas quedan fuera del examen. La regla tiene que cumplirse en al menos el 99 % de las filas: una que un solo caso rompe por debajo de ese corte no se publica, porque ofrecer la línea para declararla sería afirmar de más.Con el mismo argumento viaja
regla_silencia_ausencia, tambiénok: avisa cuando una columna declarada opcional o con universo propio sigue casi vacía dentro de ese universo. La declaración funcionó y por eso el perfil salió limpio; el aviso existe para que eso sea una decisión y no un efecto.- sentinelas_numericos
Vector completo de valores numéricos que se interpretan como ausencia. Se normaliza como un conjunto numérico, por lo que el orden, el tipo entero o doble y los duplicados no cambian el resultado.
numeric()los desactiva; las cadenas de ausencia se siguen evaluando por separado.La lista por omisión es una sospecha del paquete y se aplica con una reserva: un valor de esa lista que aparece una sola vez y cae dentro del rango de los demás valores de la columna no se señala, porque una aparición única es un número y no una convención de ausencia. Un
999suelto dentro de un catálogo de1a3000no se acusa; el mismo999repetido, o un-9fuera del rango, sí.La reserva sólo rige para la lista por omisión. Si usted declara sus propios valores —o pide sentinelas_naniar, que es optar por una heurística más ancha—, se cuentan todos, incluida la aparición única. Ésa es también la forma de recuperar el caso que la reserva calla: declarar el valor lo vuelve a contar y además lo saca de los estadísticos del resumen.
- cadenas_ausencia
Cadenas que el usuario declara como ausencia, además de la lista incorporada. Lo declarado atraviesa la guarda del vocabulario:
sd,ncyndsólo cuentan solas en columnas de pocos valores distintos —para no acusar a un código de dos letras—, y declararlas las hace contar siempre. Es el equivalente textual desentinelas_numericos.- analizar_dependencias
Si se buscan dependencias funcionales entre pares de columnas. Se aplica una sola muestra común a toda la tabla.
- umbral_dependencia
Cumplimiento mínimo para informar una dependencia.
- umbral_casi_clave_dependencia
Tasa de valores distintos a partir de la cual un determinante se descarta como casi-clave antes de agrupar.
- max_columnas_dependencias
Máximo de columnas que intervienen en la búsqueda, cuyo costo crece cuadráticamente.
- datos_personales_permitidos
Si la entrega admite datos personales. El valor predeterminado no juzga su presencia: la clasificación se informa con severidad
"ok". UseFALSEsólo cuando el contrato de la entrega declare que no deben existir.- proteger_datos_personales
Si se reemplazan modas, ejemplos, evidencia y estadísticos de orden concretos cuando
poder_discriminantees medio, alto o verificado. Las clasificaciones débiles se conservan como aviso pero no suprimen estadísticos. Para conservar todo en el objeto debe desactivarse explícitamente;reportar()aplica además su propia protección predeterminada. El enmascarado es por columna: se reemplaza lo que describe a una columna protegida —su moda, sus estadísticos, sus ejemplos y la evidencia de sus hallazgos—, no todas las apariciones de ese texto en la salida. Un valor puede ser vocabulario compartido:"S/D"es un centinela de una cédula y a la vez el «sin dato» desexo, y publicarlo ensexono revela nada de la cédula. Lo que no tiene columna a la que atribuirse sí se enmascara en toda la salida: un hallazgo de filas duplicadas muestra filas enteras, y los componentes que describen la tabla —general, la cobertura, los formatos de fecha— no son de ninguna columna en particular. Los nombres de columna nunca se enmascaran, aunque un valor protegido aparezca dentro de uno: un nombre es estructura, existía antes y aparte del dato, y el paquete lo necesita para cruzar el perfil con los datos. Se enmascaraban, y en una base realfecha_nacimientosalió publicada comofecha_[valor protegido]:planificar_limpieza()yanalizar()abortaban porque los nombres del perfil ya no coincidían con los de los datos. Un campo se reconoce como de nombres por su contenido —todos sus valores son nombres de la entrada, solos o unidos—, así que la protección de los valores no se afloja: si un valor protegido es igual a un nombre de columna, su moda sigue tapada. Y un texto que nombra una columna entre comillas —la prosa del paquete, la SQL que guardaperfilar_dbi()— la conserva entera: con «Segundo» protegido,segundo_nombresalía[valor protegido]_nombreen los hallazgos sin acción del plan y en la SQL. Un nombre que es él mismo un valor protegido, con otra caja, otros separadores, tildes o codificación, no se exceptúa. Lo mismo vale para el vocabulario: un tipo de hallazgo, una severidad, una estrategia, el nombre de un diagnóstico o de una métrica, o el de un factor de un marco que declaró el usuario, no se enmascaran aunque coincidan con un valor protegido o lo contengan. El principio es el de los nombres de columna: son estructura, no salen de las celdas, y publicarlos no dice nada de ninguna fila —el tipoconstanteaparece porque otra columna es constante, no porque alguien se apellide así—. Se enmascaraban: en una base real, un nombre de persona contenido enfaltantes_disfrazadosdejó seis hallazgos con el tipo[valor protegido], y el plan sin ninguna acción para ellos. La regla es por campo y por contenido: un campo cuyos valores son todos vocabulario del propio objeto queda afuera, uno que mezcla no. Ese alcance tiene un piso: un valor de una columna protegida que identifique —seis caracteres o más, el mismo corte con el que la batería de fugas decide qué cuenta como filtración— no se publica en ninguna parte, tenga o no columna a la que atribuirse, y también en los campos numéricos. Sin ese piso, una columna que el clasificador no marcó publicaba los documentos de la protegida con sólo repetirlos: una copia con nombre neutro, un texto libre que los contuviera, o los estadísticos de orden de una columna clasificada con poder discriminantedebil. El vocabulario corto no entra en el piso:"S/D"sigue publicándose donde describe a una columna que no es personal. El piso tiene una excepción declarada, y es la única:perfilar_por()agrupando por una columna personal publica sus valores como etiquetas de grupo, porque la etiqueta es el eje del resultado y sin ella los grupos no se distinguen. Lo mismo cuando la columna de agrupación no es personal pero alguna etiqueta contiene un valor protegido de otra columna. No ocurre en silencio —avisa al ejecutar y lo declara enetiquetas_personales— y el remedio es agrupar por una columna seudonimizada. El piso alcanza además la forma sin separadores:"771.771-01"es el mismo documento que"77177101", y la celda entera se enmascara. En los dígitos se busca el documento en cada número del texto, con cualquier agrupación: un número une grupos de dígitos con espacios, guiones o puntos —también con un espacio a cada lado—, y con barras, comas sin espacio, apóstrofos, signos más, puntos medios o paréntesis pegados, y se tapa la celda si grupos contiguos forman un documento protegido —"2901 1234","099 12 34 56","(2) 487 1234","4111 1111 1111 1111","01.23.45.67.89"—, también con ceros a la izquierda, con los guiones, espacios e invisibles de Unicode y los parecidos más comunes del punto, la coma y la barra, o escrito con dígitos arábigo-índicos, persas o devanagari. Los dos puntos y la barra vertical no unen: una razón"494:2714"o una lista"12|345|6789"no es un documento, y un documento escrito así no se reconoce. Una fecha con hora protegida se reconoce escrita conT,Z, espacio o coma, con día y mes en cualquier orden, con la hora separada por puntos o en la forma compacta"20240627T212425"; no en palabras, con a.m./p.m. ni con el año en dos cifras. El documento parcial, de siete cifras o más, se busca con más cuidado porque es el que coincide por azar: sin su verificador, fuera de la parte de un decimal y de un número con forma de dirección IPv4 ("4.123.456"frente a"4.123.456-7", el verificador mal tipeado, el documento seguido de un año), y sin su primer dígito sólo detrás de un asterisco o una equis ("*.123.456-7","X.012.345-8"); un número redondo (1.000.000) no cuenta como parcial. Es un decimal el número cuyo último signo es un punto o una coma con otra cantidad de cifras que tres, y antes un entero o miles separados con otro signo:"4.123.456,7"es un importe,"4.123.456.7"no; su parte decimal no se pega al resto cuando es la de un importe con miles o la de un decimal suelto —una coordenada, un par lat lon—, pero sí cuando el número es otra cosa, como"(02) 901.1234". Una fecha con un año entre 1800 y 2100, una hora —con su fracción de segundo—, un ISBN y una lista separada por comas no son documentos. En la prosa del paquete los números son conteos suyos, y ahí la regla mira sólo lo que la prosa cita entre comillas y el código que sugiere entre comillas invertidas, y tapa la cita o el número, no la frase. El parcial tiene un costo, medido: un número entero de siete cifras que coincide con un documento protegido sin su verificador no se distingue de él, y se tapa. Con un millón de cédulas, 300.000 teléfonos fijos y 300.000 celulares protegidos, eso le pasó a uno de cada cinco números de siete cifras en ejemplos, modas y evidencia —en cualquier escritura: con puntos o apóstrofos de miles, con un guion, tras un numeral; tras un asterisco, a uno de cada cuatro—, y a uno o dos de cada cien enteros de ocho cifras, que coinciden con un documento entero; las coordenadas, los p-valores, los importes con decimales, las horas, los ISBN, las listas separadas por comas o por barras verticales y las razones no se taparon, y una dirección IP sólo cuando coincide entera. Lo mismo vale, por la misma razón, para una lista separada por espacios, un rango"a - b"o una versión"v4.16.4302": se unen como el documento. La medición se rehace condata-raw/medir_tapado_de_mas.R. Las tildes, la caja y la escritura también son cosméticas:juan.perezse enmascara frente al nombre protegido «Juan Pérez», y el nombre pegado a otras letras dentro de un correo o un alias, también. La comparación usa un pliegue generado de Unicode —caja de todo alfabeto, letras con y sin diacríticos, ligaduras, ancho completo y medio ancho, letras matemáticas—, compara el texto publicado también con los escapes del paquete deshechos (un espacio duro sale<U+00A0>) —el valor protegido nunca se desescapa: una barra literal es parte del valor—, y lee lo que no es UTF-8 válido de las dos maneras, byte por byte como CP1252 conservando las secuencias válidas y entero. Las marcas del paquete se apartan del texto y también del valor protegido. Una fecha de calendario sola no entra en el piso, tampoco escrita comoAAAAMMDDen una columna de esas fechas ni como fecha-hora ISO a medianoche: en una tabla de miles de personas casi todo día es el cumpleaños de alguien, y con la fecha de nacimiento protegida se tapaban los estadísticos de todas las demás columnas de fechas. La columna de fechas protegida se sigue protegiendo entera; una fecha con hora sí cuenta. En la prosa del paquete —descripción, sugerencia, motivo, cómo resolverlo, justificación— se exige además que la variante no tenga una letra pegada antes o después: con los nombres de millones de personas como valores protegidos, alguno aparecía cruzando palabras de esas frases y las tapaba enteras, también en columnas no personales. Lo que esa prosa cita entre comillas dobles —el nivel de un determinante en la sugerencia deposible_ausencia_estructural— no es prosa sino un valor: se compara con la regla de los valores y se tapa sólo la cita, para que la sugerencia se siga leyendo. Y si el determinante es una columna protegida, la sugerencia no reproduce el criterio. Las palabras y las marcas del propio paquete no se tapan: con columnas de nombre y de apellido separadas, los valores protegidos son palabras sueltas, y algunas lo son también del paquete —Blancofrente a la marca<blanco>,Máximofrente a la clavegrupo_maximo,Patrónen «el patrón dominante»—. El léxico se calcula al instalar, desde las cadenas del código del paquete: sus marcas no se comparan en ningún campo, y sus palabras no se tapan en su prosa salvo dentro de una cita. El nombre completo en el texto de otra columna se sigue tapando. Y lo aplican tambiénanalizar()ydistribucion_valores(), que antes protegían sólo por columna y publicaban lo queperfilar()tapaba sobre la misma tabla.- columnas_personales
Columnas que traen datos personales, declaradas por quien conoce el dato.
c("cod_benef", "apodo")las nombra sin decir de qué tipo son;c(cod_benef = "documento_identidad")además lo dice. Lo declarado gana sobre lo inferido y no se vuelve a examinar.Existe porque el léxico de nombres de columna no puede ser completo: una columna con documentos se puede llamar de cualquier manera, y ninguna lista de nombres frecuentes la va a reconocer. Es el mismo patrón que
columnas_opcionales, aplicado a la otra decisión que el paquete no puede tomar solo.- validadores_personales
Pack o lista nombrada de funciones que reciben un vector de texto y devuelven un lógico de igual longitud.
NULLusavalidadores_uruguay()por compatibilidad;FALSEonumeric()desactiva la verificación de documentos. El nombre del mejor validador queda en el fundamento de la clasificación.- umbral_documento_verificado
Proporción mínima de valores que debe aceptar un validador para clasificar una forma de documento como
verificado. Por defecto es0.9.- muestra_validadores
Máximo de valores usados en el filtro preliminar de cada validador. Si la proporción preliminar ya queda bajo el umbral no se valida la columna completa; use
Infpara revisar todos desde el inicio.- duplicados_aproximados
FALSEpor omisión. UseTRUEo una lista de argumentos para ejecutardetectar_duplicados_aproximados()y añadir sus pares y hallazgos al perfil. Es un análisis acotado y opcional porque no afirma identidad ni debe encarecer todas las corridas.- normalizar
Perfil de comparación que se conserva en
meta$normalizaciony que heredan los análisis de duplicados y claves cuando no reciben uno explícito. Cambia sólo la representación usada para comparar.- max_filas_hallazgo
Tope de índices de fila que conserva cada trazabilidad disponible. Por defecto es
1000; cuando se supera, el estado queda comotruncaday el total se conserva. UseInfsólo si necesita desactivar explícitamente el tope.- umbral_orden_columnas
Cumplimiento mínimo de una relación de orden entre columnas comparables. Se usa
0.95por omisión; con menos de 20 filas comparables se permite una sola inversión para no descartar tablas pequeñas. El alcance efectivo queda enmeta$orden_columnas.- max_columnas_orden
Máximo de columnas numéricas o temporales que se comparan entre sí para detectar relaciones de orden. Las columnas que exceden el límite se conservan en
meta$orden_columnas$columnas_omitidas.- umbral_solapamiento_orden
Solapamiento mínimo de los rangos intercuartiles para considerar que dos columnas representan magnitudes comparables. Por defecto es
0.1: al menos una décima parte del rango intercuartílico más ancho debe ser común a ambos. Esto evita interpretar como restricción fila a fila un orden explicado sólo por escalas separadas. Si no hay ese solapamiento, una brecha con IQR exactamente cero conserva el par porque la mitad central sostiene el mismo desplazamiento fila a fila. No se aplica una tolerancia oculta. Use0para desactivar el filtro de magnitud. Ambos criterios se publican en la evidencia. Los pares descartados se cuentan enmeta$orden_columnas$pares_descartados_magnitudy los recuperados enmeta$orden_columnas$pares_rescatados_brecha_estable. Los pares que involucran una columna cuyo tipo o formato se descubrió sobre una muestra y cuya conversión dejó valores afuera no se comparan; el alcance los conserva encolumnas_conversion_parcial,n_valores_excluidos_conversionypares_descartados_conversion_parcial.- umbral_aritmetica
Proporción mínima de filas comparables que deben satisfacer una identidad dentro de
tolerancia_aritmeticapara reconocer una regularidad aritmética entre columnas numéricas. El valor por omisión es0.9. Una vez reconocida la relación se informan todas sus discrepancias, sin un segundo filtro por su cantidad absoluta. La proporción y el criterio efectivos se publican en cada evidencia.- min_filas_aritmetica
Mínimo de filas comparables necesario para evaluar una candidata aritmética. Por omisión es
3.- tolerancia_aritmetica
Tolerancia numérica relativa escalada usada al comparar un valor observado y uno esperado. Por omisión es
1e-8; el criterio completo y el valor efectivo se declaran en cada evidencia.- max_columnas_aritmetica
Máximo de columnas numéricas que intervienen en la búsqueda aritmética, cuyo costo crece cúbicamente. Por omisión es
20. Si se omiten columnas,cobertura_diagnosticosdeclara el recorte ymeta$aritmetica_columnasconserva los conteos de combinaciones.- casi_duplicados_vocabulario
Lógico que activa el diagnóstico de variantes casi duplicadas dentro del vocabulario de cada columna de texto. Por defecto es
TRUE;FALSElo omite sin afectar los demás hallazgos. La distancia es una señal heurística, no una prueba de identidad: Jaro–Winkler puede agrupar nombres de calles o códigos que sólo comparten un prefijo o un sufijo. En vocabularios heterogéneos revise la evidencia como sospechosa, declare una regla de dominio o useFALSEpara desactivar este diagnóstico.- max_proporcion_grupo_vocabulario
Proporción máxima del vocabulario que puede abarcar el grupo mayor para entregar grupos de variantes. Por defecto es
0.5; si se supera, el alcance declara que el diagnóstico no aplica en vez de entregar un bloque que abarque casi toda la columna. El diagnóstico se ejecuta por columna y no deduplica columnas con el mismo vocabulario: cada una conserva su propia fila de cobertura y sus propios hallazgos. El costo de comparar vocabularios idénticos puede repetirse.- umbral_variante_rara_vocabulario
Proporcion maxima de la columna que puede ocupar una variante breve para abrir la comparacion por una edicion.
- min_asimetria_vocabulario_corto
Razon minima entre la frecuencia de una forma dominante y una variante breve para abrir la comparacion por una edicion.
- min_asimetria_vocabulario
Razón mínima entre la frecuencia de la forma dominante y la de la variante para abrir un grupo por la vía general de distancia. Por omisión
2. Una asimetría de1,5significa que las dos formas son casi igual de comunes, que es evidencia muy floja de una errata: medido sobre tablas limpias y sobre erratas sembradas, los falsos positivos están entre1,0y1,5y las erratas reales desde9,0.- min_participacion_dominante_vocabulario_corto
Proporcion minima de la columna que debe ocupar la forma dominante en la comparacion por una edicion.
- variantes_equifrecuentes_vocabulario
Si se informa el diagnóstico
variantes_equifrecuentes_vocabulario, que señala dos formas cercanas que se reparten la columna sin que ninguna sea dominante —el error sistemático de dos operadores, una plantilla rota o una migración parcial—.Está apagado por omisión y la razón está medida: sobre la batería de 31 tablas limpias produce un grupo sospechoso donde no hay defecto. Lo que lo dispara no es el tamaño de la tabla sino el reparto: dos formas parecidas con frecuencias del mismo orden lo activan igual con cuatro filas que con quinientas, y en una tabla chica ese reparto sale por casualidad más seguido. Decía «tablas de menos de veinte filas», y se midió de cuatro a quinientas: dispara en todas. Es aditivo: encenderlo no cambia ni pierde ninguna detección de
casi_duplicados_vocabulario. El límite que deja de cubrir se declara igual, encendido o apagado, enn_grupos_sin_variante_rara.- max_asimetria_equifrecuente_vocabulario
Razón máxima entre la frecuencia mayor y la menor para considerar que dos formas se reparten la columna. Con
2,40contra5no entra y5contra5sí.- max_comparaciones_dependencias
Tope de pares determinante-dependiente que se comparan. El costo de las dependencias es del orden de
columnas^2 x filasy empeora con determinantes casi únicos; cuando el presupuesto se agota, lo comparado se informa y lo que quedó sin comparar se declara encobertura_diagnosticos, nunca como cero.- max_trabajo_vocabulario
Tope de comparaciones de carácter para las comparaciones de distancia del vocabulario. Comparar dos valores cuesta del orden del producto de sus largos, así que el trabajo es la suma de ese producto sobre todos los pares. Contar pares por longitud media subestima las cadenas largas: medido, el mismo presupuesto compraba 5,3 millones de unidades por segundo con valores de 900 caracteres y 44 millones con valores de 40. Se combina con el límite interno de pares del detector y manda el más restrictivo; ese límite interno no es un argumento de
perfilar(). El recorte declara valores, pares y trabajo sin comparar.Inflo desactiva.- max_trabajo_dependencias
Tope predeterminado de unidades fila-par para las dependencias. Se combina con
max_comparaciones_dependencias; el límite efectivo baja cuando hay muchas filas.Infdesactiva este tope, pero no el de pares.- max_largo_valor_vocabulario
Maximo de caracteres permitido en cada valor que entra en la comparacion de casi duplicados del vocabulario. Por defecto es
10000, elegido porque la distancia normalizada deja de distinguir de forma estable una diferencia local de muchas diferencias en textos largos. Una columna que supera el tope se declara completa fuera de alcance encobertura_diagnosticos; no se recortan valores en silencio.Infrecupera explicitamente el comportamiento anterior sin tope.- max_celdas_muestra
Maximo de celdas que puede contener la muestra comun de los diagnosticos que muestrean filas. Por defecto es
1000000; se calcula como filas efectivas por columnas de la tabla. Si reduce la muestra,cobertura_diagnosticosinforma las filas y celdas solicitadas, el umbral y el nuevo alcance.Infdesactiva este tope.- max_bytes_muestra
Maximo de bytes de la muestra materializada que alimenta los diagnosticos que muestrean filas. Por defecto es
512 MiB. El tamaño se estima sobre una sonda de hasta cien filas y se comprueba sobre la muestra efectiva; si la cota reduce el alcance,cobertura_diagnosticosinforma los bytes observados y el umbral.Infdesactiva este tope.- avisar_costo_tabla_ancha
Si es
TRUE, avisa en sesiones interactivas cuando las celdas proyectadas superanumbral_celdas_aviso_tabla_ancha. El aviso es una estimacion y queda en silencio en scripts no interactivos.- umbral_celdas_aviso_tabla_ancha
Cantidad de celdas a partir de la cual se emite el aviso de tabla ancha. Por defecto,
100000;Inflo desactiva explicitamente.
Value
Objeto S3 de clase perfil. Cada fila de hallazgos incluye
n_evaluados, n_afectados y unidad_conteo: son conteos de las unidades
declaradas (por ejemplo fila, columna, formato o par). En
mayusculas_inconsistentes, normalizacion_unicode y
casi_duplicados_vocabulario, la unidad es
valor_distinto: n_evaluados cuenta los valores distintos evaluados y
n_afectados los valores distintos que participan en la colisión. Su
trazabilidad sigue siendo por fila y enumera todas las filas que
contienen esos valores, no sólo las filas defectuosas; por eso su total
puede ser mayor que n_afectados. En casi_duplicados_vocabulario, la
traza incluye todas las filas cuyos
valores pertenecen al grupo elegido, incluida la forma dominante. La
distancia es una senal heuristica, no una prueba de que cada fila deba
corregirse; la evidencia declara cuantas filas mostradas pertenecen a las
formas variantes y cuantas a las formas dominantes.
En filas_duplicadas, el conteo y la traza incluyen todas las filas
participantes de los grupos; el numero de excedentes queda en la
evidencia. Cuando el camino
no puede conocer un conteo, informa NA, nunca cero. La columna de lista
trazabilidad distingue disponible, truncada, no_aplica y
no_disponible; cuando corresponde conserva índices de fila acotados por
max_filas_hallazgo, el total conocido y el alcance. En
clave_con_ausentes, cuenta las filas con al menos un componente ausente
y su traza enumera esas filas. En clave_no_unica, cuenta las filas que
participan en colisiones entre claves completas y su traza excluye las
filas incompletas; ambas unidades son fila. Así, una colisión entre
ausentes no aparece como una repetición que refute la unicidad.
casi_duplicados_vocabulario, donde la traza mezcla filas de formas
variantes con filas de la forma dominante, conserva además
n_filas_formas_variantes y n_filas_formas_dominantes con el reparto
completo, y mostrados_formas_variantes y mostrados_formas_dominantes
con el reparto de lo que sobrevivió al truncado. Las variantes se entregan
primero, de modo que el truncado no se lleve lo accionable. Para
patron_raro,
el alcance puede ser completo, muestra_patrones,
patrones_parciales o muestra_patrones+patrones_parciales. El resumen y
el texto de evidencia de patron_raro muestran como maximo seis patrones,
pero la trazabilidad conserva los nombres de todos los patrones raros hasta
un limite de 5.000; patrones_parciales indica que se alcanzo ese limite,
no que se haya alcanzado el tope de presentacion.
Cuando se emite un hallazgo patron_raro, su evidencia incluye la
proporcion del patron dominante y cuantas filas pertenecen a patrones no
dominantes que superan umbral_patron_raro y por eso quedan excluidos.
Si el patron dominante no alcanza umbral_patron_dominante, no se emite
el hallazgo: cobertura_diagnosticos declara la no medicion, su proporcion
observada y el argumento que se puede ajustar.
Si el conteo y la traza no coinciden, conserva el hallazgo y emite una
advertencia de clase lupa_trazabilidad_incoherente. La guarda compara el
total previo al truncado y respeta la unidad declarada.
Una columna compuesta no analizada conserva en la traza todas sus filas. Si una
columna de listas se reconoce como constante pero no se puede contar su
frecuencia, el conteo afectado queda en NA y cobertura_diagnosticos
explica la no evaluación.
Los índices no contienen valores. Usarlos para extraer filas de los datos
originales puede volver a exponer datos personales; el paquete no realiza
esa extracción y la protección de salidas no sustituye el control de acceso
a los datos de entrada.
En la evidencia de casi_duplicados_vocabulario, clase_diferencia puede
ser normalizacion_exacta (la coincidencia aparece después de normalizar),
dentro_de_palabra (la diferencia está dentro de un token),
token_completo (cambian tokens completos), token_unico (ambos valores
son un solo token y esa distinción estructural no aplica), mixta (el grupo
reúne aristas de más de una clase) o indeterminada (no hubo aristas
clasificables). Son categorías de evidencia, no veredictos de identidad.
cobertura_diagnosticos es una tabla hermana de hallazgos, con una fila
por diagnóstico que no pudo evaluarse o cuya enumeración quedó parcial y
las columnas diagnostico,
columna, motivo, como_resolverlo y dependencia. Incluye la falta de
stringdist, stringi, bit64 o sf, incluida la medición de secuencias
integer64 cuando falta bit64, y las zonas horarias POSIXt sin declarar.
Los patrones de frecuencia intermedia no se consideran desvios
del patron dominante: patron_raro es completo respecto de su criterio de
rareza cuando no hay recorte de trazabilidad. Si el conjunto de nombres
raros supera 5.000, cobertura_diagnosticos declara el recorte y su limite.
Quien decida automáticamente sobre un perfil debe revisar
nrow(perfil$cobertura_diagnosticos) además de las severidades: un perfil
sin hallazgos y con diagnósticos no evaluados no es un perfil limpio.
Cuando una clave declarada no queda plenamente verificada, meta$clave
conserva los estados de unicidad y ausencia de nulos, sus conteos y la
semántica usada por la trazabilidad. Los tres responden preguntas distintas
y no comparten universo:
unicidadse evalúa sólo entre las filas con la clave completa (semantica = "claves_completas"), porque una repetición entre filas incompletas no viola la unicidad: en SQL dosNULLno son iguales.filas_evaluadascuenta esas filas yfilas_totalesla tabla entera. Su estado es"verificada","refutada","no_verificada"cuando no se pudo comparar, o"sin_casos_evaluables"cuando ninguna fila tiene la clave completa: ahí la unicidad sería cierta sobre un conjunto vacío, que es cierto y engañoso a la vez, y por eso tiene estado propio.ausencia_nulosresponde si todos los componentes están presentes. Su hallazgo asociado,clave_con_ausentes, cuenta las filas con al menos un componente ausente y conserva sus índices.clave_no_unicasólo se emite cuando hay valores repetidos entre las filas completas y usafilas_evaluadascomon_evaluados; no convierte una colisión entre ausentes en una violación de unicidad.trazabilidadconserva la semántica de R, que es la que localiza las filas, e informa encolisiona_con_ausentessi el localizador queda ambiguo porque dos filas con ausentes comparten representación.
Details
Los umbrales de faltantes se aplican a la suma de ausentes reales y
faltantes disfrazados y son estrictos: la proporción debe superar el umbral
para generar el nivel correspondiente. La lista de cadenas está congelada con referencia a
naniar::common_na_strings 1.1.0 y suma extensiones habituales en datos
administrativos uruguayos. Las entradas que naniar expresa como patrones
escapados se adaptan a los signos literales de interrogación, asterisco y
punto porque aquí se comparan por igualdad. La lista no depende de la
versión instalada.
Los sentinelas numéricos predeterminados son -9, -99, -999, -9999 y
999. La lista es deliberadamente más corta que
naniar::common_na_numbers 1.1.0:
66, 77, 88 y 9999 también pueden
ser edades, códigos o años legítimos. sentinelas_numericos representa la
política completa, no una lista que se agrega silenciosamente: use
numeric() para desactivar todos los sentinelas numéricos, o
sentinelas_naniar para solicitar explícitamente la lista de naniar. La
política se normaliza como un conjunto numérico: el orden, el tipo entero o
doble, los duplicados y los NA no cambian su interpretación.
muestra limita el descubrimiento de patrones, la inferencia de tipos, la
detección de formatos de fecha y la búsqueda de dependencias funcionales.
Las demás métricas y hallazgos se calculan sobre todas las filas. En los
resúmenes que convierten texto con el tipo o formato descubierto en esa
muestra, los valores presentes que no se pueden convertir no se ocultan:
n_fechas_excluidas_granularidad los cuenta para fechas y
n_valores_excluidos_resumen para números, y el estado deja de ser un
cálculo completo. n_valores_excluidos_resumen cuenta también los valores no
finitos que el resumen deja afuera, y lo hace en cualquier clase: una columna
de fechas con un Inf publica n_valores_excluidos_resumen = 1,
n_fechas_resumidas sin contarlo y el estado
"calculados_sobre_valores". Y para las longitudes de texto,
n_longitudes_resumidas declara sobre cuántos valores se calcularon
longitud_minima, longitud_maxima y longitud_media: nchar() no puede medir
un valor cuyos bytes no son UTF-8 válido, y ése queda fuera del promedio. La causa
viaja aparte, en n_codificacion_invalida. Por eso
meta$filas_analizadas describe el máximo usado por los análisis muestreados,
no el alcance del perfil completo.
En cada fila de columnas, n_filas_analizadas_tipo y
muestreado_tipo_inferido declaran el alcance concreto de
proporcion_tipo_inferido; no debe interpretarse esa proporción como si
hubiera usado necesariamente toda la columna.
El tipo se infiere de los valores sólo en las columnas de texto: en las
demás es el tipo con que R las guarda —lógico, entero, doble, fecha—, que no
depende de los valores. Por eso una columna no textual sin ningún valor
conserva su tipo de almacenamiento, con proporcion_tipo_inferido en NA
porque ningún valor lo midió, y una columna de texto sin valores queda
"desconocido", porque ahí no hay nada de qué inferirlo.
Cuando el tipo inferido es "fecha" o "fecha-hora",
estado_tipo_inferido declara además cómo quedó establecida esa lectura:
"confirmado" si todo formato con casamientos es inequívoco, y
"candidato" si el veredicto se apoya en casamientos ambiguos — el caso de
una columna donde proporcion_tipo_inferido llega a 1 sin un solo valor
inequívoco. En los demás tipos la columna queda NA. El resumen impreso
anota (candidato) junto al tipo cuando corresponde; la conversión, el
rango temporal y la remediación ya exigían formatos confirmados y no
cambian.
En una columna temporal, minimo, maximo, media y mediana quedan en
NA y su valor viaja en minimo_fecha, maximo_fecha, media_fecha y
mediana_fecha, que son texto legible. desvio es la excepción y conviene
saberlo: no es un momento sino una duración, así que se informa como número,
y ese número está en segundos tanto para "fecha" como para
"fecha-hora", porque las dos clases se unifican en esa unidad antes de
resumirlas. Un desvío de 136610.4 sobre una columna de fechas son 1,6 días.
Ese detalle importa al comparar las dos puertas. perfilar_dbi() no clasifica
tipos: informa el que declara el motor, y un motor que no preserva DATE ni
BOOLEAN —SQLite guarda ambos como número— hace que esas columnas se midan
como números. La misma columna de fechas da entonces desvio en días por la
puerta DBI y en segundos por ésta, y su moda sale como el entero crudo del
motor en vez de la fecha formateada. No es una discrepancia de cálculo: cada
puerta describe lo que tiene delante, y lo que tiene delante es distinto.
Una columna cuyo año se expresa con dos dígitos se informa con su
tipo_inferido —"fecha" o "fecha-hora"— pero deja minimo_fecha,
maximo_fecha, media_fecha y mediana_fecha en NA. No es una omisión:
23 puede ser 1923 o 2023, y elegir el siglo para calcular un rango sería
inventarlo. El hallazgo anio_de_dos_digitos señala esas columnas, y el
rango aparece una vez que el usuario resuelve la ambigüedad.
Lo mismo vale para un valor que dos formatos confirmados de la misma columna
leen distinto: en una columna que tiene 13/06/2020 y 06/30/2020, el valor
01/12/2020 puede ser el 1 de diciembre o el 12 de enero, y no se decide por
la mayoría. Queda fuera del rango, y planificar_limpieza() bloquea la
conversión de la columna mientras queden valores así. El resumen de fechas
usa la misma regla de validez que la detección: un 99991231 que el formato
%Y%m%d no admite tampoco llega a maximo_fecha.
Cuando no sobrevive ningún valor utilizable, el estado distingue dos
afirmaciones que no son la misma: "sin_valores" dice que la columna no tenía
ningún valor presente en el universo que el resumen mide —vacía, todo NA,
o con la aplicabilidad declarada dejando ese universo sin valores— y
"sin_valores_utilizables" dice que sí los tenía y ninguno servía para el
resumen. La relación que se cumple con la fila es
n_aplicables - n_faltantes, no n - n_faltantes: con aplicabilidad declarada
los valores de afuera del universo se declaran en
n_presentes_fuera_de_aplicabilidad y no cuentan para este estado. Una columna de treinta Inf es el
segundo caso y no el primero: publica n_faltantes = 0 y n_distintos = 1,
así que decir «sin valores» de ella contradiría a su propia fila. La otra puerta
del segundo caso son los centinelas que se llevan todos los valores. La
presencia se define igual que en n_faltantes, que cuenta el NaN como
ausente: una columna de treinta NaN es "sin_valores".
La media se acumula por bloques y puede diferir de mean() en los últimos
bits; la mediana es exacta. El paquete no promete exactitud bit a bit para
la media —sí para la mediana, que se calcula con median() sobre el vector
entero y por eso no se reemplazó por el cuantil 0,5—. Medido: sobre veinte mil
valores de 1e12 y veinte mil de 1e-6 la diferencia es de 6,1e-05, y sobre
treinta mil valores con signo del orden de 1e9 es de 2,3e-10; con valores de
magnitud parecida no hay diferencia en ningún bit. Quien rehaga mean() sobre
una columna grande y vea otro último dígito está viendo esto y no un error.
La unidad viaja al lado de la cifra
El campo unidad dice en qué unidad están las cifras que la fila publica
—minimo, maximo, media, mediana, desvio—, y queda en NA cuando la
columna no declara ninguna. Es una sola pregunta, y por eso no se confunde con
numero_texto_unidad, que es la unidad que un valor de texto traía pegada al
número ("12 kg"), ni con la unidad de clasificar_variables(), que es la
unidad declarada del dato.
Sin este campo, cuatro columnas medidas en unidades distintas publicaban filas
idénticas: units::set_units(c(1, 2, 3), "m") y la misma en "km" dan las dos
media = 2 y desvio = 1, y un Period de dos días publicaba 172800 sin
decir que eran segundos. Por clase:
unitsydifftime: la unidad declarada, en forma canónica (km/h,m^2,kg*m/s^2).difftimepublica sus estadísticos en esa unidad: una columna de minutos publicamedia = 75conunidad = "mins". Se abstuvo mientras no existía dónde publicar la unidad —un75sin decir de qué no es una medida—, y era el único que se abstenia:DateyPOSIXtpublicandesvioen segundos desde antes. Los valores van en la unidad declarada por la columna, sin convertir a segundos: convertirlos publicaría un número que no está en la columna.Period,Duration,DateyPOSIXt:"segundos", que es la unidad en la que saledesvioy que antes sólo estaba dicha en prosa.cualquier otra columna que declare un atributo
units—eso circula como metadato en datos importados— publica lo declarado, porque sus cifras están en esa unidad.el resto:
NA. El campo no inventa una unidad donde no hay ninguna.
Y se calla cuando la fila no publica ninguna cifra cuantitativa, o sea
cuando estado_resumen_cuantitativo vale "no_aplica": el atributo units lo
puede llevar cualquier clase, y un factor con attr(x, "units") <- "kg"
publicaba unidad = "kg" al lado de media = NA. Una unidad de cifras que no
existen no contesta la pregunta del campo. El discriminador es ese estado y no
una lista de clases, así que una columna units de puros NA sí declara
su unidad: su clase se resume, y la unidad dice en qué estarían sus cifras.
Si una columna declara más de una unidad —un atributo units de largo
mayor que uno, que ningún objeto units produce pero un dato importado sí—,
el campo las publica todas separadas por coma. Ese valor no es una unidad: dice
que la declaración está mal. Quedarse con la primera sería elegir una por el
usuario, que es el defecto que este campo vino a cerrar.
Y comparar_perfiles() compara este campo como un aspecto propio, con la
severidad de un cambio de tipo: publicar la unidad sin compararla dejaba en pie
el defecto entero, porque el problema era que una entrega donde cambió la
unidad no mostraba cambio.
Una columna de períodos expresados sólo como mes y año informa la
granularidad "mes" en formatos_fecha y deja los resúmenes de fecha en
NA con estado_resumen_cuantitativo = "granularidad_incompleta": asignar
el día 1 para obtener un mínimo o una media también sería inventar un dato.
Si esos períodos son minoritarios dentro de una columna que también contiene
fechas con día, los resúmenes se calculan sólo sobre las fechas completas y
declaran estado_resumen_cuantitativo = "calculados_sobre_dias", junto con
n_fechas_resumidas y n_fechas_excluidas_granularidad. El mínimo y el máximo
son entonces condicionales al subconjunto con día; no representan un rango
de toda la columna.
Los dos conteos que acompañan a ese estado cuentan cosas distintas.
n_fechas_excluidas_granularidad cuenta sólo las fechas que quedaron
fuera por ser de granularidad mes, y queda en NA cuando los formatos se
descubrieron sobre una muestra: ese número se deriva de los formatos
detectados en la muestra mientras el resumen recorre la columna entera, así
que atribuir la causa exigiría rehacer la detección sobre todo, que es el
costo que el muestreo existe para evitar. n_valores_excluidos_resumen es el
campo general —el mismo que usan las columnas numéricas— y cuenta todo
valor presente que no sostiene el resumen, sea un período de mes o un texto
que ningún formato pudo leer. Siempre que ese total sea mayor que cero, el
perfil agrega una fila resumen_cuantitativo en cobertura_diagnosticos,
haya muestreo o no.
Para números ordinarios, los estadísticos cuantitativos se calculan sólo con
valores finitos; n_nan, n_infinito_positivo y n_infinito_negativo
declaran lo excluido. En columnas integer64 que exceden el entero máximo
representable exactamente por double, minimo y maximo quedan en NA y
los extremos exactos se conservan en minimo_exacto y maximo_exacto.
Cuando la conversión de texto deja valores presentes afuera —haya muestreo o
no, porque la conversión descarta lo que no puede leer en los dos casos—,
n_valores_excluidos_resumen y
estado_resumen_cuantitativo = "calculados_sobre_valores" declaran ese
alcance parcial.
Los limites de Tukey se publican solo cuando hay al menos 20 valores finitos
y el recorrido intercuartilico es positivo. Si falta alguna condicion,
n_outliers queda en NA y cobertura_diagnosticos explica el motivo y
como reunir evidencia suficiente; un IQR cero no convierte una categoria
minoritaria en outlier.
El paquete distingue lo que el usuario declara de lo que él mismo
sospecha, y esa distinción gobierna los resúmenes. Cuando se reemplaza
sentinelas_numericos, esos valores son una declaración de ausencia y salen
de media, mediana, minimo, maximo, desvio y n_outliers, igual que
salen los NA y las filas que aplicabilidad deja fuera del universo;
n_valores_excluidos_resumen y estado_resumen_cuantitativo declaran ese
alcance. La lista por omisión —-9, -99, -999, -9999 y 999— es en
cambio una conjetura del paquete: se informa como faltantes_disfrazados
pero no se retira de los resúmenes, porque 999 también puede ser un
dato legítimo y actuar sobre una sospecha cambiaría números que nadie pidió
cambiar.
moda, frecuencia_moda, n_distintos, tasa_distintos y longitud_*
describen la representación almacenada y siguen contando esos valores,
del mismo modo que cuentan un Inf que los estadísticos excluyen o un
"N/A" textual. Por eso moda puede quedar fuera del rango que informan
minimo y maximo: no es una contradicción sino la diferencia entre
describir lo guardado y resumir lo que vale como dato, y los conteos
—n_valores_excluidos_resumen, n_infinito_positivo,
n_faltantes_disfrazados— dicen cuánto separa a los dos.
Una columna de listas intenta contar sus valores distintos; si la clase no
admite comparación, informa NA en lugar de afirmar cero.
Las columnas compuestas —matrices o arreglos de más de una dimensión— se
conservan como una unidad por fila: n informa las filas de la tabla, pero
los estadísticos por valor quedan en NA y un hallazgo explica que deben
separarse en columnas con semántica explícita.
Cuando todos los valores válidos aparecen una sola vez no hay moda, y
moda queda en NA. Lo que se publicaría es el ganador de un desempate de
tantas vías como valores haya, y ese desempate sigue el orden de
ordenamiento, que depende de cómo esté guardada la columna: la misma columna
c(-5.5, -1, 0, 3.75) daba -5.5 como número y -1 como texto.
frecuencia_moda se conserva —vale 1, es cierto y es la evidencia de por qué
la moda quedó callada—. Con un solo valor distinto sí hay moda, aunque su
frecuencia sea 1: ahí no hay empate que resolver.
Una columna numérica emite valor_concentrado como señal sospechoso cuando
tiene al menos 20 valores válidos y 10 valores distintos, y su moda tiene
una frecuencia al menos cinco veces mayor que la del segundo valor más
frecuente y representa al menos 0,15 de los valores válidos. La elegibilidad
es parte de la señal: una columna con menos categorías no recibe una fila de
cobertura_diagnosticos, porque allí la moda es la distribución. La
evidencia publica el valor modal, ambas frecuencias, el cociente y la
fracción. M2 fue la regla seleccionada en
.trabajo-agente/medicion-concentracion.md: produjo cero falsos positivos en
114 columnas limpias, con un margen medido de 2,2 veces. Es una sospecha, no
una acusación: un valor legítimo puede dominar. Sus puntos ciegos medidos
son las concentraciones por debajo de 15 % y los empates naturales en
columnas enteras pequeñas, donde el cociente puede quedar por debajo de
cinco.
La ley de Benford se evalúa sólo en columnas numéricas con al menos 50
valores finitos; las columnas compuestas —matrices o arreglos de más de una
dimensión— no son magnitudes por fila y quedan fuera del análisis. Antes de
comparar exige variación, que la columna no parezca un
identificador ni una secuencia correlativa, al menos 100 observaciones
positivas utilizables, una proporción de positivos igual a 1 y tres órdenes
de magnitud según log10(max/min). Si falla alguna precondición no emite un
hallazgo: la enumera en cobertura_diagnosticos. Si aplica,
meta$benford$resultados conserva la distribución observada y esperada por
primer dígito, el chi-cuadrado de Pearson, ocho grados de libertad y el valor
p; meta$benford$umbrales publica todos los cortes. Un valor p menor que
0.01 genera desviacion_benford como señal descriptiva para revisar, no
como evidencia de fraude o manipulación. Topes administrativos, redondeos,
precios psicológicos y subsidios de monto fijo son explicaciones posibles.
Las relaciones aritméticas se buscan sólo entre columnas numéricas sin
clase declarada y con variación: una columna que declara una clase propia
—Date, POSIXt, difftime, integer64, units, lubridate::Period o
cualquier otra— no participa, porque sumarla o dividirla no es la aritmética
de un doble; el texto numérico y las columnas constantes tampoco. Las
Las columnas que publican cifras cuantitativas y quedan fuera por su clase
se declaran una por una en cobertura_diagnosticos: integer64, units,
lubridate::Period, haven_labelled y difftime. El criterio no es
is.numeric() —que responde FALSE sobre un difftime— sino si la fila
publica minimo, maximo y media como números: ahí la ausencia de hallazgo
se lee como conformidad y por eso hay que declararla. difftime entró en esa
categoría cuando dejó de abstenerse del resumen cuantitativo, y sin declararlo
quedaba mudo en los dos diagnósticos mientras units los declaraba —la misma
columna, con las mismas cifras, tratada de dos maneras—.
Una fecha y una hora no entran en esa declaración, y eso es deliberado:
no publican media como número pelado —su resumen sale en media_fecha y
compañía—, así que su silencio se lee distinto, y no son magnitudes para estos
diagnósticos. Quedan fuera del mismo modo que una columna de texto, sin fila
propia. meta$aritmetica_columnas publica
las clases medidas en esta tabla. La ley de Benford sigue el mismo criterio y
la misma declaración. Cada relación requiere al
menos tres filas con valores finitos en todas las columnas involucradas;
los NA, NaN e infinitos quedan fuera del universo que publica la
evidencia. Para cada terna se prueban las tres orientaciones de una identidad
aditiva; esto cubre sumas y sus restas equivalentes sin informar tres veces
la misma igualdad. En pares proporcionales, k es la mediana de los cocientes
finitos cuya base no es cero, pero el cumplimiento se evalúa después también
en las filas con base cero. Si una identidad aditiva ya relaciona una terna,
se omiten las proporcionalidades redundantes entre su total y sus sumandos;
se conserva la proporcionalidad entre los dos sumandos. Una regularidad
completa se informa con severidad "ok"; si alcanza el umbral pero tiene
discrepancias, sigue el criterio de las relaciones de orden y es
"sospechoso". Todo esto describe evidencia observada: no declara una regla
del dominio ni autoriza una corrección.
Los valores de texto que no forman UTF-8 válido tampoco se convierten: se
cuentan, se excluyen de los análisis textuales y generan un hallazgo con sus
posiciones. Los diagnósticos de invisibles incluyen controles C0/C1,
espacios Unicode, marcas direccionales, BOM y otros caracteres de transporte.
La evidencia los muestra como puntos de código; los espacios Unicode se
detectan aunque sólo se normalizan mediante una acción explícita, y ZWJ/ZWNJ
se informan pero se conservan porque pueden ser semánticos. La comparación
de duplicados con normalizar = TRUE aplica estas mismas clases sin borrar
ZWJ/ZWNJ.
Un valor double subnormal —distinto de cero y menor que
.Machine$double.xmin en valor absoluto, unos 2,2e-308— casi siempre sale de
reinterpretar un patrón de bits o de un desbordamiento por defecto. El
hallazgo valores_subnormales los cuenta y da sus filas, con severidad
sospechoso. El caso que motiva el diagnóstico es leer una tabla donde una
columna de enteros grandes se escribió como doble reinterpretando los bits:
los números salen del orden de 1e-314 y, al venir así desde el origen,
ninguna comprobación cruzada los contradice.
No es concluyente, y el hallazgo no lo afirma. Un cálculo que desborda
por defecto cae legítimamente en ese rango —2^-1050 da 8,3e-317—, y una
columna de p-valores o de verosimilitudes muy pequeñas puede dispararlo sin
que haya nada roto. Lo que se publica es el hecho medido —cuántos valores son
subnormales y en qué filas—, no un veredicto sobre su origen.
Los resúmenes de fecha-hora se expresan siempre en UTC y llevan el sufijo
UTC en el texto para hacer visible la zona aplicada. El instante se
conserva aunque la columna de entrada use otra zona horaria. Las columnas
POSIXt declaran zona_horaria_origen y
n_filas_fecha_civil_distinta_utc, que cuenta filas cuya fecha civil cambia
al mostrar el instante en UTC. Cuando la fecha civil cambia se emite un
hallazgo zona_horaria_fecha_hora; si la zona de origen no está declarada,
el conteo queda en NA y cobertura_diagnosticos declara que no se evaluó.
La zona se declara en la columna original, por ejemplo:
attr(x, "tzone") <- "America/Montevideo".
El diagnóstico de formas Unicode compara sin modificar el texto y puede usar
el paquete opcional stringi para enriquecer la evidencia cuando existen
caracteres no ASCII. columnas$unicode_evaluado declara si esa comprobación
pudo ejecutarse; en texto no ASCII sin stringi queda FALSE,
n_variantes_unicode queda en NA y cobertura_diagnosticos informa la
dependencia ausente. Las columnas ASCII se evalúan siempre y conservan cero.
El perfil de comparación es completamente R base.
Hay un segundo caso en que unicode_evaluado queda en FALSE, y no es una
dependencia ausente: cuando todos los valores de la columna traen bytes
que no forman UTF-8 válido, no queda texto que analizar. Ahí las pasadas que
dependen de texto legible no llegan a correr, así que n_codificacion_rota,
n_codificacion_reparable, n_codificacion_reparable_parcialmente,
n_codificacion_irreparable, n_codificacion_no_se_pudo y
estado_codificacion_reparacion quedan en NA en vez de en cero: un cero
afirmaría no haber encontrado nada, y lo cierto es que no hubo nada que
mirar. n_codificacion_invalida sigue contando esos valores y el hallazgo
codificacion_invalida, de severidad error, sigue declarando que se
excluyeron.
Esas cifras no son una partición. n_codificacion_rota cuenta cada valor
afectado una sola vez; n_codificacion_reparable y
n_codificacion_reparable_parcialmente, los que el motor reparó del todo o
en parte; n_codificacion_irreparable, los que traen el carácter de
reemplazo U+FFFD, cuyo original ya no está; y n_codificacion_no_se_pudo,
los que el motor reconoce como mal convertidos y no pudo reparar. Un valor
con U+FFFD suele contar en las dos últimas, así que sumarlas no da
n_codificacion_rota.
Los nombres de columna que el perfil publica —en columnas$columna y en
todo lo que nombre una columna: claves, relaciones, dependencias, hallazgos,
patrones, el plan de remediación y el reporte— son los nombres de la tabla de
entrada tal como llegaron, con sus bytes y su marca de codificación. Eso
significa que sirven para indexar esa misma tabla —datos[[nombre]]— sea
cual sea el LC_CTYPE de la sesión. El paquete deriva una representación de
trabajo para comparar y desambiguar internamente, pero no la publica: dos
nombres con bytes distintos siguen siendo dos columnas distintas.
Al componer texto publicado —en particular las sugerencias de
posible_ausencia_estructural— usa una copia marcada como UTF-8 del nombre,
sin modificar el nombre conservado en la tabla. Por eso una sugerencia como
perfilar(datos, aplicabilidad = list(entero = ~ categoría == "categoría"))
conserva el nombre real también bajo LC_CTYPE = "C" y se puede copiar y
pegar.
La identidad de la columna no depende de esa exclusión: n_distintos,
moda y frecuencia_moda comparan la representación almacenada, que se
puede distinguir sin decodificarla. Un valor ilegible cuenta como valor, y
la moda que se publica es el valor original tal como llegó, sin
reinterpretarlo. Cuando ese valor es numérico, su representación textual se
formatea con notación fija, con la precisión completa e independiente de
scipen y de digits; se conserva
OutDec, porque esa marca decimal sí pertenece a la preferencia de locale
de quien usa el paquete.
Las columnas sfc declaran su CRS, los tipos concretos y la dimensión
(XY, XYZ, XYM o XYZM), además de geometrías vacías, validez, dominio y
caja envolvente. POINT/MULTIPOINT, LINESTRING/MULTILINESTRING y
POLYGON/MULTIPOLYGON son una misma familia: el hallazgo de tipos mixtos
aparece sólo al combinar familias o ante GEOMETRYCOLLECTION.
La validez se calcula siempre con GEOS en el plano, sin CRS, para que el
resultado no dependa de que s2 esté instalado; validez_criterio publica
"planar" y n_validez_evaluados publica su universo, que incluye las
geometrías vacías porque GEOS sí devuelve su validez. Si hay una dimensión
M, se aplica sf::st_zm() y se valida la topología XY;
validez_preprocesamiento = "st_zm(x)" declara esa transformación. Si el
CRS es geográfico, un fallo es sospechoso y no un error, porque no afirma
invalidez esférica. Las dimensiones Z y M no se evalúan como medidas:
dimensiones_no_evaluadas las enumera y cobertura_diagnosticos deja
constancia explícita incluso cuando la validez XY sí pudo calcularse.
En un CRS geográfico el dominio exige longitudes en [-180, 180] y latitudes
en [-90, 90]. Además, las coordenadas se comparan en longitud/latitud con
la BBOX del área de uso incluida en el WKT del CRS. Este control puede
detectar valores en unidades incompatibles —por ejemplo, grados declarados
como metros—, pero no una zona UTM equivocada cuando esa interpretación cae
dentro del área de la zona declarada. Una BBOX mundial es un no-op válido;
si el WKT no permite extraer la caja, el dominio queda en NA y
cobertura_diagnosticos lo declara en lugar de suponer el mundo entero.
Las geometrías vacías se cuentan aparte y no integran el universo del dominio
ni de la bbox. n_dominio_evaluados y n_bbox_evaluados publican ambos
universos; la bbox se calcula sobre coordenadas crudas de geometrías no
vacías, incluidas las que el dominio marque fuera, y bbox_alcance lo
declara. Si todas son vacías, sus conteos evaluados y fuera de dominio son
cero. Sin CRS,
n_fuera_de_dominio queda en NA y se emite crs_no_declarado: nunca se
supone EPSG:4326. Si falta el paquete opcional sf, todos esos campos quedan
en NA, no se emite un hallazgo geométrico y cobertura_diagnosticos
registra la dependencia ausente.
Una columna cuyos valores son data.frame o matriz —lo que devuelve un driver al
leer un STRUCT— declara n en filas, como cualquier otra: tiene tantos
valores como la tabla, y lo que no se puede es analizarlos como texto, que se
declara aparte en cobertura_diagnosticos.
Con un universo aplicable declarado, los conteos y los índices de geometría se
restringen a ese universo, igual que el resto del perfilado: una geometría que la
regla dejó afuera no cuenta como vacía, ni como inválida, ni como fuera de dominio,
y n_geometrias, n_dominio_evaluados y n_validez_evaluados describen el
universo. Tres campos son la excepción y conviene saber por qué:
n_bbox_evaluados, n_geometrias_analizadas y n_vertices_analizados cuentan
el trabajo que se hizo, no filas de un universo —el análisis geométrico corre
sobre la columna entera, porque necesita su CRS y sus tipos—, así que se publican
tal como se midieron. Recortarlos a ojo sería inventar un número: no hay conjunto
de índices del que derivarlos.
Una columna de texto puede ser una geometría. Se reconoce como WKT o WKB
(crudo o hexadecimal) cuando la mayoría de una muestra de 20 valores tiene esa
forma; si después hay valores que no se pueden convertir, la pérdida se declara
en motivo_representacion. Esa columna se sigue perfilando como texto en lo que
toca a su escritura —espacios, codificación, invisibles—, pero no recibe los
diagnósticos que leen los valores como palabras o como claves —la proximidad de
vocabulario, posible_identificador, patron_raro—: cobertura_diagnosticos
declara que no aplican.
El argumento normalizar declara el perfil de comparación que se conserva
en meta$normalizacion; cambia sólo la representación usada para comparar,
no el texto guardado. TRUE usa el perfil predeterminado, FALSE desactiva
sus pasos configurables, "amplio" activa los tres pliegues optativos y
normalizacion() permite declararlos. También admite una lista nombrada por
columna. meta$normalizacion_fusiones informa, para cada paso activo, la
diferencia entre los valores distintos con el perfil completo y los valores
distintos con ese paso apagado y todo lo demás igual. Esa comparación
responde cuánto aporta cada paso por separado; sus números no son aditivos
porque dos pasos pueden fundir el mismo par. n_distintos y
n_distintos_normalizados declaran el total antes y después del perfil
completo. La descomposición canónica es siempre activa y forma parte de la
línea base, no una fila configurable.
Con normalizar = FALSE no hay pasos configurables que medir y el informe
de fusiones queda en NULL; en un perfil por columna sólo se incluyen las
columnas con algún paso activo. Si detectar_duplicados_aproximados() recibe
un perfil, puede reutilizar este informe ya calculado.
El informe usa el vocabulario completo: n_distintos y n_usados son el
número de valores distintos realmente comparados y el estado es exacto.
Las fusiones son una propiedad de pares, por lo que muestrear valores
aislados podría dejar fuera los dos miembros de cada par y convertir una
fusión real en un cero falso. La normalización se vectoriza para que este
alcance completo no dependa de la cardinalidad de la columna.
Las columnas sin ausentes que tienen al menos 100 filas y 90 % de valores
distintos producen un hallazgo casi_clave cuando el valor dominante
concentra al menos la mitad de los duplicados excedentes. La concentración
se calcula sobre las repeticiones posteriores a la primera de cada valor, no
sólo sobre la tasa de distintos. La evidencia declara el mínimo de filas,
ambos umbrales, los valores que colisionan y sus frecuencias. Las variables
con rol propuesto fecha, incluidas fecha-hora, se excluyen. Así una colisión
concentrada queda separada del texto libre de alta cardinalidad con
repeticiones dispersas.
Un vector double sólo participa si todos sus valores finitos son enteros;
así se conservan identificadores importados con ese almacenamiento y se
excluyen medidas con alguna parte fraccionaria, como importes o coordenadas.
Los vectores integer64 se tratan como enteros semánticos. La evidencia del
hallazgo declara este criterio y el recuento observado.
Además, si casi_duplicados_vocabulario = TRUE, el perfil busca variantes
casi duplicadas en columnas de texto. Agrupa el vocabulario crudo mediante
fusiones exactas de la normalización y estrellas de distancia centradas en
un valor de frecuencia estrictamente mayor y único; los empates no se fuerzan.
No cierra cadenas transitivamente ni elige una forma canónica. La unidad es
el valor distinto, no la fila, y cada variante
conserva su frecuencia. Cada grupo declara sus distancias mínima y máxima.
El límite max_proporcion_grupo_vocabulario evita presentar un grupo que
abarque casi toda la columna como un diagnóstico útil: en ese caso el alcance
dice que el diagnóstico no aplica. Ese límite se activa desde 20 valores
distintos o cuando el grupo mayor ya tiene 10 variantes; sólo suprime el
grupo si además ocupa una fracción mayor que el umbral. Así un grupo de tres
en cuatro valores se entrega, pero quince variantes que ocupan toda una
columna no se presentan como una sola familia. Un grupo grande dentro de un
vocabulario mucho mayor puede seguir pasando si su proporción es pequeña. El
alcance expone ambos cortes. El argumento permite apagar el detector cuando
no corresponde a la tabla. Si hay pares cercanos pero todas las frecuencias
empatan, el alcance declara que no hubo asimetría para formar una estrella y
sugiere detectar_duplicados_aproximados() para comparar filas. El alcance
clasifica cada grupo como normalizacion_exacta, dentro_de_palabra,
token_completo, token_unico, mixta o indeterminada. La primera indica
una coincidencia tras normalizar; dentro_de_palabra, una diferencia dentro
de un token; token_completo, diferencias en tokens completos;
token_unico, que ambos valores son un único token y la clase estructural no
aplica; mixta, aristas de más de una clase; e indeterminada, que no hubo
aristas clasificables. Son evidencia descriptiva, no una decisión sobre
identidad. El agrupamiento no cambia por esa etiqueta. El alcance
también descarta aristas de distancia cuyos números no coinciden. Se comparan
las secuencias numéricas, quitando ceros de relleno y separadores de miles;
una diferencia numérica se trata como otra entidad. Esto puede dejar sin
agrupar una errata dentro de un número, porque no hay evidencia para
distinguirla de dos entidades reales. El alcance declara los valores y pares
comparados, los recortes por cardinalidad y si stringdist no estuvo
disponible; también informa cuántas aristas se descartaron por esa regla y
el tamaño compatible después del filtro. El tamaño máximo usado para el
límite conserva el componente potencial antes del filtro numérico, para que
una familia de entidades numeradas no vuelva a presentarse como una sola
variante; el tamaño compatible se muestra aparte. Para mantener acotado el
perfil, por omisión se evalúan hasta
5.000 valores distintos y 2.000.000 de pares de unidades normalizadas; si
se alcanza un límite, la evidencia lo declara y no presenta el resultado
como universo completo. Las fusiones exactas se informan aun sin ese paquete
opcional.
Antes de formar esos grupos se retiran los valores que el mismo perfil ya
informó como faltantes_disfrazados. El diagnóstico fuerte de ausencia tiene
precedencia: un centinela no se presenta también como posible variante de un
valor válido. El alcance declara cuántas observaciones retiró este filtro.
La corazonada de centinelas numéricos se apaga sobre una numeración
limpia, para no llamar ausencia a un código válido. densa responde sólo
a la cobertura del rango —densidad_secuencia_entera por encima de su
umbral—; moda_sobresale_secuencia_entera es una señal independiente y no
cambia los tres escudos de forma que dependen de la numeración.
En una columna integer64, bit64 se carga de manera diferida si está
instalado para registrar sus métodos antes de medir. Si no está instalado,
las cinco medidas públicas de la secuencia quedan en NA y
cobertura_diagnosticos declara que secuencia_entera no se evaluó por
falta de esa dependencia.
Lo mismo pasa si la columna tiene valores por encima de 2^53: en un doble se
redondean, la densidad no se mide sobre números redondeados, y la secuencia
queda en NA y declarada, no como «no densa».
La guarda vuelve a abrirse si un candidato presente queda fuera del rango de
los valores restantes de la numeración, o si la frecuencia del candidato es
la moda sobresaliente. Esta segunda señal se mide por el salto entre
frecuencias consecutivas, ordenadas de mayor a menor y mirando sólo las
primeras posiciones. Es el mismo idioma que salto_de_escala_secuencia_entera
usa con los huecos: lo que delata a un centinela no es que su frecuencia sea
grande, sino que haya un acantilado entre el grupo que sobresale y el resto
de la distribución.
Comparar contra un valor concreto no alcanza, y cada intento lo rompió un caso distinto: contra el segundo valor, un señuelo legítimo y frecuente infla el segundo puesto; contra la frecuencia típica exigiendo forma de numeración, un centinela masivo sobre una clave foránea nunca la tiene; y con las dos juntas, varios centinelas empatados se cubren entre sí —tres valores con la misma frecuencia dejan «máximo igual al segundo» y la comparación vacía—. El acantilado ve los tres porque no le importa quién es el segundo sino dónde se corta la distribución.
Se miran sólo las primeras posiciones porque el grupo que sobresale está arriba: en la cola, una caída de dos apariciones a una da un salto que no dice nada de la columna. Medido sobre las diez columnas numéricas del banco real y dos formas de clave foránea, ninguna pasa de 1,71; los casos que hay que atrapar van de 4,75 a 249. Perder la condición de numeración no genera un hallazgo por sí solo: sólo devuelve la columna a la mirada de la lista de centinelas. En una numeración compacta, un candidato fuera de rango también abre la guarda aunque su frecuencia no forme un acantilado.
El factor de frecuencia sólo califica la señal moda_sobresale: una caída
menor no abre esa vía, pero tampoco bloquea la vía independiente del rango.
Así, un candidato fuera de rango se informa aunque aparezca cuatro veces y
no forme un acantilado. Lo que el usuario declara con sentinelas_numericos
atraviesa la guarda y se informa con severidad error, que es la regla
general del paquete: excluye lo que se declara e incluye lo que sospecha.
La clasificación de posibles datos personales es más amplia que la
protección. Cada clasificación declara poder_discriminante y proteger:
debil: una forma genérica, como siete a doce dígitos, coincide también con importes, facturas y códigos; se informa pero no se ocultan valores;medio: el nombre de la columna expresa una categoría personal (por ejemplotelefono,fecha_nacimientoofecha_fallecimiento); se protege aunque sus valores no se puedan validar. El nombre tiene prioridad sobre una forma numérica genérica y también determina la etiqueta de tipo. También esmediouna columna que trae correos completos sin que sean la mayoría de sus valores: un correo es un correo aunque esté solo, y la columna se protege;alto: una forma muy específica, como un correo, o nombre y forma se apoyan mutuamente; se protege;verificado: al menos tres valores distintos y al menos el 90% cumple uno de los validadores personales configurados; se protege incluso sin un nombre orientador. La proporción medida —la que se compara contra ese umbral— se publica enproporcion_verificada, para que una columna con el 90% no se lea igual que una con el 100%. El pack uruguayo es el predeterminado, pero puede reemplazarse por unpack_validadores()de otro país o desactivarse conFALSE. La tolerancia del 10% permite tipeos aislados sin convertir una columna real en una salida pública; el umbral es configurable.
La forma genérica de siete a doce dígitos tiene poder discriminante débil: también describe importes, teléfonos, facturas e identificadores. Las formas con separadores sólo se aceptan cuando tienen una estructura de documento reconocible (por ejemplo, una cédula con grupos y guion o un RUT con grupos de tres y cuatro dígitos); una fecha ISO, una fecha con puntos o guiones y separadores arbitrarios no se consideran documentos. Un validador de dígito que supera el umbral aporta evidencia verificable y eleva la clasificación; una forma sola nunca se trata como prueba de identidad.
Este criterio mide capacidad de discriminación, no juzga si la presencia del
dato es correcta. La protección sustituye modas, ejemplos, evidencia y
extremos o medianas que corresponden a observaciones reales. Los desvíos se conservan; las medias se protegen en sus dos formas
(media y
media_fecha), porque la media de una columna personal puede reconstruir
demasiado. Los desvíos siguen siendo síntesis no ligadas a una fila;
detalle_proteccion_personal hace visible la supresión. En fechas de
nacimiento y fallecimiento, un hallazgo separado conserva el diagnóstico de
valores anteriores a 1900 o posteriores a la corrida sin publicar las fechas.
Los números escritos como texto reconocen tanto coma como punto decimal y
sus separadores de miles simétricos. Los prefijos de tres letras separados
del número, incluso como sufijo, U$S y los símbolos monetarios se
conservan como evidencia; monedas_mixtas informa sus frecuencias sin
convertir ni suponer tasas de cambio. Una única moneda o un símbolo $
aislado no produce ese hallazgo.
Un sufijo de unidad se reconoce sólo si es % o una abreviatura alfabética
en minúsculas; por eso 12 kg y 13500 g son unidades, mientras que
12A y 13B se tratan como códigos. Si hay más de una unidad observada,
unidades_mixtas informa sus frecuencias y no convierte ni compara sus
magnitudes. Una única unidad no genera ese hallazgo.
formatos_fecha_mixtos compara representaciones dentro de una columna cuyo
dominio es la fecha o la fecha-hora. Una fecha agregada adentro de una
columna de horas del día —12/02/2011 6:55 a.m. entre valores 7:10 a.m.—
no produce ese hallazgo, porque la columna no es de fechas: aparece como
patron_raro, que es lo que de verdad cambió. Decidir que esa columna sólo
debe contener horas es una regla de dominio y el paquete no la supone.
Del mismo modo, un número sin sufijo no cuenta como una segunda unidad: una
columna que mezcla 5 con 5 % observa una sola unidad y se informa como
numero_como_texto, no como unidades_mixtas.
celdas_multivaluadas es deliberadamente conservador: usa los patrones de
descubrir_patrones() y exige partes numéricas, alfanuméricas o
identificadoras puntuadas homogéneas, compatibles con el patrón del resto de
la columna. No interpreta comas en nombres o direcciones como listas; el
delimitador, la cantidad de celdas y la distribución de valores por celda
quedan en la evidencia del hallazgo.
Examples
perfil <- perfilar(datos_administrativos)
perfil
#>
#> ── Perfil de datos: datos_administrativos ──────────────────────────────────────
#> ✖ 5 hallazgos con severidad error
#> ! 9 hallazgos sospechosos
#> ✔ 8 hallazgos informativos ok
#> ℹ 8 diagnosticos no evaluados
#>
#> ── Resumen general ──
#>
#> Filas: 13
#> Columnas: 10
#> Celdas: 130
#> Filas completas: 13
#> Filas duplicadas: 1
#> Memoria: 7.1 Kb
#>
#> ── Resumen por columna ──
#>
#> columna tipo_inferido prop_faltantes_totales n_distintos n_outliers
#> id_persona doble 0.00000000 11 NA
#> cedula texto 0.07692308 11 NA
#> fecha_nacimiento fecha 0.07692308 12 NA
#> sexo texto 0.15384615 4 NA
#> ingreso doble 0.07692308 12 NA
#> departamento texto 0.00000000 11 NA
#> pais texto 0.00000000 1 NA
#> correo texto 0.00000000 12 NA
#> id_copia doble 0.00000000 11 NA
#> id_tramite identificador 0.00000000 12 NA
#> ℹ Se muestran 5 de 116 campos; los demás están en `columnas()`.
summary(perfil)
#> columna tipo_declarado tipo_inferido estado_tipo_inferido
#> 1 id_persona doble doble <NA>
#> 2 cedula texto texto <NA>
#> 3 fecha_nacimiento texto fecha confirmado
#> 4 sexo texto texto <NA>
#> 5 ingreso doble doble <NA>
#> 6 departamento texto texto <NA>
#> 7 pais texto texto <NA>
#> 8 correo texto texto <NA>
#> 9 id_copia doble doble <NA>
#> 10 id_tramite texto identificador <NA>
#> proporcion_tipo_inferido n_filas_analizadas_tipo muestreado_tipo_inferido n
#> 1 1.0000000 13 FALSE 13
#> 2 1.0000000 13 FALSE 13
#> 3 0.8461538 13 FALSE 13
#> 4 1.0000000 12 FALSE 13
#> 5 1.0000000 13 FALSE 13
#> 6 1.0000000 13 FALSE 13
#> 7 1.0000000 13 FALSE 13
#> 8 1.0000000 13 FALSE 13
#> 9 1.0000000 13 FALSE 13
#> 10 1.0000000 13 FALSE 13
#> n_aplicables n_no_aplica n_aplicabilidad_indeterminada
#> 1 13 0 0
#> 2 13 0 0
#> 3 13 0 0
#> 4 13 0 0
#> 5 13 0 0
#> 6 13 0 0
#> 7 13 0 0
#> 8 13 0 0
#> 9 13 0 0
#> 10 13 0 0
#> n_presentes_en_aplicabilidad_indeterminada
#> 1 0
#> 2 0
#> 3 0
#> 4 0
#> 5 0
#> 6 0
#> 7 0
#> 8 0
#> 9 0
#> 10 0
#> n_presentes_fuera_de_aplicabilidad n_faltantes prop_faltantes
#> 1 0 0 0
#> 2 0 0 0
#> 3 0 0 0
#> 4 0 0 0
#> 5 0 0 0
#> 6 0 0 0
#> 7 0 0 0
#> 8 0 0 0
#> 9 0 0 0
#> 10 0 0 0
#> n_faltantes_disfrazados n_faltantes_disfrazados_textuales
#> 1 0 0
#> 2 1 1
#> 3 1 1
#> 4 2 2
#> 5 1 0
#> 6 0 0
#> 7 0 0
#> 8 0 0
#> 9 0 0
#> 10 0 0
#> n_faltantes_disfrazados_numericos prop_faltantes_disfrazados
#> 1 0 0.00000000
#> 2 0 0.07692308
#> 3 0 0.07692308
#> 4 0 0.15384615
#> 5 1 0.07692308
#> 6 0 0.00000000
#> 7 0 0.00000000
#> 8 0 0.00000000
#> 9 0 0.00000000
#> 10 0 0.00000000
#> n_faltantes_totales prop_faltantes_totales n_distintos tasa_distintos
#> 1 0 0.00000000 11 0.84615385
#> 2 1 0.07692308 11 0.84615385
#> 3 1 0.07692308 12 0.92307692
#> 4 2 0.15384615 4 0.30769231
#> 5 1 0.07692308 12 0.92307692
#> 6 0 0.00000000 11 0.84615385
#> 7 0 0.00000000 1 0.07692308
#> 8 0 0.00000000 12 0.92307692
#> 9 0 0.00000000 11 0.84615385
#> 10 0 0.00000000 12 0.92307692
#> secuencia_entera_densa densidad_secuencia_entera
#> 1 FALSE NA
#> 2 FALSE NA
#> 3 FALSE NA
#> 4 FALSE NA
#> 5 FALSE 1.199988e-06
#> 6 FALSE NA
#> 7 FALSE NA
#> 8 FALSE NA
#> 9 FALSE 1.000000e+00
#> 10 FALSE NA
#> n_posiciones_secuencia_entera n_huecos_secuencia_entera
#> 1 NA NA
#> 2 NA NA
#> 3 NA NA
#> 4 NA NA
#> 5 10000099 10000087
#> 6 NA NA
#> 7 NA NA
#> 8 NA NA
#> 9 11 0
#> 10 NA NA
#> hueco_maximo_secuencia_entera salto_de_escala_secuencia_entera
#> 1 NA FALSE
#> 2 NA FALSE
#> 3 NA FALSE
#> 4 NA FALSE
#> 5 9965499 TRUE
#> 6 NA FALSE
#> 7 NA FALSE
#> 8 NA FALSE
#> 9 1 FALSE
#> 10 NA FALSE
#> moda_sobresale_secuencia_entera umbral_densidad_secuencia_entera
#> 1 FALSE 0.8
#> 2 FALSE 0.8
#> 3 FALSE 0.8
#> 4 FALSE 0.8
#> 5 FALSE 0.8
#> 6 FALSE 0.8
#> 7 FALSE 0.8
#> 8 FALSE 0.8
#> 9 FALSE 0.8
#> 10 FALSE 0.8
#> min_distintos_secuencia_entera moda frecuencia_moda
#> 1 20 [valor protegido] 2
#> 2 20 [valor protegido] 2
#> 3 20 [valor protegido] 2
#> 4 20 F 6
#> 5 20 25000 2
#> 6 20 Florida 2
#> 7 20 UY 13
#> 8 20 [valor protegido] 2
#> 9 20 1 2
#> 10 20 TR001 2
#> longitud_minima longitud_maxima longitud_media n_longitudes_resumidas minimo
#> 1 NA NA NA NA NA
#> 2 3 11 9.692308 13 NA
#> 3 4 10 9.384615 13 NA
#> 4 0 3 1.076923 13 NA
#> 5 NA NA NA NA -99
#> 6 5 10 7.461538 13 NA
#> 7 2 2 2.000000 13 NA
#> 8 10 26 23.923077 13 NA
#> 9 NA NA NA NA 1
#> 10 5 5 5.000000 13 NA
#> maximo media mediana desvio minimo_exacto maximo_exacto
#> 1 NA NA NA 3.546396e+00 <NA> <NA>
#> 2 NA NA NA NA <NA> <NA>
#> 3 NA NA NA 1.191710e+08 <NA> <NA>
#> 4 NA NA NA NA <NA> <NA>
#> 5 9999999 7.900192e+05 29900 2.767287e+06 <NA> <NA>
#> 6 NA NA NA NA <NA> <NA>
#> 7 NA NA NA NA <NA> <NA>
#> 8 NA NA NA NA <NA> <NA>
#> 9 11 5.923077e+00 6 3.546396e+00 <NA> <NA>
#> 10 NA NA NA NA <NA> <NA>
#> minimo_fecha maximo_fecha media_fecha mediana_fecha
#> 1 <NA> <NA> <NA> <NA>
#> 2 <NA> <NA> <NA> <NA>
#> 3 [valor protegido] [valor protegido] [valor protegido] [valor protegido]
#> 4 <NA> <NA> <NA> <NA>
#> 5 <NA> <NA> <NA> <NA>
#> 6 <NA> <NA> <NA> <NA>
#> 7 <NA> <NA> <NA> <NA>
#> 8 <NA> <NA> <NA> <NA>
#> 9 <NA> <NA> <NA> <NA>
#> 10 <NA> <NA> <NA> <NA>
#> n_fechas_resumidas n_fechas_excluidas_granularidad
#> 1 NA NA
#> 2 NA NA
#> 3 11 0
#> 4 NA NA
#> 5 NA NA
#> 6 NA NA
#> 7 NA NA
#> 8 NA NA
#> 9 NA NA
#> 10 NA NA
#> n_valores_excluidos_resumen n_ceros n_negativos n_outliers centinela_valor
#> 1 0 0 0 NA NA
#> 2 0 NA NA NA NA
#> 3 2 0 0 NA NA
#> 4 0 NA NA NA NA
#> 5 0 1 2 NA NA
#> 6 0 NA NA NA NA
#> 7 0 NA NA NA NA
#> 8 0 NA NA NA NA
#> 9 0 0 0 NA NA
#> 10 0 NA NA NA NA
#> centinela_repeticiones densidad_sin_centinela n_nan n_infinito_positivo
#> 1 NA NA 0 0
#> 2 NA NA 0 0
#> 3 NA NA 0 0
#> 4 NA NA 0 0
#> 5 NA NA 0 0
#> 6 NA NA 0 0
#> 7 NA NA 0 0
#> 8 NA NA 0 0
#> 9 NA NA 0 0
#> 10 NA NA 0 0
#> n_infinito_negativo estado_resumen_cuantitativo unidad zona_horaria_origen
#> 1 0 calculados <NA> <NA>
#> 2 0 no_aplica <NA> <NA>
#> 3 0 calculados_sobre_valores <NA> <NA>
#> 4 0 no_aplica <NA> <NA>
#> 5 0 calculados <NA> <NA>
#> 6 0 no_aplica <NA> <NA>
#> 7 0 no_aplica <NA> <NA>
#> 8 0 no_aplica <NA> <NA>
#> 9 0 calculados <NA> <NA>
#> 10 0 no_aplica <NA> <NA>
#> n_filas_fecha_civil_distinta_utc fecha_civil_distinta_utc
#> 1 NA NA
#> 2 NA NA
#> 3 NA NA
#> 4 NA NA
#> 5 NA NA
#> 6 NA NA
#> 7 NA NA
#> 8 NA NA
#> 9 NA NA
#> 10 NA NA
#> representacion_geometria motivo_representacion crs_declarado tipo_geometria
#> 1 <NA> <NA> <NA> <NA>
#> 2 <NA> <NA> <NA> <NA>
#> 3 <NA> <NA> <NA> <NA>
#> 4 <NA> <NA> <NA> <NA>
#> 5 <NA> <NA> <NA> <NA>
#> 6 <NA> <NA> <NA> <NA>
#> 7 <NA> <NA> <NA> <NA>
#> 8 <NA> <NA> <NA> <NA>
#> 9 <NA> <NA> <NA> <NA>
#> 10 <NA> <NA> <NA> <NA>
#> dimension_geometria dimensiones_no_evaluadas n_geometrias_vacias
#> 1 <NA> <NA> NA
#> 2 <NA> <NA> NA
#> 3 <NA> <NA> NA
#> 4 <NA> <NA> NA
#> 5 <NA> <NA> NA
#> 6 <NA> <NA> NA
#> 7 <NA> <NA> NA
#> 8 <NA> <NA> NA
#> 9 <NA> <NA> NA
#> 10 <NA> <NA> NA
#> n_geometrias_invalidas n_validez_evaluados validez_criterio
#> 1 NA NA <NA>
#> 2 NA NA <NA>
#> 3 NA NA <NA>
#> 4 NA NA <NA>
#> 5 NA NA <NA>
#> 6 NA NA <NA>
#> 7 NA NA <NA>
#> 8 NA NA <NA>
#> 9 NA NA <NA>
#> 10 NA NA <NA>
#> validez_preprocesamiento n_fuera_de_dominio n_dominio_evaluados
#> 1 <NA> NA NA
#> 2 <NA> NA NA
#> 3 <NA> NA NA
#> 4 <NA> NA NA
#> 5 <NA> NA NA
#> 6 <NA> NA NA
#> 7 <NA> NA NA
#> 8 <NA> NA NA
#> 9 <NA> NA NA
#> 10 <NA> NA NA
#> n_bbox_evaluados bbox_alcance bbox_xmin bbox_xmax bbox_ymin bbox_ymax
#> 1 NA <NA> NA NA NA NA
#> 2 NA <NA> NA NA NA NA
#> 3 NA <NA> NA NA NA NA
#> 4 NA <NA> NA NA NA NA
#> 5 NA <NA> NA NA NA NA
#> 6 NA <NA> NA NA NA NA
#> 7 NA <NA> NA NA NA NA
#> 8 NA <NA> NA NA NA NA
#> 9 NA <NA> NA NA NA NA
#> 10 NA <NA> NA NA NA NA
#> detalle_proteccion_personal n_blancos n_espacios_borde
#> 1 [estadisticos de orden y la media protegidos] 0 0
#> 2 [estadisticos de orden protegidos] 0 0
#> 3 [estadisticos de orden y la media protegidos] 0 0
#> 4 <NA> 1 0
#> 5 <NA> 0 0
#> 6 <NA> 0 0
#> 7 <NA> 0 0
#> 8 [estadisticos de orden protegidos] 0 0
#> 9 <NA> 0 0
#> 10 <NA> 0 0
#> n_variantes_mayusculas n_variantes_unicode unicode_evaluado
#> 1 0 NA NA
#> 2 0 0 TRUE
#> 3 0 0 TRUE
#> 4 0 0 TRUE
#> 5 0 NA NA
#> 6 0 0 TRUE
#> 7 0 0 TRUE
#> 8 0 0 TRUE
#> 9 0 NA NA
#> 10 0 0 TRUE
#> n_codificacion_rota n_codificacion_reparable
#> 1 0 0
#> 2 0 0
#> 3 0 0
#> 4 0 0
#> 5 0 0
#> 6 0 0
#> 7 0 0
#> 8 0 0
#> 9 0 0
#> 10 0 0
#> n_codificacion_reparable_parcialmente n_codificacion_irreparable
#> 1 0 0
#> 2 0 0
#> 3 0 0
#> 4 0 0
#> 5 0 0
#> 6 0 0
#> 7 0 0
#> 8 0 0
#> 9 0 0
#> 10 0 0
#> n_codificacion_no_se_pudo estado_codificacion_reparacion
#> 1 0 no_parece_roto
#> 2 0 no_parece_roto
#> 3 0 no_parece_roto
#> 4 0 no_parece_roto
#> 5 0 no_parece_roto
#> 6 0 no_parece_roto
#> 7 0 no_parece_roto
#> 8 0 no_parece_roto
#> 9 0 no_parece_roto
#> 10 0 no_parece_roto
#> n_codificacion_invalida n_controles_invisibles n_invisibles_eliminables
#> 1 0 0 0
#> 2 0 0 0
#> 3 0 0 0
#> 4 0 0 0
#> 5 0 0 0
#> 6 0 0 0
#> 7 0 0 0
#> 8 0 0 0
#> 9 0 0 0
#> 10 0 0 0
#> n_espacios_invisibles n_invisibles_significativos n_entidades_html
#> 1 0 0 0
#> 2 0 0 0
#> 3 0 0 0
#> 4 0 0 0
#> 5 0 0 0
#> 6 0 0 0
#> 7 0 0 0
#> 8 0 0 0
#> 9 0 0 0
#> 10 0 0 0
#> n_separadores_en_campo n_numeros_texto proporcion_numeros_texto
#> 1 0 0 NA
#> 2 0 0 NA
#> 3 0 0 NA
#> 4 0 0 NA
#> 5 0 0 NA
#> 6 0 0 NA
#> 7 0 0 NA
#> 8 0 0 NA
#> 9 0 0 NA
#> 10 0 0 NA
#> numero_texto_ambiguo numero_texto_seguro numero_texto_unidad
#> 1 FALSE FALSE
#> 2 FALSE FALSE
#> 3 FALSE FALSE
#> 4 FALSE FALSE
#> 5 FALSE FALSE
#> 6 FALSE FALSE
#> 7 FALSE FALSE
#> 8 FALSE FALSE
#> 9 FALSE FALSE
#> 10 FALSE FALSE
#> numero_texto_moneda numero_texto_convencion dato_personal_posible
#> 1 TRUE
#> 2 TRUE
#> 3 TRUE
#> 4 FALSE
#> 5 FALSE
#> 6 FALSE
#> 7 FALSE
#> 8 TRUE
#> 9 FALSE
#> 10 FALSE
#> tipo_dato_personal proporcion_dato_personal
#> 1 nombre NA
#> 2 documento_identidad 0.8461538
#> 3 fecha_nacimiento 1.0000000
#> 4 <NA> NA
#> 5 <NA> NA
#> 6 <NA> NA
#> 7 <NA> NA
#> 8 correo 0.9230769
#> 9 <NA> NA
#> 10 <NA> NA
#> poder_discriminante_dato_personal dato_personal_protegido
#> 1 medio TRUE
#> 2 alto TRUE
#> 3 medio TRUE
#> 4 <NA> FALSE
#> 5 <NA> FALSE
#> 6 <NA> FALSE
#> 7 <NA> FALSE
#> 8 alto TRUE
#> 9 <NA> FALSE
#> 10 <NA> FALSE
