import streamlit as st import itertools as it import matplotlib.pyplot as plt import networkx as nx import numpy as np from operator import itemgetter # 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: Cluster Layout", "Drawing: Degree Analysis", "Drawing: Ego Graph", "Drawing: Eigenvalues", "Drawing: Four Grids", "Drawing: House With Colors", "Drawing: Labels And Colors", "Drawing: Multipartite Layout", "Drawing: Node Colormap", "Drawing: Rainbow Coloring", "Drawing: Random Geometric Graph","Drawing: Self-loops"]) # Helper function to draw and display graph def draw_graph(G, pos=None, title="Graph Visualization"): 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) # Function to display Drawing: Self-loops def display_self_loops(): st.title("Drawing: Self-loops") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Default example of a graph with self-loops G = nx.complete_graph(3, create_using=nx.DiGraph) G.add_edge(0, 0) # Add a self-loop to node 0 pos = nx.circular_layout(G) # Draw the graph nx.draw(G, pos, with_labels=True) # Add self-loops to the remaining nodes edgelist = [(1, 1), (2, 2)] G.add_edges_from(edgelist) # Draw the newly added self-loops with different formatting nx.draw_networkx_edges(G, pos, edgelist=edgelist, arrowstyle="<|-", style="dashed") st.pyplot(plt) elif option == "Create your own": # User can create their own graph with self-loops num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=20, value=3) add_self_loops = st.checkbox("Add self-loops to all nodes?", value=True) if st.button("Generate Graph"): # Generate a complete graph G = nx.complete_graph(num_nodes, create_using=nx.DiGraph) # Optionally add self-loops to all nodes if add_self_loops: for node in G.nodes(): G.add_edge(node, node) pos = nx.circular_layout(G) # Draw the graph with self-loops nx.draw(G, pos, with_labels=True) # Style self-loops differently edgelist = [(node, node) for node in G.nodes()] nx.draw_networkx_edges(G, pos, edgelist=edgelist, arrowstyle="<|-", style="dashed") st.pyplot(plt) # Display Drawing: Self-loops if selected if sidebar_option == "Drawing: Self-loops": display_self_loops() # Function to display Drawing: Random Geometric Graph def display_random_geometric_graph(): st.title("Drawing: Random Geometric Graph") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Default random geometric graph example G = nx.random_geometric_graph(200, 0.125, seed=896803) pos = nx.get_node_attributes(G, "pos") # Find node near the center (0.5, 0.5) dmin = 1 ncenter = 0 for n in pos: x, y = pos[n] d = (x - 0.5) ** 2 + (y - 0.5) ** 2 if d < dmin: ncenter = n dmin = d # Color by path length from node near center p = dict(nx.single_source_shortest_path_length(G, ncenter)) plt.figure(figsize=(8, 8)) nx.draw_networkx_edges(G, pos, alpha=0.4) nx.draw_networkx_nodes( G, pos, nodelist=list(p.keys()), node_size=80, node_color=list(p.values()), cmap=plt.cm.Reds_r, ) plt.xlim(-0.05, 1.05) plt.ylim(-0.05, 1.05) plt.axis("off") st.pyplot(plt) elif option == "Create your own": # User can create their own random geometric graph num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=500, value=200) distance = st.slider("Edge distance threshold (between 0 and 1):", 0.01, 1.0, 0.125) if st.button("Generate Graph"): # Generate the graph with user input G = nx.random_geometric_graph(num_nodes, distance, seed=896803) pos = nx.get_node_attributes(G, "pos") # Find node near the center (0.5, 0.5) dmin = 1 ncenter = 0 for n in pos: x, y = pos[n] d = (x - 0.5) ** 2 + (y - 0.5) ** 2 if d < dmin: ncenter = n dmin = d # Color by path length from node near center p = dict(nx.single_source_shortest_path_length(G, ncenter)) plt.figure(figsize=(8, 8)) nx.draw_networkx_edges(G, pos, alpha=0.4) nx.draw_networkx_nodes( G, pos, nodelist=list(p.keys()), node_size=80, node_color=list(p.values()), cmap=plt.cm.Reds_r, ) plt.xlim(-0.05, 1.05) plt.ylim(-0.05, 1.05) plt.axis("off") st.pyplot(plt) # Display Drawing: Random Geometric Graph if selected if sidebar_option == "Drawing: Random Geometric Graph": display_random_geometric_graph() # Function to display Drawing: Rainbow Coloring def display_rainbow_coloring(): st.title("Drawing: Rainbow Coloring") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Rainbow Coloring with default parameters node_dist_to_color = { 1: "tab:red", 2: "tab:orange", 3: "tab:olive", 4: "tab:green", 5: "tab:blue", 6: "tab:purple", } nnodes = 13 G = nx.complete_graph(nnodes) n = (nnodes - 1) // 2 ndist_iter = list(range(1, n + 1)) ndist_iter += ndist_iter[::-1] def cycle(nlist, n): return nlist[-n:] + nlist[:-n] nodes = list(G.nodes()) for i, nd in enumerate(ndist_iter): for u, v in zip(nodes, cycle(nodes, i + 1)): G[u][v]["color"] = node_dist_to_color[nd] pos = nx.circular_layout(G) # Create a figure with 1:1 aspect ratio to preserve the circle. fig, ax = plt.subplots(figsize=(8, 8)) node_opts = {"node_size": 500, "node_color": "w", "edgecolors": "k", "linewidths": 2.0} nx.draw_networkx_nodes(G, pos, **node_opts) nx.draw_networkx_labels(G, pos, font_size=14) # Extract color from edge data edge_colors = [edgedata["color"] for _, _, edgedata in G.edges(data=True)] nx.draw_networkx_edges(G, pos, width=2.0, edge_color=edge_colors) ax.set_axis_off() fig.tight_layout() st.pyplot(plt) elif option == "Create your own": nnodes = st.number_input("Number of nodes (max=14):", min_value=2, max_value=50, value=13) # Allow users to create their own color map red = st.color_picker("Select a color for Red (1)", "#ff0000") orange = st.color_picker("Select a color for Orange (2)", "#ff7f00") olive = st.color_picker("Select a color for Olive (3)", "#808000") green = st.color_picker("Select a color for Green (4)", "#008000") blue = st.color_picker("Select a color for Blue (5)", "#0000ff") purple = st.color_picker("Select a color for Purple (6)", "#800080") node_dist_to_color = { 1: red, 2: orange, 3: olive, 4: green, 5: blue, 6: purple, } if st.button("Generate Graph"): G = nx.complete_graph(nnodes) n = (nnodes - 1) // 2 ndist_iter = list(range(1, n + 1)) ndist_iter += ndist_iter[::-1] def cycle(nlist, n): return nlist[-n:] + nlist[:-n] nodes = list(G.nodes()) for i, nd in enumerate(ndist_iter): for u, v in zip(nodes, cycle(nodes, i + 1)): G[u][v]["color"] = node_dist_to_color[nd] pos = nx.circular_layout(G) # Create a figure with 1:1 aspect ratio to preserve the circle. fig, ax = plt.subplots(figsize=(8, 8)) node_opts = {"node_size": 500, "node_color": "w", "edgecolors": "k", "linewidths": 2.0} nx.draw_networkx_nodes(G, pos, **node_opts) nx.draw_networkx_labels(G, pos, font_size=14) # Extract color from edge data edge_colors = [edgedata["color"] for _, _, edgedata in G.edges(data=True)] nx.draw_networkx_edges(G, pos, width=2.0, edge_color=edge_colors) ax.set_axis_off() fig.tight_layout() st.pyplot(plt) # Display Drawing: Rainbow Coloring if selected if sidebar_option == "Drawing: Rainbow Coloring": display_rainbow_coloring() # Function to display Drawing: Node Colormap def display_node_colormap(): st.title("Drawing: Node Colormap") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": G = nx.cycle_graph(24) pos = nx.circular_layout(G) nx.draw(G, pos, node_color=range(24), node_size=800, cmap=plt.cm.Blues) st.pyplot(plt) elif option == "Create your own": num_nodes = st.number_input("Number of nodes:", min_value=2, max_value=100, value=24) color_map = st.selectbox("Select a colormap:", plt.colormaps(), index=plt.colormaps().index('Blues')) if st.button("Generate Graph"): # Create cycle graph with custom number of nodes G_custom = nx.cycle_graph(num_nodes) pos = nx.circular_layout(G_custom) nx.draw(G_custom, pos, node_color=range(num_nodes), node_size=800, cmap=plt.get_cmap(color_map)) st.pyplot(plt) # Display Drawing: Node Colormap if selected if sidebar_option == "Drawing: Node Colormap": display_node_colormap() # Function to create a multipartite graph def multilayered_graph(*subset_sizes): G = nx.Graph() layers = len(subset_sizes) node_id = 0 # Create nodes for each subset and add edges between nodes in adjacent layers for i, size in enumerate(subset_sizes): for j in range(size): G.add_node(node_id, layer=i) # Assign a layer attribute node_id += 1 # Add edges between nodes in adjacent layers node_ids = list(G.nodes()) for i in range(layers - 1): layer_nodes = [node for node in node_ids if G.nodes[node]["layer"] == i] next_layer_nodes = [node for node in node_ids if G.nodes[node]["layer"] == i + 1] for node in layer_nodes: for next_node in next_layer_nodes: G.add_edge(node, next_node) return G # Function to display Multipartite Layout def display_multipartite_layout(): st.title("Drawing: Multipartite Layout") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": subset_sizes = [5, 5, 4, 3, 2, 4, 4, 3] subset_color = [ "gold", "violet", "violet", "violet", "violet", "limegreen", "limegreen", "darkorange" ] # Generate and plot multipartite graph G = multilayered_graph(*subset_sizes) color = [subset_color[data["layer"]] for v, data in G.nodes(data=True)] pos = nx.multipartite_layout(G, subset_key="layer") plt.figure(figsize=(8, 8)) nx.draw(G, pos, node_color=color, with_labels=False) plt.axis("equal") st.pyplot(plt) elif option == "Create your own": # Let the user input the subset sizes and colors st.write("Enter the subset sizes and colors to create your own multipartite graph.") subset_sizes_input = st.text_area("Enter subset sizes (comma-separated, e.g., 5,5,4,3):", value="5,5,4,3,2,4,4,3") subset_sizes = list(map(int, subset_sizes_input.split(','))) subset_colors_input = st.text_area("Enter subset colors (comma-separated, e.g., gold,violet,green):", value="gold,violet,violet,violet,violet,limegreen,limegreen,darkorange") subset_colors = subset_colors_input.split(',') # Check if the number of colors matches the number of subsets if len(subset_sizes) != len(subset_colors): st.error("The number of colors should match the number of subsets.") else: # Add a button to generate the graph if st.button("Generate Graph"): # Generate and plot multipartite graph G = multilayered_graph(*subset_sizes) color = [subset_colors[data["layer"]] for v, data in G.nodes(data=True)] pos = nx.multipartite_layout(G, subset_key="layer") plt.figure(figsize=(8, 8)) nx.draw(G, pos, node_color=color, with_labels=False) plt.axis("equal") st.pyplot(plt) # Display Drawing: Multipartite Layout if selected if sidebar_option == "Drawing: Multipartite Layout": display_multipartite_layout() # Function to display Labels and Colors def display_labels_and_colors(): st.title("Drawing: Labels And Colors") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Create a cubical graph G = nx.cubical_graph() pos = nx.spring_layout(G, seed=3113794652) # positions for all nodes # Draw nodes with different colors options = {"edgecolors": "tab:gray", "node_size": 800, "alpha": 0.9} nx.draw_networkx_nodes(G, pos, nodelist=[0, 1, 2, 3], node_color="tab:red", **options) nx.draw_networkx_nodes(G, pos, nodelist=[4, 5, 6, 7], node_color="tab:blue", **options) # Draw edges nx.draw_networkx_edges(G, pos, width=1.0, alpha=0.5) nx.draw_networkx_edges( G, pos, edgelist=[(0, 1), (1, 2), (2, 3), (3, 0)], width=8, alpha=0.5, edge_color="tab:red", ) nx.draw_networkx_edges( G, pos, edgelist=[(4, 5), (5, 6), (6, 7), (7, 4)], width=8, alpha=0.5, edge_color="tab:blue", ) # Add labels for nodes labels = {0: r"$a$", 1: r"$b$", 2: r"$c$", 3: r"$d$", 4: r"$\alpha$", 5: r"$\beta$", 6: r"$\gamma$", 7: r"$\delta$"} nx.draw_networkx_labels(G, pos, labels, font_size=22, font_color="whitesmoke") plt.tight_layout() plt.axis("off") st.pyplot(plt) elif option == "Create your own": # Let the user input the nodes and edges of the graph st.write("Enter the nodes and edges to create your own labeled graph.") nodes = st.text_area("Enter node labels (comma-separated, e.g., a,b,c,d):", value="a,b,c,d") node_labels = nodes.split(',') edges = st.text_area("Enter edges (format: node1-node2, comma-separated, e.g., a-b,b-c):", value="a-b,b-c,c-d") edge_list = [tuple(edge.split('-')) for edge in edges.split(',')] # Let user choose colors for nodes and edges node_color = st.color_picker("Pick a color for nodes:", "#FF6347") edge_color = st.color_picker("Pick a color for edges:", "#4682B4") # Add a button to generate the graph if st.button("Generate Graph"): # Generate graph based on user input G_custom = nx.Graph() G_custom.add_nodes_from(node_labels) G_custom.add_edges_from(edge_list) # Generate layout for the nodes pos_custom = nx.spring_layout(G_custom) # Draw the graph nx.draw_networkx_nodes(G_custom, pos_custom, node_color=node_color, node_size=800, edgecolors="gray", alpha=0.9) nx.draw_networkx_edges(G_custom, pos_custom, edge_color=edge_color, width=2, alpha=0.7) # Create custom labels custom_labels = {node: f"${node}$" for node in node_labels} nx.draw_networkx_labels(G_custom, pos_custom, labels=custom_labels, font_size=22, font_color="whitesmoke") plt.tight_layout() plt.axis("off") st.pyplot(plt) # Display Drawing: Labels And Colors if selected if sidebar_option == "Drawing: Labels And Colors": display_labels_and_colors() # Function to display Drawing: House With Colors def display_house_with_colors(): st.title("Drawing: House With Colors") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Create the house graph and explicitly set positions G = nx.house_graph() pos = {0: (0, 0), 1: (1, 0), 2: (0, 1), 3: (1, 1), 4: (0.5, 2.0)} # Plot nodes with different properties for the "wall" and "roof" nodes nx.draw_networkx_nodes(G, pos, node_size=3000, nodelist=[0, 1, 2, 3], node_color="tab:blue") nx.draw_networkx_nodes(G, pos, node_size=2000, nodelist=[4], node_color="tab:orange") nx.draw_networkx_edges(G, pos, alpha=0.5, width=6) # Customize axes ax = plt.gca() ax.margins(0.11) plt.tight_layout() plt.axis("off") st.pyplot(plt) elif option == "Create your own": # Allow the user to specify node positions and colors st.write("Specify positions for the house graph nodes.") positions = {} for i in range(5): x = st.number_input(f"X-coordinate for node {i}:", min_value=-10.0, max_value=10.0, value=0.0, step=0.1) y = st.number_input(f"Y-coordinate for node {i}:", min_value=-10.0, max_value=10.0, value=0.0, step=0.1) positions[i] = (x, y) # Allow the user to specify colors for wall and roof nodes wall_color = st.color_picker("Wall color:", "#0000FF") roof_color = st.color_picker("Roof color:", "#FFA500") if st.button("Generate"): # Create the house graph with the specified positions G_custom = nx.house_graph() # Plot nodes with user-defined properties for wall and roof nodes nx.draw_networkx_nodes(G_custom, positions, node_size=3000, nodelist=[0, 1, 2, 3], node_color=wall_color) nx.draw_networkx_nodes(G_custom, positions, node_size=2000, nodelist=[4], node_color=roof_color) nx.draw_networkx_edges(G_custom, positions, alpha=0.5, width=6) # Customize axes ax = plt.gca() ax.margins(0.11) plt.tight_layout() plt.axis("off") st.pyplot(plt) # Display Drawing: House With Colors if selected if sidebar_option == "Drawing: House With Colors": display_house_with_colors() # Function to display Four Grids visualization for Drawing: Four Grids def display_four_grids(): st.title("Drawing: Four Grids") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Generate a 4x4 grid graph G = nx.grid_2d_graph(4, 4) # 4x4 grid pos = nx.spring_layout(G, iterations=100, seed=39775) # Create a 2x2 subplot fig, all_axes = plt.subplots(2, 2) ax = all_axes.flat # Draw graphs in 4 different styles nx.draw(G, pos, ax=ax[0], font_size=8) nx.draw(G, pos, ax=ax[1], node_size=0, with_labels=False) nx.draw( G, pos, ax=ax[2], node_color="tab:green", edgecolors="tab:gray", # Node surface color edge_color="tab:gray", # Color of graph edges node_size=250, with_labels=False, width=6, ) H = G.to_directed() nx.draw( H, pos, ax=ax[3], node_color="tab:orange", node_size=20, with_labels=False, arrowsize=10, width=2, ) # Set margins for the axes so that nodes aren't clipped for a in ax: a.margins(0.10) fig.tight_layout() st.pyplot(fig) elif option == "Create your own": # Allow the user to customize the grid dimensions rows = st.number_input("Number of rows:", min_value=2, max_value=20, value=4) cols = st.number_input("Number of columns:", min_value=2, max_value=20, value=4) if st.button("Generate"): # Generate a custom grid graph G_custom = nx.grid_2d_graph(rows, cols) # Create the grid graph pos = nx.spring_layout(G_custom, iterations=100, seed=39775) # Create a 2x2 subplot fig, all_axes = plt.subplots(2, 2) ax = all_axes.flat # Draw graphs in 4 different styles nx.draw(G_custom, pos, ax=ax[0], font_size=8) nx.draw(G_custom, pos, ax=ax[1], node_size=0, with_labels=False) nx.draw( G_custom, pos, ax=ax[2], node_color="tab:green", edgecolors="tab:gray", # Node surface color edge_color="tab:gray", # Color of graph edges node_size=250, with_labels=False, width=6, ) H = G_custom.to_directed() nx.draw( H, pos, ax=ax[3], node_color="tab:orange", node_size=20, with_labels=False, arrowsize=10, width=2, ) # Set margins for the axes so that nodes aren't clipped for a in ax: a.margins(0.10) fig.tight_layout() st.pyplot(fig) # Display Drawing: Four Grids if selected if sidebar_option == "Drawing: Four Grids": display_four_grids() # Function to display Eigenvalue analysis for Drawing: Eigenvalues def display_eigenvalue_analysis(): st.title("Drawing: Eigenvalues") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Generate random graph with 1000 nodes and 5000 edges n = 1000 m = 5000 G = nx.gnm_random_graph(n, m, seed=5040) # Seed for reproducibility # Calculate the normalized Laplacian matrix L = nx.normalized_laplacian_matrix(G) eigenvalues = np.linalg.eigvals(L.toarray()) # Print largest and smallest eigenvalues st.write(f"Largest eigenvalue: {max(eigenvalues)}") st.write(f"Smallest eigenvalue: {min(eigenvalues)}") # Display the histogram of eigenvalues st.write("### Eigenvalue Histogram") plt.hist(eigenvalues, bins=100) plt.xlim(0, 2) # Eigenvalues between 0 and 2 st.pyplot(plt) elif option == "Create your own": # Allow the user to customize the number of nodes and edges n_nodes = st.number_input("Number of nodes:", min_value=2, max_value=1000, value=100) m_edges = st.number_input("Number of edges:", min_value=1, max_value=n_nodes*(n_nodes-1)//2, value=500) if st.button("Generate"): # Generate a random graph with the custom number of nodes and edges G_custom = nx.gnm_random_graph(n_nodes, m_edges, seed=5040) # Seed for reproducibility # Calculate the normalized Laplacian matrix L = nx.normalized_laplacian_matrix(G_custom) eigenvalues = np.linalg.eigvals(L.toarray()) # Print largest and smallest eigenvalues st.write(f"Largest eigenvalue: {max(eigenvalues)}") st.write(f"Smallest eigenvalue: {min(eigenvalues)}") # Display the histogram of eigenvalues st.write("### Eigenvalue Histogram") plt.hist(eigenvalues, bins=100) plt.xlim(0, 2) # Eigenvalues between 0 and 2 st.pyplot(plt) # Display Drawing: Eigenvalues if selected if sidebar_option == "Drawing: Eigenvalues": display_eigenvalue_analysis() # Function to display properties and graph for Basic: Properties 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: dist[p] = dist.get(p, 0) + 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)}") # Visualize the graph st.write("### Graph Visualization") pos = nx.spring_layout(G, seed=3068) # Seed layout for reproducibility draw_graph(G, pos) # Function to display graph for Basic: Read and write graphs def display_read_write_graph(G): st.write("### Adjacency List:") for line in nx.generate_adjlist(G): st.write(line) # Write the graph's edge list to a file st.write("### Writing Edge List to 'grid.edgelist' file:") nx.write_edgelist(G, path="grid.edgelist", delimiter=":") # Save edge list st.write("Edge list written to 'grid.edgelist'") # Read the graph from the edge list st.write("### Reading Edge List from 'grid.edgelist' file:") H = nx.read_edgelist(path="grid.edgelist", delimiter=":") st.write("Edge list read into graph H") # Visualize the graph st.write("### Graph Visualization:") pos = nx.spring_layout(H, seed=200) # Seed for reproducibility draw_graph(H, pos) # Function to display Simple Graphs for Basic: Simple graph def display_simple_graph(G, pos=None): options = { "font_size": 36, "node_size": 3000, "node_color": "white", "edgecolors": "black", "linewidths": 5, "width": 5, } # Draw the network nx.draw_networkx(G, pos, **options) # Set margins for the axes so that nodes aren't clipped ax = plt.gca() ax.margins(0.20) plt.axis("off") st.pyplot(plt) # Function to display Simple Directed Graphs for Basic: Simple graph Directed def display_simple_directed_graph(G, pos=None): options = { "node_size": 500, "node_color": "lightblue", "arrowsize": 20, "width": 2, "edge_color": "gray", } # Draw the directed graph with the given positions and options nx.draw_networkx(G, pos, **options) # Set margins for the axes so that nodes aren't clipped ax = plt.gca() ax.margins(0.20) plt.axis("off") st.pyplot(plt) # Function to display Custom Node Position Graphs for Drawing: Custom Node Position def display_custom_node_position(): st.title("Drawing: Custom Node Position") # Default example graph (path graph with custom node position) G = nx.path_graph(20) center_node = 5 edge_nodes = set(G) - {center_node} # Ensure the nodes around the circle are evenly distributed pos = nx.circular_layout(G.subgraph(edge_nodes)) pos[center_node] = np.array([0, 0]) # Manually specify node position # Draw the graph draw_graph(G, pos) # Function to display Cluster Layout for Drawing: Cluster Layout def display_cluster_layout(): st.title("Drawing: Cluster Layout") G = nx.davis_southern_women_graph() # Example graph communities = nx.community.greedy_modularity_communities(G) # Compute positions for the node clusters as if they were themselves nodes in a supergraph using a larger scale factor 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) # Function to display Degree Analysis for Drawing: Degree Analysis def display_degree_analysis(): st.title("Drawing: Degree Analysis") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": G = nx.gnp_random_graph(100, 0.02, seed=10374196) degree_sequence = sorted((d for n, d in G.degree()), reverse=True) dmax = max(degree_sequence) fig = plt.figure("Degree of a random graph", figsize=(8, 8)) # Create a gridspec for adding subplots of different sizes axgrid = fig.add_gridspec(5, 4) ax0 = fig.add_subplot(axgrid[0:3, :]) Gcc = G.subgraph(sorted(nx.connected_components(G), key=len, reverse=True)[0]) pos = nx.spring_layout(Gcc, seed=10396953) nx.draw_networkx_nodes(Gcc, pos, ax=ax0, node_size=20) nx.draw_networkx_edges(Gcc, pos, ax=ax0, alpha=0.4) ax0.set_title("Connected components of G") ax0.set_axis_off() ax1 = fig.add_subplot(axgrid[3:, :2]) ax1.plot(degree_sequence, "b-", marker="o") ax1.set_title("Degree Rank Plot") ax1.set_ylabel("Degree") ax1.set_xlabel("Rank") ax2 = fig.add_subplot(axgrid[3:, 2:]) ax2.bar(*np.unique(degree_sequence, return_counts=True)) ax2.set_title("Degree histogram") ax2.set_xlabel("Degree") ax2.set_ylabel("# of Nodes") fig.tight_layout() st.pyplot(fig) elif option == "Create your own": n_nodes = st.number_input("Number of nodes:", min_value=2, max_value=500, value=100) p_edge = st.slider("Edge probability:", min_value=0.0, max_value=1.0, value=0.02) if st.button("Generate"): if n_nodes >= 2: G_custom = nx.gnp_random_graph(n_nodes, p_edge, seed=10374196) degree_sequence = sorted((d for n, d in G_custom.degree()), reverse=True) dmax = max(degree_sequence) fig = plt.figure("Degree of a random graph", figsize=(8, 8)) # Create a gridspec for adding subplots of different sizes axgrid = fig.add_gridspec(5, 4) ax0 = fig.add_subplot(axgrid[0:3, :]) Gcc = G_custom.subgraph(sorted(nx.connected_components(G_custom), key=len, reverse=True)[0]) pos = nx.spring_layout(Gcc, seed=10396953) nx.draw_networkx_nodes(Gcc, pos, ax=ax0, node_size=20) nx.draw_networkx_edges(Gcc, pos, ax=ax0, alpha=0.4) ax0.set_title("Connected components of G") ax0.set_axis_off() ax1 = fig.add_subplot(axgrid[3:, :2]) ax1.plot(degree_sequence, "b-", marker="o") ax1.set_title("Degree Rank Plot") ax1.set_ylabel("Degree") ax1.set_xlabel("Rank") ax2 = fig.add_subplot(axgrid[3:, 2:]) ax2.bar(*np.unique(degree_sequence, return_counts=True)) ax2.set_title("Degree histogram") ax2.set_xlabel("Degree") ax2.set_ylabel("# of Nodes") fig.tight_layout() st.pyplot(fig) # Function to display Ego Graph for Drawing: Ego Graph def display_ego_graph(): st.title("Drawing: Ego Graph") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": # Create a BA model graph - use seed for reproducibility n = 1000 m = 2 seed = 20532 G = nx.barabasi_albert_graph(n, m, seed=seed) # Find node with largest degree node_and_degree = G.degree() (largest_hub, degree) = sorted(node_and_degree, key=itemgetter(1))[-1] # Create ego graph of main hub hub_ego = nx.ego_graph(G, largest_hub) # Draw graph pos = nx.spring_layout(hub_ego, seed=seed) # Seed layout for reproducibility nx.draw(hub_ego, pos, node_color="b", node_size=50, with_labels=False) # Draw ego as large and red options = {"node_size": 300, "node_color": "r"} nx.draw_networkx_nodes(hub_ego, pos, nodelist=[largest_hub], **options) plt.tight_layout() st.pyplot(plt) elif option == "Create your own": n_nodes = st.number_input("Number of nodes:", min_value=2, max_value=1000, value=100) m_edges = st.number_input("Edges per node:", min_value=1, max_value=10, value=2) if st.button("Generate"): if n_nodes >= 2: G_custom = nx.barabasi_albert_graph(n_nodes, m_edges, seed=20532) # Find node with largest degree node_and_degree = G_custom.degree() (largest_hub, degree) = sorted(node_and_degree, key=itemgetter(1))[-1] # Create ego graph of main hub hub_ego = nx.ego_graph(G_custom, largest_hub) # Draw graph pos = nx.spring_layout(hub_ego, seed=20532) # Seed layout for reproducibility nx.draw(hub_ego, pos, node_color="b", node_size=50, with_labels=False) # Draw ego as large and red options = {"node_size": 300, "node_color": "r"} nx.draw_networkx_nodes(hub_ego, pos, nodelist=[largest_hub], **options) plt.tight_layout() st.pyplot(plt) # Display Drawing: Ego Graph if selected if sidebar_option == "Drawing: Ego Graph": display_ego_graph() # Display Basic: Properties if selected elif sidebar_option == "Basic: Properties": st.title("Basic: Properties") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) 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) # Display Basic: Read and write graphs if selected elif sidebar_option == "Basic: Read and write graphs": st.title("Basic: Read and write graphs") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": G = nx.grid_2d_graph(5, 5) display_read_write_graph(G) elif option == "Create your own": rows = st.number_input("Number of rows:", min_value=2, max_value=20, value=5) cols = st.number_input("Number of columns:", min_value=2, max_value=20, value=5) if st.button("Generate"): if rows >= 2 and cols >= 2: G_custom = nx.grid_2d_graph(rows, cols) display_read_write_graph(G_custom) # Display Basic: Simple Graph if selected elif sidebar_option == "Basic: Simple graph": st.title("Basic: Simple graph") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": G = nx.Graph() G.add_edge(1, 2) G.add_edge(1, 3) G.add_edge(1, 5) G.add_edge(2, 3) G.add_edge(3, 4) G.add_edge(4, 5) pos = {1: (0, 0), 2: (-1, 0.3), 3: (2, 0.17), 4: (4, 0.255), 5: (5, 0.03)} display_simple_graph(G, pos) elif option == "Create your own": edges = [] edge_input = st.text_area("Edges:", value="1,2\n1,3\n2,3") if edge_input: edge_list = edge_input.split("\n") for edge in edge_list: u, v = map(int, edge.split(",")) edges.append((u, v)) if st.button("Generate"): G_custom = nx.Graph() G_custom.add_edges_from(edges) pos = nx.spring_layout(G_custom, seed=42) display_simple_graph(G_custom, pos) # Display Basic: Simple Directed Graph if selected elif sidebar_option == "Basic: Simple graph Directed": st.title("Basic: Simple graph Directed") option = st.radio("Choose a graph type:", ("Default Example", "Create your own")) if option == "Default Example": G = nx.DiGraph([(0, 3), (1, 3), (2, 4), (3, 5), (3, 6), (4, 6), (5, 6)]) left_nodes = [0, 1, 2] middle_nodes = [3, 4] right_nodes = [5, 6] pos = {n: (0, i) for i, n in enumerate(left_nodes)} pos.update({n: (1, i + 0.5) for i, n in enumerate(middle_nodes)}) pos.update({n: (2, i + 0.5) for i, n in enumerate(right_nodes)}) display_simple_directed_graph(G, pos) elif option == "Create your own": edges = [] edge_input = st.text_area("Edges:", value="1,2\n1,3\n2,3") if edge_input: edge_list = edge_input.split("\n") for edge in edge_list: u, v = map(int, edge.split(",")) edges.append((u, v)) if st.button("Generate"): G_custom = nx.DiGraph() G_custom.add_edges_from(edges) pos = nx.spring_layout(G_custom, seed=42) display_simple_directed_graph(G_custom, pos) # Display Drawing: Custom Node Position if selected elif sidebar_option == "Drawing: Custom Node Position": display_custom_node_position() # Display Drawing: Cluster Layout if selected elif sidebar_option == "Drawing: Cluster Layout": display_cluster_layout() # Display Drawing: Degree Analysis if selected elif sidebar_option == "Drawing: Degree Analysis": display_degree_analysis()