feat(infra): auto-commit con 56 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,40 +15,73 @@ from datascience import (
|
||||
)
|
||||
|
||||
|
||||
def _to_numeric_subset(columns: dict) -> dict:
|
||||
"""Extrae las columnas numericas como {nombre: [float values]}.
|
||||
def _pf(v):
|
||||
"""Parsea un valor a float; devuelve None si es None/bool/no parseable."""
|
||||
if v is None or isinstance(v, bool):
|
||||
return None
|
||||
try:
|
||||
return float(v)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
Solo se quedan las columnas con ``type == "numeric"``. Para cada una, los
|
||||
valores se convierten a float cuando es posible y los que son None o no
|
||||
parseables se descartan (la lista resultante puede ser mas corta que la
|
||||
original). Mantiene el orden de aparicion de las columnas.
|
||||
|
||||
def _to_numeric_subset(columns: dict) -> dict:
|
||||
"""Extrae las columnas numericas alineadas por fila (listwise deletion).
|
||||
|
||||
Solo se quedan las columnas con ``type == "numeric"``. CLAVE: la alineacion
|
||||
por fila se preserva. Pasos:
|
||||
1. Descarta columnas numericas con menos del 50% de valores parseables
|
||||
(evita que una columna casi-toda-nula tire todas las filas en el paso 3).
|
||||
2. Sobre las columnas buenas, conserva SOLO las filas en las que TODAS
|
||||
tienen un valor numerico (listwise deletion).
|
||||
El resultado es un mapa {nombre: [float, ...]} donde todas las listas tienen
|
||||
la MISMA longitud (filas completas) — requisito de PCA/KMeans/IsolationForest
|
||||
(matriz rectangular sin NaN). El bug previo descartaba None por columna,
|
||||
dejando longitudes desiguales y reventando sklearn con ValueError.
|
||||
|
||||
Args:
|
||||
columns: mapa {nombre_columna: {"values": list, "type": str}}.
|
||||
columns: mapa {nombre_columna: {"values": list, "type": str}}; las listas
|
||||
llegan alineadas por fila (misma longitud, None donde no hay dato).
|
||||
|
||||
Returns:
|
||||
dict {nombre_columna: [float, ...]} solo con columnas numericas.
|
||||
dict {nombre_columna: [float, ...]} con columnas numericas de igual
|
||||
longitud. Vacio si no hay columnas numericas validas.
|
||||
"""
|
||||
numeric: dict[str, list] = {}
|
||||
if not isinstance(columns, dict):
|
||||
return numeric
|
||||
return {}
|
||||
raw: dict[str, list] = {}
|
||||
for name, meta in columns.items():
|
||||
if not isinstance(meta, dict):
|
||||
continue
|
||||
if meta.get("type") != "numeric":
|
||||
continue
|
||||
values = meta.get("values")
|
||||
if not isinstance(values, (list, tuple)):
|
||||
continue
|
||||
parsed: list[float] = []
|
||||
for v in values:
|
||||
if v is None or isinstance(v, bool):
|
||||
continue
|
||||
try:
|
||||
parsed.append(float(v))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
numeric[name] = parsed
|
||||
if isinstance(values, (list, tuple)):
|
||||
raw[name] = list(values)
|
||||
if not raw:
|
||||
return {}
|
||||
|
||||
# Longitud comun (min, defensivo si llegaran desalineadas).
|
||||
n = min(len(v) for v in raw.values())
|
||||
if n == 0:
|
||||
return {}
|
||||
|
||||
# 1) Parsea por celda y descarta columnas con <50% de valores parseables.
|
||||
good: dict[str, list] = {}
|
||||
for name, values in raw.items():
|
||||
parsed = [_pf(values[i]) for i in range(n)]
|
||||
if sum(1 for x in parsed if x is not None) >= 0.5 * n:
|
||||
good[name] = parsed
|
||||
if not good:
|
||||
return {}
|
||||
|
||||
# 2) Listwise: conserva solo filas donde TODAS las columnas tienen valor.
|
||||
names = list(good.keys())
|
||||
numeric: dict[str, list] = {name: [] for name in names}
|
||||
for i in range(n):
|
||||
if all(good[name][i] is not None for name in names):
|
||||
for name in names:
|
||||
numeric[name].append(good[name][i])
|
||||
return numeric
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user