¿Qué hace que los datos sean buenos o malos?
Parece una pregunta sencilla, pero no lo es. Pregúntele a un implementador FHIR qué significa la calidad de los datos y le hablará del validador: perfiles, cardinalidades, bindings de terminología, invariantes. Hágale la misma pregunta a un ingeniero de datos y le hablará de pruebas dbt: not-null, unique, valores aceptados, freshness.
Ambas respuestas son la mitad de la verdad, y las dos mitades no se solapan de la forma que cada parte asume. La confusión tiene un coste: los equipos o bien reconstruyen el validador en SQL, o bien interpretan una ejecución de validación en verde como «los datos están bien» — y ambos errores afloran tarde, normalmente cuando una medida de calidad devuelve un número que nadie puede defender.
La buena noticia es que el mundo de los datos sanitarios ya elaboró una respuesta precisa a «qué son buenos datos» hace una década: el marco de Kahn. Este artículo lo recorre y lo mapea sobre FHIR: qué cubre ya su validador y qué no puede cubrir estructuralmente.
Fui a buscarlo mientras portaba los controles de calidad de datos de OMOP sobre SQL on FHIR. La mecánica era sencilla; lo difícil era determinar qué controles corresponden al validador y cuáles no — y este marco es lo que traza esa línea.
Kahn, en tres preguntas
Kahn et al., A Harmonized Data Quality Assessment Terminology and Framework (2016). No es un conjunto de métricas ni una herramienta — es una terminología, escrita para fusionar una docena de vocabularios incompatibles que distintos grupos habían inventado por separado. Por eso perduró. Discutir sobre nombres es más barato que discutir sobre mediciones.
Formula tres preguntas sobre los datos:
| Pregunta | Kahn la denomina | Ejemplo |
|---|---|---|
| ¿Está registrado correctamente? | Conformance | Sex solo tiene los valores M, F o U |
| ¿Está el valor presente? | Completeness | El 40 % de las observaciones no llevan valor |
| ¿Puede creerse el valor? | Plausibility | Un peso corporal de 1000 kg |
Dos detalles son fundamentales. Completeness se define «sin referencia a los valores de los datos» — cuenta con qué frecuencia algo está presente, nunca qué dice. Y plausibility trata sobre la credibilidad, no sobre la verdad: si 78 kg es plausible, no si el paciente pesa realmente 78 kg. Nada en los datos puede responder a la segunda pregunta, y el marco no pretende lo contrario.
La trampa: «validación» significa dos cosas distintas
Antes de continuar — esta palabra puede confundir a un público FHIR, así que conviene aclararlo.
En FHIR, validación significa comprobar un recurso contra una StructureDefinition. En Kahn, validación significa comparar datos contra un referente externo, a diferencia de la verificación frente a las propias expectativas. Conceptos sin relación, misma palabra, y se solapan lo justo para despistar:
- Validar un recurso contra US Core es validación en sentido Kahn — la regla proviene del exterior.
- Validar el mismo recurso contra un perfil propio es verificación en sentido Kahn — la regla es propia.
El validador realiza un trabajo idéntico en ambos casos. Solo cambia la procedencia del criterio. Una prueba práctica: ¿puede ejecutar este control únicamente con su propia base de datos? Un peso de 1000 kg — sí. La prevalencia de diabetes coincidente con la tasa nacional — no.
Una advertencia, porque la versión incorrecta circula ampliamente: verificación = conformance, validación = completeness + plausibility no es lo que dice el marco. Los dos ejes se cruzan genuinamente. El Data Quality Dashboard de OHDSI, la implementación de referencia del marco, rellena las seis celdas.
La línea real es el alcance, no la categoría
Aquí está lo que la mayoría de los artículos confunden. La división no es «el validador hace conformance, los controles de calidad hacen el resto». La validación FHIR llega considerablemente más lejos que eso.
Hace plausibility, siempre que la pregunta quepa dentro de un único recurso. minValue[x] / maxValue[x] es literalmente una restricción de rango plausible:
{
"path": "Observation.value[x]",
"minValueQuantity": { "value": 0.5, "unit": "kg" },
"maxValueQuantity": { "value": 650, "unit": "kg" }
}
Hace plausibilidad temporal. Todo Period en FHIR ya lleva per-1:
per-1: "If present, start SHALL have a lower or equal value than end"
start.hasValue().not() or end.hasValue().not() or (start <= end)
Y hace lógica entre campos dentro de un recurso, mediante invariantes:
obs-7: "If Observation.code is the same as a Observation.component.code
then the value element associated with the code SHALL NOT be present"
Por tanto, el validador no está limitado a la estructura. Lo que no puede hacer es cualquier cosa que necesite una población o un segundo recurso:
| La pregunta necesita… | Ejemplo | Validador |
|---|---|---|
| un recurso | peso entre 0,5 y 650 kg; end no anterior a start | ✅ |
| una proporción | el 40 % de las Observations no tienen valor | ❌ sin denominador |
| una tolerancia | el 5 % de ausencias es aceptable aquí, el 0 % allá | ❌ la validez es binaria |
| otro recurso | Observation con fecha anterior al nacimiento del paciente | ❌ referencias no resueltas |
| todos los registros | un MRN por paciente | ❌ sin conjunto de datos en vista |
| una distribución | media de peso, prevalencia, deriva | ❌ necesita una población |
| un referente externo | prevalencia coincide con la tasa nacional | ❌ nada con qué comparar |
Lo que da lugar a la formulación honesta en una sola línea:
Un validador responde toda pregunta que cabe dentro de un único recurso, y ninguna que no cabe.
El único caso que cae exactamente en la línea
Un elemento tiene min=1 y está ausente. El validador lo reporta, y eso parece un control de completeness. Según Kahn es conformance — se violó una regla estructural, no una expectativa de frecuencia.
La completeness real trata de elementos opcionales rellenos con tan poca frecuencia que la columna resulta inútil. El mismo SQL en ambos casos; categoría diferente, mecanismo diferente, determinado enteramente por si el elemento era obligatorio.
Lo que solo un conjunto de datos puede responder
Todo lo que es una propiedad del conjunto de datos y no de ningún registro en él. En SQL on FHIR son consultas ordinarias sobre una vista aplanada — cuyo objetivo es precisamente devolver las filas problemáticas:
-- unicidad entre registros: un MRN por paciente
SELECT mrn FROM patient_flat
GROUP BY mrn HAVING count(DISTINCT id) > 1
-- integridad referencial: subject apunta a un Patient que no existe aquí
-- (una exportación Bulk FHIR falla esto más a menudo de lo que se cree)
SELECT o.id, o.patient_id
FROM obs o LEFT JOIN pat ON o.patient_id = pat.id
WHERE o.patient_id IS NOT NULL AND pat.id IS NULL
-- temporal entre recursos: observado antes del nacimiento del paciente
SELECT o.id FROM obs o JOIN pat ON o.patient_id = pat.id
WHERE o.effective < pat.birth_date
Más los que no tienen ningún análogo a nivel de registro individual: proporciones (40 % de valores ausentes), tolerancias (5 % aceptable aquí, 0 % allá), distribuciones (media, cuantiles, valores atípicos, deriva), puntualidad (el registro más reciente tiene seis semanas de antigüedad — no hay nada incorrecto en ningún registro, el conjunto de datos está desactualizado), y el resumen que se convierte en el dashboard.
Nada de esto es una brecha que un validador mejor pudiera cerrar. Es una pregunta diferente.
Y merece decirse lo contrario, porque una división clara necesita ambas mitades: los controles SQL solo ven lo que una proyección expuso. El validador inspecciona el recurso completo — elementos no proyectados, slicing, extensiones, invariantes sobre estructuras anidadas, expansión de ValueSet con jerarquía. Los controles ni hacen eso ni deberían hacerlo. Un control que reimplementa una restricción de perfil es una segunda fuente de verdad, y divergirá de la primera.
Donde el marco de Kahn se queda corto
Dos lagunas con las que se tropieza de inmediato si se construye sobre él.
Sin timeliness. «¿Siguen llegando datos?» no es ni conformance, ni completeness, ni plausibility. Es un fallo real y frecuente — la detección de anomalías de Databricks se reduce a freshness y completeness — pero el marco es anterior a ese enfoque. O bien se añade una cuarta categoría, o bien se archiva la freshness bajo plausibilidad temporal y se acepta la incomodidad.
Sin accuracy. Plausibility es credibilidad, no verdad. Si un peso registrado es el peso real del paciente es algo que los datos no pueden responder.
Dos cosas que OHDSI tuvo que añadir en la práctica merecen adoptarse junto con la taxonomía: un nivel (dataset / columna / código) y una severidad (fatal / convention / characterization). Su división de severidad entre 27 tipos de control es instructiva — 12 characterization, 8 convention, 7 fatal. La mayoría de los controles describen en lugar de juzgar, lo cual es una expectativa útil que establecer antes de que alguien construya un dashboard que coloree todo de rojo.
Dónde nos deja esto
Un validador responde «¿está bien formado este recurso?» Un control responde «¿es este conjunto de datos apto para su uso?»
La segunda pregunta carece de sentido sin la primera, y la primera es insuficiente sin la segunda. FHIR tiene una excelente respuesta a la primera y, hasta ahora, ningún mecanismo estándar para la segunda.
La parte alentadora es lo poco que queda por inventar. El vocabulario está establecido. La implementación de referencia lleva una década funcionando en el mundo OMOP. Y en FHIR las piezas ya existen: una ViewDefinition es el conjunto de datos, una SQLQuery es el control, y relatedArtifact ya declara el grafo de dependencias. Lo que falta son solo las semánticas — decir esta consulta es un control, esto es lo que mide, y este nivel de fallo es aceptable. De eso trata el trabajo de calidad de datos en SQL on FHIR, y está abierto.
Fuentes
- Kahn MG et al., A Harmonized Data Quality Assessment Terminology and Framework for the Secondary Use of Electronic Health Record Data, eGEMs 4(1):1244, 2016 — PMC5051581. Todas las citas provienen de este artículo.
- OHDSI Data Quality Dashboard — la matriz de clasificación de 27 tipos se encuentra en
inst/csv/OMOP_CDMv5.4_Check_Descriptions.csv.





