| | import streamlit as st |
| | import networkx as nx |
| | import matplotlib.pyplot as plt |
| | import pandas as pd |
| | import random |
| |
|
| | |
| | st.set_page_config(page_title="Visualizador de PageRank", layout="wide") |
| |
|
| |
|
| | st.title("🕸️ Visualizador Interactivo de PageRank") |
| | st.markdown(""" |
| | Esta herramienta permite visualizar cómo fluye la importancia (PageRank) a través de los nodos de una red. |
| | El **tamaño del nodo** representa su PageRank actual. |
| | """) |
| |
|
| | |
| | with st.sidebar: |
| | st.header("Configuración del Grafo") |
| | |
| | num_nodes = st.slider("Número de Nodos", min_value=3, max_value=20, value=8) |
| | prob_link = st.slider("Probabilidad de enlace (Densidad)", min_value=0.1, max_value=1.0, value=0.3) |
| | damping_factor = st.slider("Factor de Amortiguación (Damping)", min_value=0.5, max_value=0.99, value=0.85) |
| | |
| | st.markdown("---") |
| | |
| | if st.button("🔄 Generar Nuevo Grafo"): |
| | st.session_state.clear() |
| |
|
| | |
| | |
| |
|
| | if 'graph' not in st.session_state: |
| | |
| | G = nx.gnp_random_graph(num_nodes, prob_link, directed=True, seed=random.randint(1, 1000)) |
| | st.session_state.graph = G |
| | |
| | |
| | st.session_state.pos = nx.spring_layout(G, k=0.8, iterations=50) |
| | |
| | |
| | initial_pr = {node: 1.0/num_nodes for node in G.nodes()} |
| | st.session_state.pagerank = initial_pr |
| | st.session_state.iteration = 0 |
| | st.session_state.history = [initial_pr.copy()] |
| |
|
| | |
| | def step_pagerank(): |
| | G = st.session_state.graph |
| | current_pr = st.session_state.pagerank |
| | d = damping_factor |
| | N = len(G.nodes) |
| | |
| | new_pr = {} |
| | |
| | |
| | for i in G.nodes(): |
| | incoming_sum = 0 |
| | |
| | for node_j in G.predecessors(i): |
| | |
| | out_degree = G.out_degree(node_j) |
| | if out_degree > 0: |
| | incoming_sum += current_pr[node_j] / out_degree |
| | |
| | |
| | val = ((1 - d) / N) + (d * incoming_sum) |
| | new_pr[i] = val |
| | |
| | |
| | |
| | total_mass = sum(new_pr.values()) |
| | for k in new_pr: |
| | new_pr[k] /= total_mass |
| | |
| | st.session_state.pagerank = new_pr |
| | st.session_state.iteration += 1 |
| | st.session_state.history.append(new_pr.copy()) |
| |
|
| | |
| |
|
| | col1, col2 = st.columns([3, 1]) |
| |
|
| | with col1: |
| | st.subheader(f"Grafo - Iteración: {st.session_state.iteration}") |
| | |
| | |
| | fig, ax = plt.subplots(figsize=(8, 6)) |
| | |
| | G = st.session_state.graph |
| | pos = st.session_state.pos |
| | pr_values = st.session_state.pagerank |
| | |
| | |
| | node_sizes = [v * 5000 for v in pr_values.values()] |
| | |
| | |
| | node_colors = list(pr_values.values()) |
| | |
| | nx.draw_networkx_nodes(G, pos, node_size=node_sizes, node_color=node_colors, cmap=plt.cm.viridis, alpha=0.9, ax=ax) |
| | nx.draw_networkx_edges(G, pos, arrowstyle='->', arrowsize=20, edge_color='gray', width=1, alpha=0.5, ax=ax, connectionstyle="arc3,rad=0.1") |
| | nx.draw_networkx_labels(G, pos, font_size=10, font_color="white", font_weight="bold", ax=ax) |
| | |
| | ax.axis('off') |
| | st.pyplot(fig) |
| |
|
| | |
| | if st.button("▶️ Siguiente Iteración", use_container_width=True): |
| | step_pagerank() |
| | st.rerun() |
| |
|
| | with col2: |
| | st.subheader("Datos") |
| | |
| | |
| | df = pd.DataFrame.from_dict(st.session_state.pagerank, orient='index', columns=['PageRank']) |
| | df = df.sort_values(by='PageRank', ascending=False) |
| | |
| | st.dataframe(df.style.background_gradient(cmap='viridis'), height=400) |
| | |
| | |
| | if st.session_state.iteration > 0: |
| | prev = st.session_state.history[-2] |
| | curr = st.session_state.history[-1] |
| | diff = sum([abs(curr[n] - prev[n]) for n in curr]) |
| | st.metric("Cambio Total (Delta)", f"{diff:.4f}") |
| | |
| | if diff < 0.001: |
| | st.success("¡El algoritmo ha convergido!") |
| |
|
| | |
| | st.divider() |
| | st.subheader("Evolución de Pesos por Nodo") |
| |
|
| | if st.session_state.iteration > 0: |
| | history_df = pd.DataFrame(st.session_state.history) |
| | st.line_chart(history_df) |
| | else: |
| | st.info("Presiona 'Siguiente Iteración' para ver cómo evolucionan los pesos.") |