Investigación y fuentes

Crear un diccionario de datos con significados, unidades, tipos y valores permitidos

Documenta qué significa cada columna del conjunto de datos antes del análisis, incluida su unidad, tipo de dato y valores permitidos. Un pequeño conjunto de datos sintético muestra cómo el mismo diccionario también puede servir para una validación automática sencilla.

Ver el índice

La traducción se ha realizado con IA. Comprueba el código, las unidades y los valores junto con el original. La revisión por hablantes nativos de cada idioma aún no se ha completado. English

Para quién esEsta guía está dirigida a investigadores que necesitan una descripción clara, columna por columna, de un conjunto de datos que otras personas puedan leer y revisar.

Preparación
  • Python 3.12 y un comando de terminal que inicie esa versión.
  • Un editor de texto o programa de hojas de cálculo que pueda guardar archivos CSV en UTF-8.
  • Una carpeta de trabajo donde el script pueda crear una nueva carpeta dentro de outputs.
  • Solo se requiere la biblioteca estándar de Python: csv, pathlib y re.

01Tratar el diccionario de datos como parte del conjunto de datos

El nombre de una columna rara vez basta para describir datos de investigación. speed podría significar velocidad del vehículo, velocidad de la rueda o velocidad angular. accel podría significar aceleración longitudinal, lateral o total. Un diccionario de datos registra el significado previsto para que los análisis posteriores no dependan de suposiciones.

Este tutorial registra seis elementos de información para cada columna: nombre de la columna, significado, unidad, tipo de dato, valores permitidos y un ejemplo. El campo de valores permitidos puede describir categorías, rangos numéricos, reglas de unicidad o patrones de texto simples.

02Crear un pequeño conjunto de datos sintético de investigación

El siguiente conjunto de datos es sintético y fue creado específicamente para este artículo. Guárdalo como test_runs.csv. Cuatro filas siguen las reglas previstas. La quinta fila contiene deliberadamente cuatro problemas para que el diccionario pueda utilizarse como referencia de validación.

csv
run_id,speed_kmh,accel_m_s2,road_surface,test_status
R001,30,0.5,dry,complete
R002,50,-1.2,wet,complete
R003,70,0.0,dry,review
R004,40,-0.8,wet,complete
R005,135,fast,snow,done

Hay 5 filas y 5 columnas. R005 tiene un speed por encima del rango permitido, un valor de acceleration no numérico, una categoría de road-surface no admitida y un valor de test-status no admitido. Su run_id sí es válido.

03Escribir manualmente el diccionario de datos esperado

El diccionario debe poder entenderse sin abrir el código de análisis. Mantén el significado lo bastante específico como para que otro investigador pueda distinguir la columna de otras mediciones similares.

column_namesignificadounidadtipovalores permitidosejemplo
run_idIdentificador único de una ejecución de pruebacadenaR seguida de exactamente 3 dígitos; únicoR001
speed_kmhVelocidad del vehículo al inicio del registrokm/henterode 0 a 120 inclusive50
accel_m_s2Aceleración longitudinalm/s^2decimalde -5.0 a 5.0 inclusive-1.2
road_surfaceCondición de la superficie de la carretera utilizada en la pruebacategoríadry;wetwet
test_statusEstado de revisión del registro de pruebacategoríacomplete;reviewcomplete

Una unidad vacía es intencional para identificadores y etiquetas categóricas porque son metadatos adimensionales, no magnitudes físicas medidas. El campo type describe la representación de datos prevista, no lo que una hoja de cálculo pueda inferir automáticamente.

04Generar el diccionario y validar el conjunto de datos

Guarda el siguiente script como data_dictionary_check.py. La especificación de columnas se escribe una sola vez en SPEC. El script exporta esa especificación como data_dictionary.csv y comprueba cada fila con las mismas reglas. El archivo original test_runs.csv solo se lee.

python
import csv
import re
from pathlib import Path

SOURCE = Path("test_runs.csv")
OUTPUT_DIR = Path("outputs") / "data_dictionary_result"
DICTIONARY = OUTPUT_DIR / "data_dictionary.csv"
ISSUES = OUTPUT_DIR / "validation_issues.csv"

