File size: 4,910 Bytes
34a568c dff68e1 f41b181 dff68e1 34a568c dff68e1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | import streamlit as st
import networkx as nx
import matplotlib.pyplot as plt
import pandas as pd
import random
# Configuración de la página
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.
""")
# --- BARRA LATERAL: Configuración ---
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() # Limpia todo para reiniciar
# --- LÓGICA DE SESSION STATE ---
# Necesitamos mantener el estado del grafo y los valores entre interacciones
if 'graph' not in st.session_state:
# Crear grafo dirigido aleatorio
G = nx.gnp_random_graph(num_nodes, prob_link, directed=True, seed=random.randint(1, 1000))
st.session_state.graph = G
# Calcular layout fijo para que los nodos no bailen en cada paso
st.session_state.pos = nx.spring_layout(G, k=0.8, iterations=50)
# Inicializar PageRank: todos empiezan con 1/N
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()]
# Función para dar un paso del algoritmo
def step_pagerank():
G = st.session_state.graph
current_pr = st.session_state.pagerank
d = damping_factor
N = len(G.nodes)
new_pr = {}
# Fórmula básica de PageRank iterativo
for i in G.nodes():
incoming_sum = 0
# Buscar nodos que apuntan a 'i'
for node_j in G.predecessors(i):
# Out-degree del nodo j
out_degree = G.out_degree(node_j)
if out_degree > 0:
incoming_sum += current_pr[node_j] / out_degree
# Aplicar fórmula: (1-d)/N + d * sum(PR(j)/L(j))
val = ((1 - d) / N) + (d * incoming_sum)
new_pr[i] = val
# Manejo básico de sumideros (nodos sin salidas) para mantener la suma ~1
# En implementaciones complejas se redistribuye la masa perdida
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())
# --- INTERFAZ PRINCIPAL ---
col1, col2 = st.columns([3, 1])
with col1:
st.subheader(f"Grafo - Iteración: {st.session_state.iteration}")
# Dibujar Grafo
fig, ax = plt.subplots(figsize=(8, 6))
G = st.session_state.graph
pos = st.session_state.pos
pr_values = st.session_state.pagerank
# Escalar tamaño de nodos basado en PR (multiplicador estético)
node_sizes = [v * 5000 for v in pr_values.values()]
# Colores basados en importancia
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)
# Botón grande para iterar
if st.button("▶️ Siguiente Iteración", use_container_width=True):
step_pagerank()
st.rerun()
with col2:
st.subheader("Datos")
# Crear DataFrame para mostrar valores
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)
# Métrica de convergencia (cambio respecto al paso anterior)
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!")
# --- GRÁFICO DE EVOLUCIÓN ---
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.") |