--- name: generate_synthetic_eda_table kind: function lang: py domain: datascience version: "1.0.0" purity: impure signature: "def generate_synthetic_eda_table(out_db_path: str, table: str = 'synthetic', n_rows: int = 2000, seed: int = 42) -> dict" description: "Genera una tabla DuckDB sintetica (Faker + numpy, determinista por seed) cuyo contenido esta disenado para ACTIVAR el maximo de capitulos del motor AutomaticEDA del grupo eda: numericas continuas con correlacion lineal/no-lineal, numericas con outliers, categoricas desbalanceadas, texto libre multi-idioma con duplicados, fecha para serie temporal, lat/lon validas, semanticos/PII (uuid/email/iban/phone) y nulos con patron MCAR/MAR. Fixture para evaluar el EDA de punta a punta. Estilo dict-no-throw: nunca lanza." tags: [eda, synthetic, faker, testing, fixture, datascience] params: - name: out_db_path desc: "Ruta al archivo DuckDB de salida. Se crea (o reutiliza) y la tabla se reemplaza con CREATE OR REPLACE TABLE si ya existe." - name: table desc: "Nombre de la tabla a crear. Se valida contra ^[A-Za-z_][A-Za-z0-9_]*$ y se cita en el DDL. Default 'synthetic'." - name: n_rows desc: "Numero de filas (clientes unicos). Cada fila es un cliente con id/email/iban/phone propios. Default 2000." - name: seed desc: "Semilla para Faker (Faker.seed) y numpy (np.random.default_rng). Mismo seed -> tabla identica byte a byte. Default 42." output: "dict dict-no-throw. En exito {status:'ok', db_path, table, n_rows, columns:[19 nombres de columna], seed}. En error (sin lanzar, p.ej. nombre de tabla invalido o n_rows<=0) {status:'error', error:str}. Columnas: customer_id,email,iban,phone,income,spending,age,risk_score,tenure_months,engagement_quad,amount,n_purchases,country,category,plan,review,signup_date,latitude,longitude." uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [] tested: true tests: ["test_genera_ok_y_columnas", "test_determinismo_mismo_seed", "test_seeds_distintos_difieren", "test_latlon_en_rango", "test_plan_solo_niveles_validos", "test_income_spending_co_nulos", "test_review_mediana_palabras_y_signup_datetime", "test_phone_matchea_regex_internacional", "test_outliers_y_correlaciones", "test_tabla_invalida_devuelve_error"] test_file_path: "python/functions/datascience/generate_synthetic_eda_table_test.py" file_path: "python/functions/datascience/generate_synthetic_eda_table.py" --- ## Ejemplo ```bash # Genera /tmp/x.duckdb con la tabla `synthetic` (2000 filas, seed 42) fn run generate_synthetic_eda_table /tmp/x.duckdb synthetic 2000 42 ``` ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from datascience import generate_synthetic_eda_table res = generate_synthetic_eda_table("/tmp/x.duckdb", "synthetic", n_rows=2000, seed=42) # res == {"status":"ok", "db_path":"/tmp/x.duckdb", "table":"synthetic", # "n_rows":2000, "columns":[...19...], "seed":42} # Luego perfilala con el grupo eda: # fn run profile_table /tmp/x.duckdb synthetic ``` ## Cuando usarla - Cuando necesites un dataset de prueba REPRODUCIBLE para evaluar el motor AutomaticEDA de punta a punta: su contenido dispara, a proposito, num_distr, cat_distr, text_distr, correlacion, missingness (MCAR/MAR), modelos (PCA/KMeans/outliers), timeseries, geospatial, calidad, agregacion y los detectores semanticos / PII (`infer_semantic_type`). - Cuando escribas tests de capitulos del EDA y quieras una tabla con una columna que active CADA detector sin montar datos a mano. - Cuando quieras un fixture determinista (mismo seed -> misma tabla) para comparar el render del EDA entre versiones. ## Gotchas - **Impura**: escribe a disco (crea/reutiliza el archivo DuckDB). Reemplaza la tabla destino con `CREATE OR REPLACE`. - **Requiere `faker`, `duckdb`, `numpy` y `pandas`** instalados en el venv. Sin `faker` la generacion devuelve `{status:'error'}` (no lanza). - **`signup_date` queda como TIMESTAMP/DATE en DuckDB** (se construye con `datetime64[ns]`), NO VARCHAR — condicion para que `detect_time_column` la elija y se active el capitulo timeseries. Si fuese VARCHAR, el detector de fecha fallaria. - **El texto de `review` debe superar el gate de text_distr**: media de caracteres >= 50 y mediana de palabras >= 20. Por eso cada review concatena dos parrafos Faker (~50 palabras de mediana); no reducir el numero de frases o el capitulo text_distr no activa. - **Determinismo dependiente del orden de llamadas**: se siembra `Faker.seed(seed)` + `np.random.default_rng(seed)` al inicio; cambiar el orden de las extracciones cambia la salida aunque el seed sea el mismo. - **PII real-istica**: `email`/`iban`/`phone`/`customer_id` matchean los regex de `infer_semantic_type` (email/iban/phone_intl/uuid) al 100%; son datos sinteticos de Faker, no personas reales. ## Notas Mapa columna -> detector que activa: | Columna(s) | Tipo | Detector / capitulo | |---|---|---| | income, spending | num continua | correlacion POSITIVA fuerte (Pearson > 0.8) | | age, risk_score | num continua | correlacion NEGATIVA | | tenure_months, engagement_quad | num continua | relacion NO LINEAL (cuadratica) | | amount, n_purchases | num + outliers | num_distr / outliers (cola pesada + extremos inyectados) | | country (12), category (6), plan (3 desbalanceado) | categorica | cat_distr / agregacion (entropia baja en plan) | | review | texto libre multi-idioma | text_distr (len_mean>=50, mediana palabras>=20) + duplicados exactos | | signup_date | DATE/TIMESTAMP | timeseries | | latitude, longitude | num [-90,90]/[-180,180] | geospatial (detect_latlon_columns) | | customer_id, email, iban, phone | texto | semantic_type uuid/email/iban/phone_intl (PII) | | income+spending (co-nulos 12%), risk_score (nulo si plan=alta), review (8%) | nulos con patron | missingness MCAR/MAR |