# Transformacion de datos tabulares

Gap identificado en el registry. Reimplementar desde cero.

Hay estadistica y rolling windows pero no transformaciones tabulares puras.
Necesarias para pipelines Go/Python que procesan CSVs o resultados de queries
sin depender de pandas.

Datos tabulares = `list[dict]` en Python, `[]map[string]any` en Go.

## Funciones a implementar

### pivot

- **Dominio:** datascience
- **Lang:** Python
- **Purity:** pure
- **Signature:** `pivot(rows: list[dict], index: str, columns: str, values: str, agg: str = "sum") -> list[dict]`
- **Retorno:** Lista de dicts donde cada dict tiene el campo index + un campo por cada valor unico de columns
- **Descripcion:** Pivot table sin pandas. Agrupa por `index`, expande valores unicos de `columns` como nuevas columnas, agrega `values` con la funcion indicada.
- **Agregaciones:** sum, count, mean, min, max, first, last
- **Ejemplo:**
  ```python
  rows = [
      {"region": "US", "product": "A", "sales": 10},
      {"region": "US", "product": "B", "sales": 20},
      {"region": "EU", "product": "A", "sales": 15},
  ]
  pivot(rows, index="region", columns="product", values="sales")
  # [{"region": "US", "A": 10, "B": 20}, {"region": "EU", "A": 15, "B": 0}]
  ```
- **Deps:** ninguna (solo stdlib)
- **Tests:**
  - Pivot basico con sum
  - Pivot con count y mean
  - Valores faltantes rellenados con 0 (numericos) o None (strings)
  - Una sola fila
  - Multiples valores por celda (requiere agregacion)

### melt

- **Dominio:** datascience
- **Lang:** Python
- **Purity:** pure
- **Signature:** `melt(rows: list[dict], id_vars: list[str], value_vars: list[str], var_name: str = "variable", value_name: str = "value") -> list[dict]`
- **Retorno:** Lista de dicts en formato largo (unpivoted)
- **Descripcion:** Inversa de pivot — convierte columnas en filas. Cada combinacion de id_vars + value_var genera una fila.
- **Ejemplo:**
  ```python
  rows = [{"region": "US", "q1": 10, "q2": 20}]
  melt(rows, id_vars=["region"], value_vars=["q1", "q2"])
  # [{"region": "US", "variable": "q1", "value": 10},
  #  {"region": "US", "variable": "q2", "value": 20}]
  ```
- **Deps:** ninguna
- **Tests:**
  - Melt basico
  - Multiples id_vars
  - value_vars con None → melt todas las columnas no-id
  - Fila con campo faltante en value_vars

### join_by_key

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `join_by_key(left: list[dict], right: list[dict], key: str, how: str = "inner") -> list[dict]`
- **Retorno:** Lista de dicts con campos de ambos lados mergeados
- **Descripcion:** Join de dos tablas por una clave comun. Soporta inner, left, right, outer. Campos duplicados del right se sufijan con `_right`.
- **Algoritmo:**
  1. Indexar right por key en un dict (O(n))
  2. Iterar left, buscar match en index (O(m))
  3. Merge de campos segun tipo de join
- **Deps:** ninguna
- **Tests:**
  - Inner join (solo matches)
  - Left join (todos los left, None para right sin match)
  - Right join
  - Outer join
  - Campos duplicados con sufijo
  - Key ausente en alguna fila

### aggregate_by_group

- **Dominio:** datascience
- **Lang:** Python
- **Purity:** pure
- **Signature:** `aggregate_by_group(rows: list[dict], group_by: list[str], aggs: dict[str, str]) -> list[dict]`
- **Retorno:** Lista de dicts agrupados con valores agregados
- **Descripcion:** GROUP BY + agregaciones sobre datos tabulares. `aggs` es `{"column": "sum|mean|count|min|max|first|last|collect"}`. `collect` acumula valores en lista.
- **Ejemplo:**
  ```python
  rows = [{"dept": "eng", "salary": 100}, {"dept": "eng", "salary": 120}, {"dept": "sales", "salary": 80}]
  aggregate_by_group(rows, group_by=["dept"], aggs={"salary": "mean"})
  # [{"dept": "eng", "salary": 110.0}, {"dept": "sales", "salary": 80.0}]
  ```
- **Deps:** ninguna
- **Tests:**
  - Group by una columna con sum
  - Group by multiples columnas
  - Agregacion mean, count, min, max
  - collect acumula en lista
  - Grupo con una sola fila
  - Campo con None (ignorar en agregaciones numericas)

### pivot (Go)

- **Dominio:** datascience
- **Lang:** Go
- **Purity:** pure
- **Signature:** `Pivot(rows []map[string]any, index, columns, values, agg string) []map[string]any`
- **Descripcion:** Misma semantica que la version Python. Go no tiene generics suficientes para hacerlo elegante, pero con `map[string]any` y type assertions funciona para datos deserializados de JSON/SQL.
- **Tests:** Mismos casos que Python

### join_by_key (Go)

- **Dominio:** core
- **Lang:** Go
- **Purity:** pure
- **Signature:** `JoinByKey(left, right []map[string]any, key, how string) []map[string]any`
- **Descripcion:** Misma semantica que la version Python.
- **Tests:** Mismos casos que Python
