Daniel Ari Friedman eaf134b3c3 BioFirm
2025-02-11 08:39:22 -08:00

324 строки
12 KiB
Python

"""
Visualization implementation for BioFirm framework.
Provides comprehensive plotting and visualization tools.
"""
from typing import Dict, List, Optional, Tuple, Union, Any
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.figure import Figure
import networkx as nx
from scipy.stats import gaussian_kde
from ..core.state_spaces import BioregionalState
from ..core.stewardship import Intervention, StewardshipMetrics
class BioregionalVisualization:
"""Comprehensive bioregional visualization tools."""
def __init__(self, style: str = "seaborn-whitegrid"):
"""Initialize visualization suite with style."""
plt.style.use(style)
self.colors = sns.color_palette("husl", 8)
def plot_system_state(self,
bioregional_state: BioregionalState,
time_series: Optional[np.ndarray] = None
) -> Figure:
"""Visualize multi-dimensional system state."""
fig = plt.figure(figsize=(15, 10))
if time_series is not None:
# Plot time series
self._plot_time_series(fig, time_series, bioregional_state)
else:
# Plot current state
self._plot_current_state(fig, bioregional_state)
plt.tight_layout()
return fig
def _plot_time_series(self,
fig: Figure,
time_series: np.ndarray,
state: BioregionalState):
"""Plot time series of state variables."""
gs = fig.add_gridspec(2, 2)
# Ecological time series
ax1 = fig.add_subplot(gs[0, 0])
self._plot_domain_time_series(
ax1, time_series, state.ecological_state,
"Ecological Metrics", self.colors[0]
)
# Climate time series
ax2 = fig.add_subplot(gs[0, 1])
self._plot_domain_time_series(
ax2, time_series, state.climate_state,
"Climate Metrics", self.colors[1]
)
# Social time series
ax3 = fig.add_subplot(gs[1, 0])
self._plot_domain_time_series(
ax3, time_series, state.social_state,
"Social Metrics", self.colors[2]
)
# Economic time series
ax4 = fig.add_subplot(gs[1, 1])
self._plot_domain_time_series(
ax4, time_series, state.economic_state,
"Economic Metrics", self.colors[3]
)
def _plot_domain_time_series(self,
ax: plt.Axes,
time_series: np.ndarray,
state_dict: Dict[str, float],
title: str,
color: Tuple[float, float, float]):
"""Plot time series for a specific domain."""
for i, (var, _) in enumerate(state_dict.items()):
ax.plot(time_series[:, i],
label=var,
color=color,
alpha=0.5 + 0.5 * i/len(state_dict))
ax.set_title(title)
ax.set_xlabel("Time")
ax.set_ylabel("Value")
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax.grid(True, alpha=0.3)
def _plot_current_state(self, fig: Figure, state: BioregionalState):
"""Plot current state as radar charts."""
gs = fig.add_gridspec(2, 2)
# Ecological radar
ax1 = fig.add_subplot(gs[0, 0], projection='polar')
self._plot_domain_radar(
ax1, state.ecological_state,
"Ecological State", self.colors[0]
)
# Climate radar
ax2 = fig.add_subplot(gs[0, 1], projection='polar')
self._plot_domain_radar(
ax2, state.climate_state,
"Climate State", self.colors[1]
)
# Social radar
ax3 = fig.add_subplot(gs[1, 0], projection='polar')
self._plot_domain_radar(
ax3, state.social_state,
"Social State", self.colors[2]
)
# Economic radar
ax4 = fig.add_subplot(gs[1, 1], projection='polar')
self._plot_domain_radar(
ax4, state.economic_state,
"Economic State", self.colors[3]
)
def _plot_domain_radar(self,
ax: plt.Axes,
state_dict: Dict[str, float],
title: str,
color: Tuple[float, float, float]):
"""Plot radar chart for a specific domain."""
variables = list(state_dict.keys())
values = list(state_dict.values())
angles = np.linspace(0, 2*np.pi, len(variables), endpoint=False)
values = np.concatenate((values, [values[0]])) # complete the loop
angles = np.concatenate((angles, [angles[0]])) # complete the loop
ax.plot(angles, values, color=color, linewidth=2)
ax.fill(angles, values, color=color, alpha=0.25)
ax.set_xticks(angles[:-1])
ax.set_xticklabels(variables)
ax.set_title(title)
def plot_intervention_impacts(self,
before_state: BioregionalState,
after_state: BioregionalState,
intervention_data: Dict[str, Any]) -> Figure:
"""Visualize intervention outcomes."""
fig = plt.figure(figsize=(15, 10))
gs = fig.add_gridspec(2, 2)
# State changes
ax1 = fig.add_subplot(gs[0, :])
self._plot_state_changes(ax1, before_state, after_state)
# Intervention details
ax2 = fig.add_subplot(gs[1, 0])
self._plot_intervention_details(ax2, intervention_data)
# Uncertainty analysis
ax3 = fig.add_subplot(gs[1, 1])
self._plot_uncertainty_analysis(ax3, intervention_data)
plt.tight_layout()
return fig
def _plot_state_changes(self,
ax: plt.Axes,
before: BioregionalState,
after: BioregionalState):
"""Plot before/after state changes."""
variables = []
before_values = []
after_values = []
# Collect all variables and values
for domain in ["ecological", "climate", "social", "economic"]:
before_dict = getattr(before, f"{domain}_state")
after_dict = getattr(after, f"{domain}_state")
for var in before_dict.keys():
variables.append(f"{domain}.{var}")
before_values.append(before_dict[var])
after_values.append(after_dict[var])
# Plot
x = np.arange(len(variables))
width = 0.35
ax.bar(x - width/2, before_values, width, label='Before',
color='lightgray')
ax.bar(x + width/2, after_values, width, label='After',
color='darkgreen')
ax.set_ylabel('Value')
ax.set_title('State Changes from Intervention')
ax.set_xticks(x)
ax.set_xticklabels(variables, rotation=45, ha='right')
ax.legend()
def _plot_intervention_details(self,
ax: plt.Axes,
intervention_data: Dict[str, Any]):
"""Plot intervention details."""
# Extract key metrics
metrics = {
'Duration': intervention_data.get('duration', 0),
'Intensity': intervention_data.get('intensity', 0),
'Cost': intervention_data.get('resources', {}).get('budget', 0),
'Success Prob': 1 - intervention_data.get('uncertainty', 0)
}
# Create horizontal bar chart
y_pos = np.arange(len(metrics))
ax.barh(y_pos, list(metrics.values()))
ax.set_yticks(y_pos)
ax.set_yticklabels(list(metrics.keys()))
ax.set_title('Intervention Metrics')
def _plot_uncertainty_analysis(self,
ax: plt.Axes,
intervention_data: Dict[str, Any]):
"""Plot uncertainty analysis."""
# Generate synthetic uncertainty data
outcomes = intervention_data.get('expected_outcomes', {})
uncertainties = np.random.normal(
loc=list(outcomes.values()),
scale=intervention_data.get('uncertainty', 0.1),
size=(1000, len(outcomes))
)
# Plot density
for i, (var, _) in enumerate(outcomes.items()):
density = gaussian_kde(uncertainties[:, i])
xs = np.linspace(0, 1, 200)
ax.plot(xs, density(xs), label=var)
ax.set_title('Outcome Uncertainty')
ax.set_xlabel('Value')
ax.set_ylabel('Density')
ax.legend()
def plot_cross_scale_dynamics(self,
states: Dict[str, np.ndarray],
scales: List[str],
interactions: np.ndarray) -> Figure:
"""Visualize cross-scale ecological dynamics."""
fig = plt.figure(figsize=(15, 10))
# Network visualization
ax1 = fig.add_subplot(121)
self._plot_scale_network(ax1, scales, interactions)
# Scale correlations
ax2 = fig.add_subplot(122)
self._plot_scale_correlations(ax2, states, scales)
plt.tight_layout()
return fig
def _plot_scale_network(self,
ax: plt.Axes,
scales: List[str],
interactions: np.ndarray):
"""Plot network of cross-scale interactions."""
G = nx.DiGraph()
# Add nodes
for scale in scales:
G.add_node(scale)
# Add edges
n_scales = len(scales)
for i in range(n_scales):
for j in range(n_scales):
if i != j and interactions[i, j] > 0:
G.add_edge(scales[i], scales[j],
weight=interactions[i, j])
# Draw network
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G, pos, node_color='lightblue',
node_size=1000, ax=ax)
nx.draw_networkx_labels(G, pos, ax=ax)
edges = nx.draw_networkx_edges(
G, pos,
edge_color=[G[u][v]['weight'] for u, v in G.edges()],
edge_cmap=plt.cm.YlOrRd,
width=2,
ax=ax
)
plt.colorbar(edges, ax=ax, label='Interaction Strength')
ax.set_title('Cross-scale Interactions')
def _plot_scale_correlations(self,
ax: plt.Axes,
states: Dict[str, np.ndarray],
scales: List[str]):
"""Plot correlations between scales."""
n_scales = len(scales)
correlations = np.zeros((n_scales, n_scales))
# Compute correlations
for i, scale1 in enumerate(scales):
for j, scale2 in enumerate(scales):
if i != j:
corr = np.corrcoef(
states[scale1].mean(axis=1),
states[scale2].mean(axis=1)
)[0, 1]
correlations[i, j] = corr
# Plot heatmap
sns.heatmap(correlations,
xticklabels=scales,
yticklabels=scales,
cmap='RdBu_r',
vmin=-1, vmax=1,
ax=ax)
ax.set_title('Cross-scale Correlations')