Cómo crear un flujo de trabajo de análisis de datos exploratorio interactivo y avanzado utilizando PyGWalker y datos de ingeniería de funciones

En este tutorial, demostramos cómo ir más allá de los gráficos estáticos y con mucho código y crear un flujo de trabajo de análisis de datos exploratorios genuinamente interactivo directamente usando PyGWalker. Comenzamos preparando el conjunto de datos del Titanic para consultas interactivas a gran escala. Estas funciones de ingeniería listas para el análisis revelan la estructura subyacente de los datos y al mismo tiempo permiten una exploración detallada a nivel de fila y vistas agregadas de alto nivel para obtener una visión más profunda. La incorporación de una interfaz de arrastrar y soltar estilo Tableau directamente en el portátil permite realizar pruebas rápidas de hipótesis, comparaciones de cohortes intuitivas y una inspección eficiente de la calidad de los datos, todo sin la fricción de cambiar entre el código y las herramientas de visualización.

importar sys, subproceso, json, matemáticas, sistema operativo desde pathlib importar ruta def pip_install(pkgs): subprocess.check_call([sys.executable, “-m”, “pip”, “install”, “-q”] + paquetes) pip_install([
“pygwalker>=0.4.9”,
“duckdb>=0.10.0”,
“pandas>=2.0.0”,
“numpy>=1.24.0”,
“seaborn>=0.13.0”
]) importar numpy como np importar pandas como pd importar seaborn como sns df_raw = sns.load_dataset(“titanic”).copy() print(“Raw shape:”, df_raw.shape) display(df_raw.head(3))

Configuramos un entorno Colab limpio y reproducible instalando todas las dependencias necesarias para EDA interactivo. Cargamos el conjunto de datos del Titanic y realizamos una verificación inicial de cordura para comprender su estructura y escala sin procesar. Establece una base estable antes de que comience cualquier transformación o visualización.

def make_safe_bucket(series, bins=Ninguno, etiquetas=Ninguno, q=Ninguno, prefix=”bucket”): s = pd.to_numeric(series, errores=”coerce”) si q no es Ninguno: intente: cuts = pd.qcut(s, q=q, duplicados=”drop”) devuelve cuts.astype(“string”).fillna(“Unknown”) excepto Excepción: pasar si bins no es Ninguno: cuts = pd.cut(s, bins=bins, etiquetas=etiquetas, include_lowest=True) return cuts.astype(“string”).fillna(“Unknown”) return s.astype(“float64″) def preprocess_titanic_advanced(df): out = df.copy() out.columns = [c.strip().lower().replace(” “, “_”) for c in out.columns]

