Spaces:
Sleeping
Fix population network distribution and add opinion-based node coloring
Browse filesFixes:
1. Population Network: Fixed persona distribution to use all selected personas
- Added check to skip personas with zero weight
- Now correctly distributes 30 nodes across 4 selected personas
- Previously only used 1 persona and created 8 nodes
2. Network Visualization: Added opinion-based node coloring
- Nodes now colored by final opinion position:
* Dark green = Strongly Support
* Green = Support
* Light green = Lean Support
* Gray = Neutral
* Light red = Lean Oppose
* Red = Oppose
* Dark red = Strongly Oppose
- Added opinion position to hover text
- Removed centrality-based coloring
3. Note: Sidebar still shows 'Web App' - pages.toml not fully supported yet
Files modified:
- src/influence/population_network.py: Fixed weight-based distribution
- pages/3_🌐_Opinion_Equilibria.py: Added opinion color scheme
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
|
@@ -566,22 +566,44 @@ if st.session_state.equilibrium_results:
|
|
| 566 |
)
|
| 567 |
)
|
| 568 |
|
| 569 |
-
# Node trace
|
| 570 |
node_x = []
|
| 571 |
node_y = []
|
| 572 |
node_text = []
|
| 573 |
node_color = []
|
| 574 |
|
| 575 |
-
|
| 576 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 577 |
|
| 578 |
for node in G.nodes():
|
| 579 |
x, y = pos[node]
|
| 580 |
node_x.append(x)
|
| 581 |
node_y.append(y)
|
| 582 |
node_data = G.nodes[node]
|
| 583 |
-
|
| 584 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
|
| 586 |
node_trace = go.Scatter(
|
| 587 |
x=node_x,
|
|
@@ -593,10 +615,7 @@ if st.session_state.equilibrium_results:
|
|
| 593 |
marker=dict(
|
| 594 |
size=20,
|
| 595 |
color=node_color,
|
| 596 |
-
|
| 597 |
-
showscale=True,
|
| 598 |
-
colorbar=dict(title="Influence<br>Centrality"),
|
| 599 |
-
line=dict(width=2, color="white"),
|
| 600 |
),
|
| 601 |
showlegend=False,
|
| 602 |
)
|
|
|
|
| 566 |
)
|
| 567 |
)
|
| 568 |
|
| 569 |
+
# Node trace - color by opinion position
|
| 570 |
node_x = []
|
| 571 |
node_y = []
|
| 572 |
node_text = []
|
| 573 |
node_color = []
|
| 574 |
|
| 575 |
+
# Map persona_id to final opinion
|
| 576 |
+
opinion_map = {op.persona_id: op for op in equilibrium.final_opinions}
|
| 577 |
+
|
| 578 |
+
# Define color scheme for opinions
|
| 579 |
+
opinion_colors = {
|
| 580 |
+
"strongly_support": "#2E7D32", # Dark green
|
| 581 |
+
"support": "#66BB6A", # Green
|
| 582 |
+
"lean_support": "#A5D6A7", # Light green
|
| 583 |
+
"neutral": "#BDBDBD", # Gray
|
| 584 |
+
"lean_oppose": "#EF9A9A", # Light red
|
| 585 |
+
"oppose": "#E57373", # Red
|
| 586 |
+
"strongly_oppose": "#C62828", # Dark red
|
| 587 |
+
}
|
| 588 |
|
| 589 |
for node in G.nodes():
|
| 590 |
x, y = pos[node]
|
| 591 |
node_x.append(x)
|
| 592 |
node_y.append(y)
|
| 593 |
node_data = G.nodes[node]
|
| 594 |
+
|
| 595 |
+
# Get opinion for this node
|
| 596 |
+
opinion = opinion_map.get(node_data.get('persona_id', node))
|
| 597 |
+
if opinion:
|
| 598 |
+
position_str = opinion.position.value
|
| 599 |
+
color = opinion_colors.get(position_str, "#BDBDBD")
|
| 600 |
+
position_label = position_str.replace("_", " ").title()
|
| 601 |
+
node_text.append(f"{node_data['label']}<br>{node_data['role']}<br>Opinion: {position_label}")
|
| 602 |
+
else:
|
| 603 |
+
color = "#BDBDBD"
|
| 604 |
+
node_text.append(f"{node_data['label']}<br>{node_data['role']}")
|
| 605 |
+
|
| 606 |
+
node_color.append(color)
|
| 607 |
|
| 608 |
node_trace = go.Scatter(
|
| 609 |
x=node_x,
|
|
|
|
| 615 |
marker=dict(
|
| 616 |
size=20,
|
| 617 |
color=node_color,
|
| 618 |
+
line=dict(width=2, color="DarkSlateGrey"),
|
|
|
|
|
|
|
|
|
|
| 619 |
),
|
| 620 |
showlegend=False,
|
| 621 |
)
|
|
@@ -72,7 +72,12 @@ class PopulationNetwork:
|
|
| 72 |
# Use custom weights to distribute population
|
| 73 |
for base_persona in self.base_personas:
|
| 74 |
weight = self.persona_weights.get(base_persona.persona_id, 0)
|
|
|
|
|
|
|
|
|
|
| 75 |
count = int(round(weight * self.population_size))
|
|
|
|
|
|
|
| 76 |
|
| 77 |
generator = VariantGenerator(base_persona, self.variation_level)
|
| 78 |
persona_variants = [
|
|
|
|
| 72 |
# Use custom weights to distribute population
|
| 73 |
for base_persona in self.base_personas:
|
| 74 |
weight = self.persona_weights.get(base_persona.persona_id, 0)
|
| 75 |
+
if weight == 0:
|
| 76 |
+
continue # Skip personas with zero weight
|
| 77 |
+
|
| 78 |
count = int(round(weight * self.population_size))
|
| 79 |
+
if count == 0:
|
| 80 |
+
continue # Skip if rounding resulted in zero
|
| 81 |
|
| 82 |
generator = VariantGenerator(base_persona, self.variation_level)
|
| 83 |
persona_variants = [
|