import streamlit as st import networkx as nx import matplotlib.pyplot as plt # Sidebar for selecting an option sidebar_option = st.sidebar.radio("Select an option", ["Select an option", "Basic: Properties", "Basic: Read and write graphs", "Basic: Simple graph", "Basic: Simple graph Directed", "Drawing: Custom Node Position", "Drawing: Chess Masters", "Drawing: Cluster Layout"]) # Helper function to draw and display graph def draw_graph(G, pos=None, title="Graph Visualization", edgewidth=None, nodesize=None): if edgewidth is None: edgewidth = [1] * len(G.edges()) # Default edge width if not provided if nodesize is None: nodesize = [300] * len(G.nodes()) # Default node size if not provided plt.figure(figsize=(12, 12)) nx.draw_networkx_edges(G, pos, alpha=0.3, width=edgewidth, edge_color="m") nx.draw_networkx_nodes(G, pos, node_size=nodesize, node_color="#210070", alpha=0.9) label_options = {"ec": "k", "fc": "white", "alpha": 0.7} nx.draw_networkx_labels(G, pos, font_size=14, bbox=label_options) # Title/legend font = {"fontname": "Helvetica", "color": "k", "fontweight": "bold", "fontsize": 14} ax = plt.gca() ax.set_title(title, font) ax.text( 0.80, 0.10, "edge width = # games played", horizontalalignment="center", transform=ax.transAxes, fontdict=font, ) ax.text( 0.80, 0.06, "node size = # games won", horizontalalignment="center", transform=ax.transAxes, fontdict=font, ) # Resize figure for label readability ax.margins(0.1, 0.05) plt.axis("off") st.pyplot(plt) # Drawing: Cluster Layout def display_cluster_layout(): st.title("Drawing: Cluster Layout") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": G = nx.davis_southern_women_graph() # Compute communities using greedy modularity community detection communities = nx.community.greedy_modularity_communities(G) # Compute positions for the node clusters as if they were themselves nodes in a supergraph supergraph = nx.cycle_graph(len(communities)) superpos = nx.spring_layout(G, scale=50, seed=429) # Use the "supernode" positions as the center of each node cluster centers = list(superpos.values()) pos = {} for center, comm in zip(centers, communities): pos.update(nx.spring_layout(nx.subgraph(G, comm), center=center, seed=1430)) # Nodes colored by cluster for nodes, clr in zip(communities, ("tab:blue", "tab:orange", "tab:green")): nx.draw_networkx_nodes(G, pos=pos, nodelist=nodes, node_color=clr, node_size=100) nx.draw_networkx_edges(G, pos=pos) plt.tight_layout() st.pyplot(plt) elif option == "Create your own": uploaded_file = st.file_uploader("Upload your own graph file (in GML format)", type="gml") if uploaded_file is not None: G_custom = nx.read_gml(uploaded_file) # Compute communities using greedy modularity community detection communities = nx.community.greedy_modularity_communities(G_custom) # Compute positions for the node clusters as if they were themselves nodes in a supergraph supergraph = nx.cycle_graph(len(communities)) superpos = nx.spring_layout(G_custom, scale=50, seed=429) # Use the "supernode" positions as the center of each node cluster centers = list(superpos.values()) pos = {} for center, comm in zip(centers, communities): pos.update(nx.spring_layout(nx.subgraph(G_custom, comm), center=center, seed=1430)) # Nodes colored by cluster for nodes, clr in zip(communities, ("tab:blue", "tab:orange", "tab:green")): nx.draw_networkx_nodes(G_custom, pos=pos, nodelist=nodes, node_color=clr, node_size=100) nx.draw_networkx_edges(G_custom, pos=pos) plt.tight_layout() st.pyplot(plt) # Display other sections def display_basic_properties(): st.title("Basic: Properties") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) # Default example: 5x5 grid graph if option == "Default Example": G = nx.lollipop_graph(4, 6) display_graph_properties(G) elif option == "Create your own": num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=50, value=5) num_edges = st.number_input("Number of edges per group (for lollipop graph):", min_value=1, max_value=10, value=3) if st.button("Generate"): if num_nodes >= 2 and num_edges >= 1: G_custom = nx.lollipop_graph(num_nodes, num_edges) display_graph_properties(G_custom) def display_graph_properties(G): pathlengths = [] st.write("### Source vertex {target:length, }") for v in G.nodes(): spl = dict(nx.single_source_shortest_path_length(G, v)) st.write(f"Vertex {v}: {spl}") for p in spl: pathlengths.append(spl[p]) avg_path_length = sum(pathlengths) / len(pathlengths) st.write(f"### Average shortest path length: {avg_path_length}") dist = {} for p in pathlengths: if p in dist: dist[p] += 1 else: dist[p] = 1 st.write("### Length #paths") for d in sorted(dist.keys()): st.write(f"Length {d}: {dist[d]} paths") st.write("### Properties") st.write(f"Radius: {nx.radius(G)}") st.write(f"Diameter: {nx.diameter(G)}") st.write(f"Eccentricity: {nx.eccentricity(G)}") st.write(f"Center: {nx.center(G)}") st.write(f"Periphery: {nx.periphery(G)}") st.write(f"Density: {nx.density(G)}") st.write("### Graph Visualization") pos = nx.spring_layout(G, seed=3068) plt.figure(figsize=(8, 6)) nx.draw(G, pos=pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10, font_weight='bold') st.pyplot(plt) # Display other sections def display_read_write_graph(): st.title("Basic: Read and write graphs") G = nx.karate_club_graph() # Write the graph nx.write_gml(G, "karate_club.gml") st.write("Graph written to 'karate_club.gml'.") # Read the graph back G_new = nx.read_gml("karate_club.gml") st.write("Graph read back from 'karate_club.gml'.") nx.draw(G_new, with_labels=True) st.pyplot(plt) def display_simple_graph(): st.title("Basic: Simple graph") G = nx.complete_graph(5) nx.draw(G, with_labels=True) st.pyplot(plt) def display_simple_directed_graph(): st.title("Basic: Simple graph Directed") G = nx.complete_graph(5, nx.DiGraph()) nx.draw(G, with_labels=True) st.pyplot(plt) def display_custom_node_position(): st.title("Drawing: Custom Node Position") # Create a graph with a few nodes and edges G = nx.Graph() G.add_edges_from([("A", "B"), ("B", "C"), ("C", "A")]) # Define custom positions for the nodes pos = {"A": (1, 2), "B": (2, 3), "C": (3, 1)} # Draw the graph with the custom positions nx.draw(G, pos=pos, with_labels=True, node_color='lightblue', node_size=500, font_size=10, font_weight='bold') st.pyplot(plt) # Call the appropriate function based on sidebar selection if sidebar_option == "Basic: Properties": display_basic_properties() elif sidebar_option == "Basic: Read and write graphs": display_read_write_graph() elif sidebar_option == "Basic: Simple graph": display_simple_graph() elif sidebar_option == "Basic: Simple graph Directed": display_simple_directed_graph() elif sidebar_option == "Drawing: Custom Node Position": display_custom_node_position() elif sidebar_option == "Drawing: Chess Masters": display_chess_masters_graph() elif sidebar_option == "Drawing: Cluster Layout": display_cluster_layout() else: st.write("Please select a valid option from the sidebar.")