Files
fn_registry/python/functions/datascience/project_clusters_2d.md
T
egutierrez 4de071f2f9 feat(eda): project_clusters_2d + describe_clusters_llm para el capitulo MODELOS
project_clusters_2d (pura): PCA(2)+KMeans sobre el MISMO subset estandarizado,
devolviendo proyeccion 2D y labels alineados por fila + centroides en espacio PCA
+ perfiles de cluster desestandarizados. Es la pieza que garantiza la alineacion
points<->labels que pca_explained y kmeans_segments no cubren (estandarizan por
separado y kmeans descarta los labels). Habilita el scatter PCA coloreado por
cluster (MUST-8.1).

describe_clusters_llm (impura): micro-analisis LLM de los clusters en una sola
llamada a ask_llm (grupo claude-direct), devuelve titulo + descripcion por cluster
con degradacion dict-no-throw a titulos genericos si el LLM no responde (MUST-8.2).

Ambas re-exportadas en datascience/__init__.py. Tests: 6/6 y 9/9 (sin red).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 14:57:27 +02:00

5.6 KiB

name, kind, lang, domain, version, purity, signature, description, tags, params, output, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags params output uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path
project_clusters_2d function py datascience 1.0.0 pure def project_clusters_2d(columns: dict, k_min: int = 2, k_max: int = 8, max_points: int = 2000) -> dict PCA a 2D + KMeans sobre el MISMO subset numerico estandarizado, devolviendo proyeccion 2D y labels de cluster ALINEADOS por fila para pintar un scatter PCA coloreado por cluster. Estandariza una sola vez, elige k por silhouette y proyecta centroides al espacio PCA. Determinista.
eda
models
clustering
pca
kmeans
scatter
dimensionality-reduction
datascience
sklearn
name desc
columns Mapa {nombre_columna: [valores numericos]}. Listas alineadas por fila (misma longitud). Columnas no numericas o con <2 valores distintos se descartan; None/NaN descartan la fila completa (listwise).
name desc
k_min Numero minimo de clusters a probar por silhouette (default 2). El minimo de filas validas requerido es max(3, k_min*2).
name desc
k_max Numero maximo de clusters a probar (default 8). Se acota a min(k_max, n_filas_validas-1).
name desc
max_points Tope de puntos devueltos en points/labels (default 2000). Si n_used lo supera, points y labels se submuestrean CONJUNTAMENTE con paso determinista para seguir alineados; el fit usa siempre todas las filas.
dict con points (proyeccion 2D, posiblemente submuestreada a max_points), labels (cluster de cada point, alineado con points), centers_2d (centroides en espacio PCA, len==best_k), best_k, silhouette, explained_2d ([var PC1, var PC2]), cluster_sizes (sobre n_used total), cluster_profiles (lista de {cluster, size, pct, centroid_original, distinctive top-3 por |z|, centroid_z}), feature_names, n_used (filas del fit antes de muestreo) y note ("" si ok). Con <2 columnas numericas o <max(3, k_min*2) filas validas devuelve best_k=0, listas vacias y note 'datos insuficientes' sin lanzar excepcion.
false
numpy
scikit-learn
true
test_golden_three_blobs_aligned_projection_and_clusters
test_edge_subsampling_keeps_points_labels_aligned
test_edge_single_numeric_column_insufficient
test_edge_too_few_rows_insufficient
test_edge_non_numeric_column_dropped_without_error
test_edge_constant_column_dropped
python/functions/datascience/project_clusters_2d_test.py python/functions/datascience/project_clusters_2d.py

Ejemplo

import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from datascience.project_clusters_2d import project_clusters_2d

# Tres grupos gaussianos bien separados sobre 4 features.
import numpy as np
rng = np.random.default_rng(0)
rows = []
for center in (np.full(4, 0.0), np.full(4, 12.0), np.array([0.0, 12.0, 0.0, 12.0])):
    rows.extend(rng.normal(loc=center, scale=0.4, size=(50, 4)))
mat = np.array(rows)
columns = {f"f{j}": [float(v) for v in mat[:, j]] for j in range(4)}

res = project_clusters_2d(columns, k_min=2, k_max=8)
print(res["best_k"])                      # 3
print(len(res["points"]), len(res["labels"]))  # 150 150 (alineados)
print(len(res["centers_2d"]))             # == best_k
print([round(v, 2) for v in res["explained_2d"]])  # varianza de PC1, PC2
# Pintar: scatter(points[:,0], points[:,1], c=labels) + marcar centers_2d.

Cuando usarla

Cuando, durante un EDA, quieres un scatter 2D de un dataset tabular numerico coloreado por segmento descubierto automaticamente, y necesitas que cada punto de la proyeccion lleve su etiqueta de cluster correcta. Usala en vez de combinar pca_explained + kmeans_segments a mano: esas estandarizan por separado y descartan los labels, asi que sus salidas no se pueden cruzar fila a fila. Esta funcion garantiza esa alineacion (mismo X estandarizado para PCA y KMeans) y ademas proyecta los centroides KMeans al espacio PCA para dibujarlos.

Gotchas

  • Funcion pura y determinista (StandardScaler + PCA random_state=0 + KMeans random_state=0, n_init=10), pero requiere numpy y scikit-learn instalados.
  • points/labels pueden venir submuestreados si n_used > max_points (paso determinista [::ceil(n_used/max_points)]); n_used, centers_2d, cluster_sizes y cluster_profiles se calculan SIEMPRE sobre todas las filas. Cuando hay submuestreo, note lo indica.
  • centroid_z y distinctive estan en z-score (espacio escalado); centroid_original esta en las unidades originales (via scaler.inverse_transform). No mezcles ambos al interpretar.
  • centers_2d esta en el espacio PCA (coordenadas del scatter), no en unidades originales: pintalo sobre el mismo eje que points.
  • Silhouette baja con best_k alto sugiere que no hay estructura de cluster real; el scatter puede no mostrar grupos separados.

Notas

Pieza de composicion que pca_explained + kmeans_segments no cubren: ambas estandarizan internamente por separado (cada una su propio StandardScaler) y kmeans_segments no expone los labels por fila, por lo que no se pueden cruzar con la projection de pca_explained. Esta funcion usa sklearn directo (StandardScaler una sola vez compartido por PCA y KMeans) para garantizar la alineacion points[i] <-> labels[i] y proyectar los centroides KMeans al espacio PCA. Coercion y listwise deletion siguen el estilo de pca_explained (None/NaN -> fila descartada, columnas no parseables o constantes descartadas). Degrada con gracia: con <2 columnas numericas o <max(3, k_min*2) filas validas devuelve note: "datos insuficientes" sin lanzar excepcion (try/except defensivo en todo el cuerpo).