Mapping the language of team learning
Network visualization of semantic similarity between team learning survey items
import pandas as pd
import networkx as nx
from pyvis.network import Network
from IPython.display import HTML
import matplotlib.colors as mcolors
import matplotlib.cm as cm
import html
import warningswarnings.filterwarnings('ignore')# Load data globally
nodes_df = pd.read_csv('nodes.csv')
links_df = pd.read_csv('links.csv')def display_horizontal_legend(df):
"""
Creates a standalone HTML legend with a two-row horizontal grid layout.
"""
unique_categories = df['Category'].unique()
category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
# CSS Grid: grid-auto-flow: column forces items to fill columns first,
# and grid-template-rows: repeat(2, auto) restricts it to two rows.
legend_html = (
'<div style="padding:15px; background:white; border:1px solid #ddd; '
'font-family:sans-serif; border-radius:8px; width:100%; box-sizing:border-box; '
'text-align:center;">'
'<strong style="font-size:14px; display:block; margin-bottom:12px;">Process Category</strong>'
'<div style="display:grid; grid-auto-flow:column; grid-template-rows:repeat(2, auto); '
'gap:10px 20px; justify-content:center;">'
)
for cat, color in cat_map.items():
legend_html += (
f'<div style="display:flex; align-items:center; white-space:nowrap;">'
f'<span style="display:inline-block; width:12px; height:12px; '
f'background-color:{color}; margin-right:8px; border-radius:50%;"></span>'
f'<span style="font-size:13px;">{cat}</span></div>'
)
legend_html += '</div></div>'
return HTML(legend_html)def create_myst_network(nodes_df, links_df):
"""
Creates the network visualization. Labels are completely hidden using
transparent font colors and zero-size definitions.
"""
G = nx.Graph()
for _, row in nodes_df.iterrows():
G.add_node(row['id'], title=row['Item'], group=row['Category'])
for _, row in links_df.iterrows():
G.add_edge(row['from'], row['to'], weight=float(row['similVal']))
net = Network(
height="600px",
width="100%",
bgcolor="#ffffff",
font_color="rgba(0,0,0,0)", # Global font set to transparent
cdn_resources='remote'
)
degrees = dict(G.degree())
unique_categories = nodes_df['Category'].unique()
category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
for node_id, deg in degrees.items():
node_data = G.nodes[node_id]
net.add_node(
node_id,
label=" ", # Use a space instead of empty string to overwrite defaults
font={'size': 0, 'color': 'rgba(0,0,0,0)'}, # Double-enforcement of invisibility
title=node_data['title'],
size=deg * 4,
color=cat_map[node_data['group']]
)
norm = mcolors.Normalize(vmin=links_df['similVal'].min(), vmax=links_df['similVal'].max())
mapper = cm.ScalarMappable(norm=norm, cmap=cm.Greys)
for u, v, d in G.edges(data=True):
edge_color = mcolors.to_hex(mapper.to_rgba(d['weight']))
net.add_edge(
u, v,
width=1 + d['weight'] * 5,
color=edge_color,
title=f"{d['weight']:.2f}"
)
net.force_atlas_2based()
raw_html = net.generate_html()
escaped_html = html.escape(raw_html)
iframe_output = (
f'<iframe srcdoc="{escaped_html}" width="100%" height="650px" '
f'style="border:none;" title="Network Visualization"></iframe>'
)
return HTML(iframe_output)display_horizontal_legend(nodes_df)Loading...
create_myst_network(nodes_df, links_df)Loading...
#def create_myst_network(nodes_path, links_path):
# """
# Creates an interactive network visualization encapsulated in an iframe
# to prevent CSS conflicts with the global MyST site theme.
# """
# nodes_df = pd.read_csv(nodes_path)
# links_df = pd.read_csv(links_path)
#
# # 1. Build Graph for Analytics
# G = nx.Graph()
# for _, row in nodes_df.iterrows():
# G.add_node(
# row['id'],
# label=str(row['id']),
# title=row['Item'],
# group=row['Category']
# )
#
# for _, row in links_df.iterrows():
# G.add_edge(row['from'], row['to'], weight=float(row['similVal']))
#
# # 2. Configure Visualization
# # We set height/width for the internal container
# net = Network(
# height="600px",
# width="100%",
# bgcolor="#ffffff",
# font_color="black",
# cdn_resources='remote'
# )
#
# # Centrality and Category Mapping
# degrees = dict(G.degree())
# unique_categories = nodes_df['Category'].unique()
# category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
# cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
#
# # Add Nodes
# for node_id, deg in degrees.items():
# node_data = G.nodes[node_id]
# net.add_node(
# node_id,
# label=node_data['label'],
# title=node_data['title'],
# size=deg * 4,
# color=cat_map[node_data['group']]
# )
#
# # Add Edges with Similarity Scaling
# norm = mcolors.Normalize(vmin=links_df['similVal'].min(), vmax=links_df['similVal'].max())
# mapper = cm.ScalarMappable(norm=norm, cmap=cm.Greys)
#
# for u, v, d in G.edges(data=True):
# edge_color = mcolors.to_hex(mapper.to_rgba(d['weight']))
# net.add_edge(u, v, width=1 + d['weight'] * 5, color=edge_color)
#
# # Set Physics
# net.force_atlas_2based()
#
# # 3. Encapsulate Output
# # The 'srcdoc' attribute allows us to embed the HTML string safely.
# # html.escape ensures that internal quotes do not break the iframe tag.
# raw_html = net.generate_html()
# escaped_html = html.escape(raw_html)
#
# iframe_output = (
# f'<iframe srcdoc="{escaped_html}" width="100%" height="650px" '
# f'style="border:none;" title="Network Visualization"></iframe>'
# )
#
# return HTML(iframe_output)#def create_myst_network(nodes_path, links_path):
# nodes_df = pd.read_csv(nodes_path)
# links_df = pd.read_csv(links_path)
#
# G = nx.Graph()
# for _, row in nodes_df.iterrows():
# G.add_node(
# row['id'],
# label=str(row['id']),
# title=row['Item'],
# group=row['Category']
# )
#
# for _, row in links_df.iterrows():
# G.add_edge(row['from'], row['to'], weight=float(row['similVal']))
#
# net = Network(
# height="600px",
# width="100%",
# bgcolor="#ffffff",
# font_color="black",
# cdn_resources='remote'
# )
#
# degrees = dict(G.degree())
# unique_categories = nodes_df['Category'].unique()
# category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
# cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
#
# # Add Nodes
# for node_id, deg in degrees.items():
# node_data = G.nodes[node_id]
# net.add_node(
# node_id,
# label="", # CHANGE: Set to empty string to hide ID text under nodes
# title=node_data['title'],
# size=deg * 4,
# color=cat_map[node_data['group']]
# )
#
# # Add Edges
# norm = mcolors.Normalize(vmin=links_df['similVal'].min(), vmax=links_df['similVal'].max())
# mapper = cm.ScalarMappable(norm=norm, cmap=cm.Greys)
#
# for u, v, d in G.edges(data=True):
# edge_color = mcolors.to_hex(mapper.to_rgba(d['weight']))
# net.add_edge(
# u, v,
# width=1 + d['weight'] * 5,
# color=edge_color,
# title=f"{d['weight']:.2f}" # CHANGE: Round similarity to 2 decimal places for hover
# )
#
# # ADDITION: Create a Legend using dummy nodes in a fixed position
# # We place these in the top-left corner with physics disabled
# x_pos, y_pos = -500, -350
# for i, (cat, color) in enumerate(cat_map.items()):
# net.add_node(
# f"legend_{i}",
# label=cat,
# color=color,
# size=15,
# physics=False,
# x=x_pos,
# y=y_pos + (i * 35),
# fixed=True,
# font={'size': 14, 'align': 'left'}
# )
#
# net.force_atlas_2based()
#
# raw_html = net.generate_html()
# escaped_html = html.escape(raw_html)
#
# iframe_output = (
# f'<iframe srcdoc="{escaped_html}" width="100%" height="650px" '
# f'style="border:none;" title="Network Visualization"></iframe>'
# )
#
# return HTML(iframe_output)#def create_myst_network(nodes_path, links_path):
# nodes_df = pd.read_csv(nodes_path)
# links_df = pd.read_csv(links_path)
#
# G = nx.Graph()
# for _, row in nodes_df.iterrows():
# G.add_node(
# row['id'],
# label=str(row['id']),
# title=row['Item'],
# group=row['Category']
# )
#
# for _, row in links_df.iterrows():
# G.add_edge(row['from'], row['to'], weight=float(row['similVal']))
#
# net = Network(
# height="600px",
# width="100%",
# bgcolor="#ffffff",
# font_color="black",
# cdn_resources='remote'
# )
#
# degrees = dict(G.degree())
# unique_categories = nodes_df['Category'].unique()
# category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
# cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
#
# # Add Nodes
# for node_id, deg in degrees.items():
# node_data = G.nodes[node_id]
# net.add_node(
# node_id,
# label="", # MODIFIED: Hides the ID text under the nodes
# title=node_data['title'],
# size=deg * 4,
# color=cat_map[node_data['group']]
# )
#
# # Add Edges
# norm = mcolors.Normalize(vmin=links_df['similVal'].min(), vmax=links_df['similVal'].max())
# mapper = cm.ScalarMappable(norm=norm, cmap=cm.Greys)
#
# for u, v, d in G.edges(data=True):
# edge_color = mcolors.to_hex(mapper.to_rgba(d['weight']))
# net.add_edge(
# u, v,
# width=1 + d['weight'] * 5,
# color=edge_color,
# title=f"{d['weight']:.2f}" # Kept: Rounded similarity values
# )
#
# net.force_atlas_2based()
#
# # MODIFIED: Create a CSS-based legend instead of using graph nodes
# legend_html = '<div style="position:fixed; top:10px; right:10px; padding:10px; background:white; border:1px solid #ccc; font-family:sans-serif; font-size:12px; z-index:999; border-radius:5px;"><strong>Category Legend</strong><br>'
# for cat, color in cat_map.items():
# legend_html += f'<div style="margin-top:5px;"><span style="display:inline-block; width:12px; height:12px; background-color:{color}; margin-right:8px; border-radius:50%;"></span>{cat}</div>'
# legend_html += '</div>'
#
# raw_html = net.generate_html()
#
# # Inject the legend HTML into the Pyvis output before escaping
# final_html = raw_html.replace('<body>', f'<body>{legend_html}')
# escaped_html = html.escape(final_html)
#
# iframe_output = (
# f'<iframe srcdoc="{escaped_html}" width="100%" height="650px" '
# f'style="border:none;" title="Network Visualization"></iframe>'
# )
#
# return HTML(iframe_output)## Execute
#network_viz = create_myst_network('nodes.csv', 'links.csv')
#network_viz#import pandas as pd
#import networkx as nx
#from pyvis.network import Network
#from IPython.display import HTML
#import matplotlib.colors as mcolors
#import matplotlib.cm as cm
#import html
#
## Load data globally for use in both cells
#nodes_df = pd.read_csv('nodes.csv')
#links_df = pd.read_csv('links.csv')
#
#def display_network_legend(df):
# """
# Creates a standalone HTML legend based on the Category column.
# """
# unique_categories = df['Category'].unique()
# category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
# cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
#
# legend_html = (
# '<div style="padding:15px; background:white; border:1px solid #ddd; '
# 'font-family:sans-serif; border-radius:8px; max-width:300px;">'
# '<strong style="font-size:14px; display:block; margin-bottom:10px;">Survey Categories</strong>'
# )
#
# for cat, color in cat_map.items():
# legend_html += (
# f'<div style="margin-bottom:6px; display:flex; align-items:center;">'
# f'<span style="display:inline-block; width:14px; height:14px; '
# f'background-color:{color}; margin-right:10px; border-radius:50%;"></span>'
# f'<span style="font-size:13px;">{cat}</span></div>'
# )
# legend_html += '</div>'
#
# return HTML(legend_html)
#
## Display the legend in this cell
#display_network_legend(nodes_df)#def create_myst_network(nodes_df, links_df):
# """
# Creates the network visualization with suppressed labels and iframe isolation.
# """
# G = nx.Graph()
# for _, row in nodes_df.iterrows():
# G.add_node(row['id'], title=row['Item'], group=row['Category'])
#
# for _, row in links_df.iterrows():
# G.add_edge(row['from'], row['to'], weight=float(row['similVal']))
#
# net = Network(
# height="600px",
# width="100%",
# bgcolor="#ffffff",
# font_color="black",
# cdn_resources='remote'
# )
#
# degrees = dict(G.degree())
# unique_categories = nodes_df['Category'].unique()
# category_colors = ['#4E79A7', '#F28E2B', '#E15759', '#76B7B2', '#59A14F', '#EDC948', '#B07AA1']
# cat_map = {cat: category_colors[i % len(category_colors)] for i, cat in enumerate(unique_categories)}
#
# # Add Nodes
# for node_id, deg in degrees.items():
# node_data = G.nodes[node_id]
# net.add_node(
# node_id,
# label="", # Keeps the label empty
# font={'size': 0}, # NEW: Force font size to 0 to ensure ID is hidden during zoom
# title=node_data['title'],
# size=deg * 4,
# color=cat_map[node_data['group']]
# )
#
# # Add Edges
# norm = mcolors.Normalize(vmin=links_df['similVal'].min(), vmax=links_df['similVal'].max())
# mapper = cm.ScalarMappable(norm=norm, cmap=cm.Greys)
#
# for u, v, d in G.edges(data=True):
# edge_color = mcolors.to_hex(mapper.to_rgba(d['weight']))
# net.add_edge(
# u, v,
# width=1 + d['weight'] * 5,
# color=edge_color,
# title=f"{d['weight']:.2f}"
# )
#
# net.force_atlas_2based()
#
# raw_html = net.generate_html()
# escaped_html = html.escape(raw_html)
#
# iframe_output = (
# f'<iframe srcdoc="{escaped_html}" width="100%" height="650px" '
# f'style="border:none;" title="Network Visualization"></iframe>'
# )
#
# return HTML(iframe_output)
#
## Display the network visualization in this cell
#create_myst_network(nodes_df, links_df)