--- name: profile_table kind: pipeline lang: py domain: pipelines purity: impure version: "1.0.0" signature: "def profile_table(db_path: str, table: str, backend: str = \"duckdb\", sample: int = 5000, run_models: bool = False, run_llm: bool = False, run_series: bool = False, emit_pdf: bool = False, report_dir: str = \"reports\", write_report: bool = True) -> dict" description: "Orquestador one-shot del grupo de capacidad eda: perfila UNA tabla (DuckDB o PostgreSQL) end-to-end componiendo las funciones del grupo (perfil base SQL + muestreo read-only + inferencia semantica + promocion de tipo + estadistica numerica/categorica + score de calidad + correlaciones con correccion FDR + re-expresion de Tukey + avisos exploratorios) y, opcional, modelos baratos (run_models), interpretacion LLM (run_llm) y analisis de serie temporal por columna (run_series: estacionariedad ADF+KPSS, ACF/PACF, STL, retornos). Emite el TableProfile completo mas (opcional) report markdown + JSON sidecar + PDF movil (emit_pdf). Es la composicion canonica para hazme un EDA de esta tabla." tags: [eda, duckdb, postgres, profiling, data-quality, pipeline, dataops, timeseries] uses_functions: - summarize_table_duckdb_py_datascience - summarize_table_pg_py_datascience - describe_numeric_py_datascience - summarize_categorical_py_datascience - infer_semantic_type_py_datascience - column_quality_score_py_datascience - association_matrix_py_datascience - run_eda_models_py_datascience - eda_llm_insights_py_datascience - adf_kpss_stationarity_py_datascience - acf_pacf_py_datascience - stl_decompose_py_datascience - to_returns_py_datascience - suggest_reexpression_py_datascience - exploratory_caveats_py_datascience - render_eda_markdown_py_datascience - render_eda_pdf_py_datascience - duckdb_query_readonly_py_infra - pg_query_py_infra uses_types: [] returns: [] returns_optional: false error_type: error_go_core imports: [] tested: true tests: - "VARCHAR-entera se promociona a numeric con bloque numeric y key_candidates es lista" test_file_path: "python/functions/pipelines/profile_table_test.py" file_path: "python/functions/pipelines/profile_table.py" params: - name: db_path desc: "Ruta al archivo DuckDB (read-only, debe existir; no se crea) o DSN PostgreSQL si backend='postgres'." - name: table desc: "Nombre de la tabla a perfilar." - name: backend desc: "'duckdb' (default) o 'postgres'. Selecciona el motor de perfilado base (summarize) y de muestreo read-only." - name: sample desc: "Maximo de valores no nulos muestreados por columna para el enriquecimiento (describe_numeric / summarize_categorical / infer_semantic_type). Default 5000." - name: run_models desc: "Si True (default False) corre los modelos baratos (PCA/KMeans/IsolationForest/normalidad) y guarda el bloque en prof['models']." - name: run_llm desc: "Si True (default False) hace 1 llamada LLM sobre el perfil agregado y guarda el resultado en prof['llm']." - name: run_series desc: "Si True (default False) calcula por columna numerica un bloque de serie temporal (estacionariedad ADF+KPSS, ACF/PACF, STL y, si parece de niveles, retornos). Ordena por la primera columna datetime si existe; si no, por el orden fisico. Guardado en col['series'] y agregado en prof['series']." - name: emit_pdf desc: "Si True (default False) renderiza un PDF multipagina vertical (legible en movil) del perfil junto al report markdown y devuelve su ruta en pdf_path." - name: report_dir desc: "Directorio donde escribir los reports si write_report (y el PDF si emit_pdf). Default 'reports'. Se crea si no existe." - name: write_report desc: "Si True (default) escribe report markdown + JSON sidecar timestamped en report_dir; si False no toca disco y los paths markdown/json del retorno son None (emit_pdf es independiente)." output: "dict {status:'ok', profile:, report_md_path:str|None, report_json_path:str|None, pdf_path:str|None} o {status:'error', error:str} (dict-no-throw)." --- ## Ejemplo ```python import os from pipelines.profile_table import profile_table # Tabla real: freelance_projects (35 filas) en la DuckDB del monitor de captacion. db = os.path.expanduser("~/.fn_freelance/freelance.duckdb") r = profile_table(db, "freelance_projects", sample=5000, write_report=False) print(r["status"], r["profile"]["quality_score"], r["profile"]["type_breakdown"]) # ok 98.9 {'numeric': 1, 'categorical': 9, 'datetime': 2, 'text': 0, 'boolean': 1} # ^ 'bids' (VARCHAR '1'..'107') se promociono a numeric via semantic_type=integer. # Con report a disco (markdown + JSON sidecar en reports/): r = profile_table(db, "freelance_projects") print(r["report_md_path"], r["report_json_path"]) # reports/eda_freelance_projects_20260620-101500.md reports/eda_freelance_projects_20260620-101500.json ``` ## Cuando usarla Cuando necesites un EDA completo de una tabla DuckDB en una sola llamada: perfil por columna + estadistica fina + calidad + report listo para leer. Usala como primer paso al recibir un dataset desconocido, antes de modelar o limpiar, o para auditar la calidad de una tabla ya productiva. Reemplaza orquestar a mano `summarize_table_duckdb` -> muestreo -> `describe_numeric`/`summarize_categorical` -> `column_quality_score` -> `render_eda_markdown` columna por columna. ## Gotchas - Impura: con `write_report=True` (default) ESCRIBE dos archivos a `report_dir` (markdown + JSON). Pasa `write_report=False` para un dry-run sin tocar disco. - La promocion de tipo es una HEURISTICA sobre la muestra: una columna VARCHAR se reclasifica a `numeric` solo si su `semantic_type` es integer/decimal/currency y al menos el 80% de la muestra parsea a float; a `datetime` si el `semantic_type` es datetime_iso/date_eu. Tablas con datos sucios o muestras no representativas pueden quedar mal clasificadas; sube `sample` para muestras mas fiables (coste: mas filas traidas a RAM por columna). - Las columnas promovidas a `datetime` aun NO reciben perfil fino: `col["datetime"]` queda en `None` (la funcion `profile_datetime` del grupo llega en otra fase). Su `semantic_type` si se conserva. - El parseo numerico limpia simbolos de moneda (€/$/£/EUR/USD/GBP), espacios y separadores de miles; con coma y punto juntos asume punto=miles, coma=decimal. Formatos exoticos pueden descartarse silenciosamente del calculo numerico. - `db_path` debe existir: DuckDB read-only NO crea la base. El muestreo usa el sandbox por defecto de `duckdb_query_readonly` (sin acceso a FS/red).