para c en [“survived”, “pclass”, “sibsp”, “parch”]: si c dentro fuera.columnas: fuera[c] = pd.to_numeric(fuera[c]errores=”coerce”).fillna(-1).astype(“int64”) si “edad” en out.columns: out[“age”] = pd.to_numeric(fuera[“age”]errores=”coerce”).astype(“float64”) fuera[“age_is_missing”] = fuera[“age”].isna() fuera[“age_bucket”] = make_safe_bucket( fuera[“age”]contenedores =[0, 12, 18, 30, 45, 60, 120]etiquetas =[“child”, “teen”, “young_adult”, “adult”, “mid_age”, “senior”]) si “tarifa” en out.columns: out[“fare”] = pd.to_numeric(fuera[“fare”]errores=”coerce”).astype(“float64”) fuera[“fare_is_missing”] = fuera[“fare”].isna() fuera[“log_fare”] = np.log1p(fuera[“fare”].fillna(0)) fuera[“fare_bucket”] = make_safe_bucket(fuera[“fare”]q=8) para c en [“sex”, “class”, “who”, “embarked”, “alone”, “adult_male”]: si c dentro fuera.columnas: fuera[c] = fuera[c].astype(“string”).fillna(“Unknown”) si “cabina” está en out.columns: out[“deck”] = fuera[“cabin”].astype(“cadena”).str.strip().str[0].fillna(“Desconocido”) fuera[“deck_is_missing”] = fuera[“cabin”].isna() más: fuera[“deck”] = “Desconocido” fuera[“deck_is_missing”] = Verdadero si “boleto” está en columnas de salida: t = salida[“ticket”].astype(“cadena”) fuera[“ticket_len”] = t.str.len().fillna(0).astype(“int64”) fuera[“ticket_has_alpha”] = t.str.contiene(r”[A-Za-z]”, expresión regular=Verdadero, na=Falso) fuera[“ticket_prefix”] = t.str.extract(r”^([A-Za-z\.\/\s]+)”, expand=False).fillna(“Ninguno”).str.strip() fuera[“ticket_prefix”] = fuera[“ticket_prefix”].replace(“”, “Ninguno”).astype(“string”) si “sibsp” en out.columns y “parch” en out.columns: out[“family_size”] = (fuera[“sibsp”] + fuera[“parch”] + 1).astype(“int64”) fuera[“is_alone”] = (fuera[“family_size”] == 1) si “nombre” está en out.columns: título = out[“name”].astype(“cadena”).str.extract(r”,\s*([^\.]+)\.”, expand=False).fillna(“Desconocido”).str.strip() vc = title.value_counts(dropna=False) keep = set(vc[vc >= 15].index.tolist()) fuera[“title”] = título.dónde(título.isin(mantener), otro=”Raro”).astype(“cadena”) else: fuera[“title”] = “Desconocido” fuera[“segment”] = (fuera[“sex”].fillna(“Desconocido”).astype(“cadena”) + ” | ” + salida[“class”].fillna(“Desconocido”).astype(“cadena”) + ” | ” + salida[“age_bucket”].fillna(“Unknown”).astype(“string”) ) para c en out.columns: si está fuera[c].dtype == bool: fuera[c] = fuera[c].astype(“int64”) si está fuera[c].dtype == “objeto”: fuera[c] = fuera[c].astype(“string”) devuelve df = preprocess_titanic_advanced(df_raw) print(“Forma preparada:”, df.shape) display(df.head(3))

Nos centramos en el preprocesamiento avanzado y la ingeniería de funciones para convertir los datos sin procesar en un formato listo para el análisis. Creamos funciones sólidas y seguras para DuckDB, como depósitos, segmentos y señales categóricas diseñadas que mejoran la exploración posterior. Nos aseguramos de que el conjunto de datos sea estable, expresivo y adecuado para consultas interactivas.

def informe_calidad_datos(df): filas = []
n = len(df) para c en df.columnas: s = df[c]
miss = int(s.isna().sum()) miss_pct = (miss / n * 100.0) if n else 0.0 nunique = int(s.nunique(dropna=True)) dtype = str(s.dtype) sample = s.dropna().head(3).tolist() rows.append({ “col”: c, “dtype”: dtype, “missing”: miss, “missing_%”: round(miss_pct, 2), “nunique”: nunique, “sample_values”: muestra }) return pd.DataFrame(rows).sort_values([“missing”, “nunique”]ascendente =[False, False]) dq = data_quality_report(df) display(dq.head(20)) RANDOM_SEED = 42 MAX_ROWS_FOR_UI = 200_000 df_for_ui = df if len(df_for_ui) > MAX_ROWS_FOR_UI: df_for_ui = df_for_ui.sample(MAX_ROWS_FOR_UI, random_state=RANDOM_SEED).reset_index(drop=True) agg = ( df.groupby([“segment”, “deck”, “embarked”]dropna=False) .agg( n=(“sobrevivió”, “tamaño”), survival_rate=(“sobrevivió”, “media”), avg_fare=(“tarifa”, “media”), avg_age=(“edad”, “media”), ) .reset_index() ) para c en [“survival_rate”, “avg_fare”, “avg_age”]: ag[c] = agregar[c].astype(“float64”) Ruta(“/content”).mkdir(parents=True, exist_ok=True) df_for_ui.to_csv(“/content/titanic_prepped_for_ui.csv”, index=False) agg.to_csv(“/content/titanic_agg_segment_deck_embarked.csv”, index=False)

