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)
This commit is contained in:
@@ -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,6 +175,10 @@ 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():
|
||||
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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user