SPEC = [
    {
        "column_name": "run_id",
        "meaning": "Unique identifier for one test run",
        "unit": "",
        "type": "string",
        "allowed_values": "R followed by exactly 3 digits; unique",
        "example": "R001",
    },
    {
        "column_name": "speed_kmh",
        "meaning": "Vehicle speed at the start of the record",
        "unit": "km/h",
        "type": "integer",
        "allowed_values": "0 to 120 inclusive",
        "example": "50",
    },
    {
        "column_name": "accel_m_s2",
        "meaning": "Longitudinal acceleration",
        "unit": "m/s^2",
        "type": "float",
        "allowed_values": "-5.0 to 5.0 inclusive",
        "example": "-1.2",
    },
    {
        "column_name": "road_surface",
        "meaning": "Road-surface condition used for the test",
        "unit": "",
        "type": "category",
        "allowed_values": "dry;wet",
        "example": "wet",
    },
    {
        "column_name": "test_status",
        "meaning": "Review state of the test record",
        "unit": "",
        "type": "category",
        "allowed_values": "complete;review",
        "example": "complete",
    },
]


def issue(row_number, column, value, problem):
    return {
        "row_number": row_number,
        "column_name": column,
        "value": value,
        "issue": problem,
    }


def main() -> None:
    if not SOURCE.is_file():
        raise FileNotFoundError(f"Source CSV not found: {SOURCE}")
    if OUTPUT_DIR.exists():
        raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")

    expected_columns = [item["column_name"] for item in SPEC]
    issues = []
    seen_run_ids = set()
    row_count = 0

    with SOURCE.open("r", encoding="utf-8-sig", newline="") as stream:
        reader = csv.DictReader(stream)
        if reader.fieldnames != expected_columns:
            raise ValueError(
                f"Header mismatch. Expected {expected_columns}, got {reader.fieldnames}."
            )

        for row_number, row in enumerate(reader, start=2):
            row_count += 1

            run_id = row["run_id"].strip()
            if re.fullmatch(r"R[0-9]{3}", run_id) is None:
                issues.append(issue(row_number, "run_id", run_id, "PATTERN_ERROR"))
            elif run_id in seen_run_ids:
                issues.append(issue(row_number, "run_id", run_id, "DUPLICATE_VALUE"))
            seen_run_ids.add(run_id)

            try:
                speed = int(row["speed_kmh"])
            except ValueError:
                issues.append(issue(row_number, "speed_kmh", row["speed_kmh"], "TYPE_ERROR"))
            else:
                if not 0 <= speed <= 120:
                    issues.append(issue(row_number, "speed_kmh", row["speed_kmh"], "OUT_OF_RANGE"))

            try:
                accel = float(row["accel_m_s2"])
            except ValueError:
                issues.append(issue(row_number, "accel_m_s2", row["accel_m_s2"], "TYPE_ERROR"))
            else:
                if not -5.0 <= accel <= 5.0:
                    issues.append(issue(row_number, "accel_m_s2", row["accel_m_s2"], "OUT_OF_RANGE"))

            if row["road_surface"] not in {"dry", "wet"}:
                issues.append(issue(row_number, "road_surface", row["road_surface"], "ALLOWED_VALUE_ERROR"))

            if row["test_status"] not in {"complete", "review"}:
                issues.append(issue(row_number, "test_status", row["test_status"], "ALLOWED_VALUE_ERROR"))

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()

    dictionary_fields = [
        "column_name", "meaning", "unit", "type", "allowed_values", "example"
    ]
    with DICTIONARY.open("x", encoding="utf-8", newline="") as stream:
        writer = csv.DictWriter(stream, fieldnames=dictionary_fields)
        writer.writeheader()
        writer.writerows(SPEC)

    issue_fields = ["row_number", "column_name", "value", "issue"]
    with ISSUES.open("x", encoding="utf-8", newline="") as stream:
        writer = csv.DictWriter(stream, fieldnames=issue_fields)
        writer.writeheader()
        writer.writerows(issues)

    print(f"Rows checked: {row_count}.")
    print(f"Dictionary columns: {len(SPEC)}.")
    print(f"Validation issues: {len(issues)}.")
    print(f"Output folder: {OUTPUT_DIR.as_posix()}")


if __name__ == "__main__":
    main()

05Comparar los problemas de validación esperados

Solo R005 debería producir problemas. Como el encabezado del CSV ocupa la fila 1, R005 corresponde físicamente a la fila 6 del CSV. Deberían escribirse cuatro filas de problemas.

csv
row_number,column_name,value,issue
6,speed_kmh,135,OUT_OF_RANGE
6,accel_m_s2,fast,TYPE_ERROR
6,road_surface,snow,ALLOWED_VALUE_ERROR
6,test_status,done,ALLOWED_VALUE_ERROR

