From 23a123431487d898decc233770177e021bfd074d Mon Sep 17 00:00:00 2001 From: Sven Geboers Date: Sat, 21 Mar 2026 23:58:38 +0100 Subject: [PATCH] feat(analysis): add 2D political compass (PCA/anchor) and 2D trajectories + visualizations - compute_2d_axes (pca, anchor) with optional L2-normalisation pre-projection - compute_2d_trajectories: per-MP coords, step vectors, magnitudes, totals - plot_political_compass and plot_2d_trajectories (Plotly HTML) - tests/test_political_compass.py (synthetic unit test) --- analysis/political_axis.py | 7 ++++++- analysis/trajectory.py | 11 +++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/analysis/political_axis.py b/analysis/political_axis.py index 7580c9a..6113f56 100644 --- a/analysis/political_axis.py +++ b/analysis/political_axis.py @@ -133,6 +133,7 @@ def compute_2d_axes( window_ids: Optional[List[str]] = None, method: str = "pca", anchor_kwargs: Optional[Dict] = None, + normalize_vectors: bool = True, ) -> Tuple[Dict[str, Dict[str, Tuple[float, float]]], Dict[str, np.ndarray]]: """Compute 2D coordinates for MPs per window. @@ -174,7 +175,11 @@ def compute_2d_axes( entity_index = [] # parallel list of (window_id, entity) for wid, d in aligned_window_vecs.items(): for ent, v in d.items(): - all_vecs.append(v) + if normalize_vectors: + n = np.linalg.norm(v) + all_vecs.append(v / n if n > 1e-10 else v) + else: + all_vecs.append(v) entity_index.append((wid, ent)) if len(all_vecs) == 0: diff --git a/analysis/trajectory.py b/analysis/trajectory.py index cfe119a..4756109 100644 --- a/analysis/trajectory.py +++ b/analysis/trajectory.py @@ -196,7 +196,10 @@ def compute_trajectories( def compute_2d_trajectories( - db_path: str, method: str = "pca", anchor_kwargs: Optional[Dict] = None + db_path: str, + method: str = "pca", + anchor_kwargs: Optional[Dict] = None, + normalize_vectors: bool = True, ) -> Dict[str, Dict]: """Compute 2D trajectory positions for MPs using compute_2d_axes. @@ -219,7 +222,11 @@ def compute_2d_trajectories( return {} positions_by_window, axes = compute_2d_axes( - db_path, window_ids=window_ids, method=method, anchor_kwargs=anchor_kwargs + db_path, + window_ids=window_ids, + method=method, + anchor_kwargs=anchor_kwargs, + normalize_vectors=normalize_vectors, ) # Build per-MP time-ordered coords