---
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`.