La salida de consola esperada que aparece a continuación se obtuvo manualmente a partir del conjunto de datos sintético y del script. No es un registro capturado de una ejecución.

text
Rows checked: 5.
Dictionary columns: 5.
Validation issues: 4.
Output folder: outputs/data_dictionary_result

06Comprobar que el diccionario y los datos sigan alineados

  • Confirma que cada columna del conjunto de datos tenga exactamente una fila en el diccionario.
  • Comprueba que las unidades físicas estén indicadas explícitamente para las magnitudes medidas.
  • Confirma que los valores categóricos permitidos usen la misma ortografía y capitalización que el conjunto de datos.
  • Revisa cada problema de validación en lugar de eliminar automáticamente las filas incorrectas.
  • Ejecuta el script de nuevo sin cambiar OUTPUT_DIR. Debería detenerse con FileExistsError en lugar de sustituir el diccionario y el informe anteriores.
ProblemaQué comprobar
Una nueva columna del conjunto de datos no tiene entrada en el diccionarioActualiza el diccionario antes del análisis para documentar el significado y la unidad.
El mismo concepto aparece con unidades diferentesUsa columnas separadas o estandariza las unidades explícitamente; no dependas de la memoria.
Las etiquetas de categoría cambian con el tiempoDefine los valores permitidos y documenta las adiciones intencionadas, como una nueva condición de carretera.
La hoja de cálculo convierte IDs enteros en númerosGuarda los identificadores como cadenas cuando los ceros iniciales o los patrones sean significativos.
La validación pasa, pero el significado es incorrectoLas comprobaciones estructurales no pueden detectar una definición científica incorrecta ni un canal de sensor mal etiquetado.

07Ampliar el diccionario a medida que crece el proyecto

Un diccionario práctico de datos de investigación suele necesitar más que los seis campos mostrados aquí. Según el proyecto, añade source system, sensor location, coordinate direction, sampling rate, nullable status, precision, calculation method, valid range, missing-value code y responsible owner.

Los valores permitidos deben reflejar el significado científico y no simplemente el mínimo y el máximo observados actualmente. Si en un archivo los valores de speed están entre 30 y 70 km/h, eso no significa automáticamente que de 30 a 70 sea el rango de ingeniería válido.

Versiona el diccionario junto con el conjunto de datos o el código de análisis. Si cambia la definición de una columna, una unidad, la convención de signo o una categoría, registra cuándo se produjo el cambio. Un diccionario de datos resulta más útil cuando describe exactamente la versión de los datos que se está analizando.

Registro de ejecución y verificación

ChatGPT Python tool · Python 3.13.5 · standard library: csv, pathlib, re

  • La ejecución terminó correctamente para 5 filas y 5 columnas del diccionario.
  • El validation_issues.csv generado contenía exactamente 4 filas, todas correspondientes a la fila física 6 del CSV (R005): speed_kmh 135 OUT_OF_RANGE, accel_m_s2 fast TYPE_ERROR, road_surface snow ALLOWED_VALUE_ERROR y test_status done ALLOWED_VALUE_ERROR.
  • El validation_issues.csv generado coincidió exactamente con el CSV esperado del artículo.
  • El data_dictionary.csv generado contenía exactamente las 5 filas SPEC y los valores de metadatos esperados.
  • La salida de consola coincidió exactamente: Rows checked: 5; Dictionary columns: 5; Validation issues: 4; Output folder: outputs/data_dictionary_result.
  • La segunda ejecución produjo FileExistsError para outputs/data_dictionary_result, confirmando la protección contra sobrescritura.
  • La comparación SHA-256 confirmó que test_runs.csv permaneció sin cambios.
Límites de la verificación
  • El entorno de ejecución fue Python 3.13.5 en lugar del Python 3.12 objetivo del artículo.
  • El entorno de ejecución emitió en stderr una advertencia no relacionada del calentamiento del entorno de hojas de cálculo durante el inicio de Python; no afectó a la primera ejecución correcta del script, a los archivos generados ni a la prueba de protección contra sobrescritura.
  • El script verifica únicamente las reglas declaradas de estructura, tipo, rango y categoría; no valida la corrección científica de las definiciones de metadatos en sí.

Fuentes de referencia

Las explicaciones y los ejemplos son de elaboración propia. Puedes consultar los comportamientos y conceptos relacionados en las siguientes fuentes oficiales.