feat(eda): funciones de agregación/OLAP para AutomaticEDA (groupby/pivot push-down + selección LLM)
Cuatro funciones nuevas del grupo eda que nutren el capítulo AGREGACION: - select_groupby_keys (pure): elige categóricas agrupables + numéricas medida desde el TableProfile. - groupby_stats_duckdb (impure): GROUP BY push-down en DuckDB (count/mean/median/std/min/max por grupo). - pivot_table_duckdb (impure): pivot A×B push-down, limitado a top filas/cols para no cortar. - suggest_aggregations_llm (impure): el LLM elige las agregaciones interesantes con fallback determinista. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
---
|
||||
name: groupby_stats_duckdb
|
||||
kind: function
|
||||
lang: py
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "def groupby_stats_duckdb(db_path: str, table: str, group_by: str, measures: list, aggs: list = None, top_n: int = 15) -> dict"
|
||||
description: "Agregaciones GROUP BY con push-down SQL en DuckDB: para cada measure numerica calcula mean/median/std/min/max por grupo (split-apply-combine en el motor), trayendo solo una fila por grupo. Nucleo de un capitulo de agregacion/OLAP de un EDA. count = tamanio del grupo, independiente de measures."
|
||||
tags: [eda, groupby, aggregation, olap, duckdb, datascience, push-down, split-apply-combine]
|
||||
uses_functions: [duckdb_query_readonly_py_infra]
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
params:
|
||||
- name: db_path
|
||||
desc: "Ruta al archivo DuckDB. Debe existir; el modo read_only NO crea la base. Path inexistente -> {status:'error'} sin lanzar."
|
||||
- name: table
|
||||
desc: "Nombre de la tabla. Se interpola citado con dobles comillas (soporta nombres con espacios; las comillas internas se escapan)."
|
||||
- name: group_by
|
||||
desc: "Columna por la que agrupar. Se interpola citada. Sus valores distintos son las claves de los grupos."
|
||||
- name: measures
|
||||
desc: "Lista de columnas numericas a agregar. Lista vacia es valida: cada grupo trae solo su tamanio `n` y `stats` vacio."
|
||||
- name: aggs
|
||||
desc: "Lista de agregaciones. None (default) = ['count','mean','median','std','min','max']. Validas: count (tamanio del grupo, va a `n`), mean->avg, median, std->stddev_samp, min, max (estas cinco por measure). Agg desconocido -> error."
|
||||
- name: top_n
|
||||
desc: "Maximo de grupos a devolver, ordenados por tamanio de grupo descendente (default 15). Internamente se piden top_n+1 para detectar truncado."
|
||||
output: "dict. En exito {status:'ok', group_by, measures:[...], aggs:[...], n_groups:int, truncated:bool, groups:[{key:<valor grupo>, n:int, stats:{<measure>:{mean,median,std,min,max}}}], note:str}. Las estadisticas son float o None (p.ej. std de un grupo de 1 fila -> NULL -> None). En error {status:'error', error:str} (no lanza)."
|
||||
tested: true
|
||||
tests: ["agrega por grupo con valores conocidos", "db inexistente devuelve error sin lanzar", "measures vacias agrega solo count", "columna con espacio agrupa bien"]
|
||||
test_file_path: "python/functions/datascience/groupby_stats_duckdb_test.py"
|
||||
file_path: "python/functions/datascience/groupby_stats_duckdb.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
import duckdb
|
||||
from datascience import groupby_stats_duckdb
|
||||
|
||||
# Cargar el titanic en una tabla DuckDB de prueba.
|
||||
db = "/tmp/titanic.duckdb"
|
||||
con = duckdb.connect(db)
|
||||
con.execute(
|
||||
"CREATE TABLE titanic AS "
|
||||
"SELECT * FROM read_csv_auto('https://raw.githubusercontent.com/"
|
||||
"datasciencedojo/datasets/master/titanic.csv')"
|
||||
)
|
||||
con.close()
|
||||
|
||||
# Agrupar por sexo midiendo edad y tarifa.
|
||||
res = groupby_stats_duckdb(db, "titanic", "Sex", ["Age", "Fare"])
|
||||
print(res["status"]) # ok
|
||||
print(res["n_groups"]) # 2 (male, female)
|
||||
for g in res["groups"]:
|
||||
print(g["key"], g["n"], round(g["stats"]["Fare"]["mean"], 2))
|
||||
# female 314 44.48
|
||||
# male 577 25.52
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando en un EDA necesitas el clasico split-apply-combine: "para cada categoria de X,
|
||||
¿cuanto vale en media/mediana/desviacion/min/max la metrica Y?". Es el nucleo de un
|
||||
capitulo de agregacion/OLAP. Usala antes de pintar barras o boxplots por grupo, para
|
||||
detectar segmentos con comportamiento distinto, o para resumir una tabla grande sin
|
||||
traer las filas a RAM: todo el GROUP BY ocurre push-down en el motor de DuckDB y solo
|
||||
viaja una fila por grupo. `top_n` te deja quedarte con los grupos mas poblados.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- Funcion impura: lee un archivo DuckDB del disco (read_only, nunca lo modifica). La
|
||||
tabla debe existir ya en el `.db` (no carga CSV; para eso crea la tabla antes).
|
||||
- Identificadores (tabla, group_by, measures) se interpolan citados con dobles comillas
|
||||
y escapando las internas: soporta nombres con espacios y evita inyeccion. No pases
|
||||
expresiones SQL como group_by/measure — solo nombres de columna.
|
||||
- `count` es el tamanio del grupo (`COUNT(*)`), independiente de las measures: se
|
||||
refleja en el campo `n` de cada grupo, NO como clave dentro de `stats`. Las claves de
|
||||
`stats[measure]` son las measure-aggs efectivas (mean/median/std/min/max menos count).
|
||||
- `std` usa `stddev_samp` (muestral, n-1): un grupo con una sola fila da `NULL` -> `None`.
|
||||
Las measures pueden contener NULLs; cada agregada los ignora segun la semantica de DuckDB.
|
||||
- `truncated:True` indica que habia mas grupos que `top_n` (se devolvieron los `top_n`
|
||||
mayores por tamanio). Sube `top_n` si necesitas todos los grupos.
|
||||
- Si `measures` esta vacio, cada grupo trae solo `n` y `stats == {}` (valido, util para
|
||||
un simple conteo por categoria).
|
||||
Reference in New Issue
Block a user