Evaluamos la calidad de los datos y generamos una descripción general estructurada de faltantes, cardinalidad y tipos de datos. Preparamos un conjunto de datos a nivel de fila y una tabla agregada a nivel de cohorte para respaldar un análisis comparativo rápido. La representación dual nos permite explorar patrones detallados y tendencias de alto nivel simultáneamente.

importar pygwalker como pyg SPEC_PATH = Path(“/content/pygwalker_spec_titanic.json”) def load_spec(path): if path.exists(): intente: return json.loads(path.read_text()) excepto Excepción: return Ninguno return Ninguno def save_spec(path, spec_obj): intente: if isinstance(spec_obj, str): spec_obj = json.loads(spec_obj) path.write_text(json.dumps(spec_obj, indent=2)) devuelve Verdadero excepto Excepción: devuelve Falso def launch_pygwalker(df, spec_path): spec = load_spec(spec_path) kwargs = {} si la especificación no es Ninguna: kwargs[“spec”] = prueba de especificación: walker = pyg.walk(df, use_kernel_calc=True, **kwargs) excepto TypeError: walker = pyg.walk(df, **kwargs) si la especificación no es Ninguno más pyg.walk(df) capturado = Ninguno para el atributo [“spec”, “_spec”]: si hasattr(walker, attr): prueba: capture = getattr(walker, attr) break excepto Excepción: pasar por metanfetamina en [“to_spec”, “export_spec”, “get_spec”]: si capturado es Ninguno y hasattr(walker, meth): intenta: capturado = getattr(walker, meth)() break excepto Excepción: pasar si capturado no es Ninguno: save_spec(spec_path, capture) return walker walker_rows = launch_pygwalker(df_for_ui, SPEC_PATH) walker_agg = pyg.walk(agg)

Integramos PyGWalker para transformar nuestras tablas preparadas en una interfaz analítica totalmente interactiva de arrastrar y soltar. Mantenemos la especificación de visualización para que los diseños y codificaciones del tablero sobrevivan a las reejecuciones del cuaderno. Convierte el portátil en un entorno de exploración reutilizable de estilo BI.

HTML_PATH = Path(“/content/pygwalker_titanic_dashboard.html”) def export_html_best_effort(df, spec_path, out_path): spec = load_spec(spec_path) html = Ninguno intente: html = pyg.walk(df, spec=spec, return_html=True) si la especificación no es Ninguno más pyg.walk(df, return_html=True) excepto Excepción: html = Ninguno si html es Ninguno: para fn en [“to_html”, “export_html”]: si hasattr(pyg, fn): prueba: f = getattr(pyg, fn) html = f(df, spec=spec) si la especificación no es Ninguno más f(df) break excepto Excepción: continuar si html es Ninguno: devolver Ninguno si no es instancia(html, str): html = str(html) out_path.write_text(html, encoding=”utf-8″) return out_path export_html_best_effort(df_for_ui, SPEC_PATH, HTML_PATH)

Ampliamos el flujo de trabajo exportando el panel interactivo como un artefacto HTML independiente. Nos aseguramos de que el análisis se pueda compartir o revisar sin necesidad de un entorno Python o una sesión de Colab. Completa el proceso desde datos sin procesar hasta información distribuible e interactiva.

Panel interactivo de EDA

En conclusión, establecimos un patrón sólido para EDA avanzado que va mucho más allá del conjunto de datos del Titanic sin dejar de ser completamente nativo de los portátiles. Mostramos cómo el preprocesamiento cuidadoso, la seguridad de tipos y el diseño de funciones permiten que PyGWalker opere de manera confiable con datos complejos y cómo la combinación de registros detallados con resúmenes agregados desbloquea poderosos flujos de trabajo analíticos. En lugar de tratar la visualización como una ocurrencia tardía, la utilizamos como una capa interactiva de primera clase, lo que nos permite iterar, validar suposiciones y extraer conocimientos en tiempo real.

Consulte los códigos completos aquí. Además, no dude en seguirnos en Twitter y no olvide unirse a nuestro SubReddit de más de 100.000 ML y suscribirse a nuestro boletín. ¡Esperar! estas en telegrama? Ahora también puedes unirte a nosotros en Telegram.