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,
|
window_ids: Optional[List[str]] = None,
|
||||||
method: str = "pca",
|
method: str = "pca",
|
||||||
anchor_kwargs: Optional[Dict] = None,
|
anchor_kwargs: Optional[Dict] = None,
|
||||||
|
normalize_vectors: bool = True,
|
||||||
) -> Tuple[Dict[str, Dict[str, Tuple[float, float]]], Dict[str, np.ndarray]]:
|
) -> Tuple[Dict[str, Dict[str, Tuple[float, float]]], Dict[str, np.ndarray]]:
|
||||||
"""Compute 2D coordinates for MPs per window.
|
"""Compute 2D coordinates for MPs per window.
|
||||||
|
|
||||||
@@ -174,7 +175,11 @@ def compute_2d_axes(
|
|||||||
entity_index = [] # parallel list of (window_id, entity)
|
entity_index = [] # parallel list of (window_id, entity)
|
||||||
for wid, d in aligned_window_vecs.items():
|
for wid, d in aligned_window_vecs.items():
|
||||||
for ent, v in d.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))
|
entity_index.append((wid, ent))
|
||||||
|
|
||||||
if len(all_vecs) == 0:
|
if len(all_vecs) == 0:
|
||||||
|
|||||||
@@ -196,7 +196,10 @@ def compute_trajectories(
|
|||||||
|
|
||||||
|
|
||||||
def compute_2d_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]:
|
) -> Dict[str, Dict]:
|
||||||
"""Compute 2D trajectory positions for MPs using compute_2d_axes.
|
"""Compute 2D trajectory positions for MPs using compute_2d_axes.
|
||||||
|
|
||||||
@@ -219,7 +222,11 @@ def compute_2d_trajectories(
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
positions_by_window, axes = compute_2d_axes(
|
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
|
# Build per-MP time-ordered coords
|
||||||
|
|||||||
Reference in New Issue
Block a user