--- name: generate_synthetic_eda_folder kind: function lang: py domain: datascience version: "1.0.0" purity: impure signature: "def generate_synthetic_eda_folder(out_dir: str, n_rows: int = 2000, seed: int = 42) -> dict" description: "Genera una carpeta con 3 CSV RELACIONADOS (customers, orders, reviews) deterministas por seed (Faker + numpy) para ejercitar el motor AutomaticEDA multi-tabla / profile_database. orders.customer_id y reviews.customer_id estan contenidos al 100% en customers.customer_id (PK uuid), de modo que la deteccion FK por containment (min_inclusion=0.9) descubre ambas relaciones. customers es la tabla padre; reutiliza helpers de generate_synthetic_eda_table (texto multi-idioma, lat/lon validas, amount con outliers). Estilo dict-no-throw: nunca lanza." tags: [eda, synthetic, faker, testing, fixture, datascience] params: - name: out_dir desc: "Carpeta de salida. Se crea con mkdir -p si no existe. Recibe customers.csv, orders.csv y reviews.csv." - name: n_rows desc: "Numero de clientes (filas de customers). orders ~= 2*n_rows filas, reviews ~= n_rows filas. Default 2000." - name: seed desc: "Semilla para Faker (Faker.seed) y numpy (np.random.default_rng). Mismo seed -> CSVs identicos byte a byte. Default 42." output: "dict dict-no-throw. En exito {status:'ok', out_dir, files:{customers,orders,reviews}, n_customers, n_orders, n_reviews, expected_relations:[{from_table,from_col,to_table,to_col}, ...], seed}. En error (sin lanzar, p.ej. n_rows<=0) {status:'error', error:str}. expected_relations declara las 2 FK orders->customers y reviews->customers (ambas por customer_id)." uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: [] tested: true tests: ["test_genera_ok_y_archivos", "test_determinismo_mismo_seed", "test_seeds_distintos_difieren", "test_fk_containment", "test_review_text_mediana_palabras", "test_n_rows_invalido"] test_file_path: "python/functions/datascience/generate_synthetic_eda_folder_test.py" file_path: "python/functions/datascience/generate_synthetic_eda_folder.py" --- ## Ejemplo ```bash # Genera /tmp/eda_folder/{customers,orders,reviews}.csv (300 customers, seed 42) fn run generate_synthetic_eda_folder /tmp/eda_folder 300 42 ``` ```python import sys, os sys.path.insert(0, os.path.join("python", "functions")) from datascience import generate_synthetic_eda_folder res = generate_synthetic_eda_folder("/tmp/eda_folder", n_rows=300, seed=42) # res["files"] -> {"customers": ".../customers.csv", "orders": ..., "reviews": ...} # res["expected_relations"] -> orders.customer_id y reviews.customer_id -> customers.customer_id # Luego perfila la carpeta/base con el grupo eda: # fn run profile_database /tmp/eda_folder ``` ## Cuando usarla - Cuando necesites un fixture REPRODUCIBLE multi-tabla para evaluar el EDA de carpeta/base (`profile_database`, join graph, capitulo de relaciones inter-tabla) con relaciones FK reales y detectables. - Cuando escribas tests de la deteccion de claves foraneas por containment: orders y reviews referencian customer_id contenido al 100% en customers (inclusion 1.0 >= min_inclusion 0.9). - Como contraparte multi-tabla de `generate_synthetic_eda_table` (que cubre el EDA de UNA tabla). ## Gotchas - **Impura**: escribe 3 CSV a disco (`mkdir -p` de la carpeta). Sobrescribe los CSV existentes con el mismo nombre. - **Requiere `faker`, `numpy` y `pandas`** en el venv. Sin `faker` devuelve `{status:'error'}` (no lanza). - **El containment depende del orden**: customers se genera PRIMERO y orders/reviews muestrean sus `customer_id`. Si se invierte el orden, la FK deja de estar contenida y el detector no la encuentra. - **`signup_date`/`ts` se escriben como texto ISO en el CSV** (`YYYY-MM-DD` / `YYYY-MM-DD HH:MM:SS`): es CSV, todo es texto; el profiler los promociona a datetime al leerlos. - **Determinismo dependiente del orden de llamadas**: se siembra `Faker.seed(seed)` + `np.random.default_rng(seed)` al inicio; mismo seed -> CSVs identicos byte a byte. - **Reutiliza helpers privados** de `generate_synthetic_eda_table` (`_make_fakers`, `_make_latlon`, `_make_reviews`, `_amount_with_outliers`): no romper esas firmas sin actualizar esta funcion. ## Notas Estructura generada: | Archivo | PK | FK | Columnas clave | |---|---|---|---| | customers.csv | customer_id (uuid) | — | name, country, signup_date, latitude, longitude, email | | orders.csv | order_id (uuid) | customer_id -> customers | amount (lognormal + outliers), category, ts | | reviews.csv | review_id (uuid) | customer_id -> customers | review_text (multi-idioma, mediana palabras>=20), rating (1..5) | orders tiene ~2x filas que customers y reviews ~1x. Todos los `customer_id` de orders y reviews estan contenidos en customers (containment ⊆), por lo que la deteccion FK por inclusion descubre las dos relaciones declaradas en `expected_relations`.