Overview
Co-votation analysis measures how often each pair of legislators vote the same way across roll-call events, then builds a weighted network from those pairwise similarities. This page presents the dynamic extension: instead of a single static snapshot, the pipeline segments the full record into temporal windows and traces how discipline, community structure, and coalition alignment evolve over time.
Interactive: Party Discipline
Degree of internal cohesion for each caucus across temporal windows. Values close to 1.0 indicate high discipline.
Interactive: Network Evolution
Modularity (block fragmentation) and graph density by temporal window.
Co-Voting Matrix Construction
The co-voting matrix is the foundation of all network-based analysis. Construction follows a deterministic pipeline:
- Load data — votes, persons, and organizations from SQLite.
- Party normalization —
normalize_party()maps mixedvote.groupvalues to canonical organization IDs using theORG_TO_SHORTmapping (e.g.O01→MORENA,O02→PT). - Primary party assignment —
get_primary_party()assigns each legislator to the party where they cast the most votes. Ties are broken by most recentstart_datemembership. - Build matrix —
build_covotacion_matrix()produces an NxN numpy matrix where entry(i, j)is the normalized agreement count between legislators i and j, ranging from 0.0 to 1.0. - Build graph —
build_graph()converts the matrix to a NetworkX graph with:- Nodes: legislators, with attributes for party (
PARTY_COLORS), gender, and chamber. - Edges: co-voting pairs, with
weight= normalized similarity.
- Nodes: legislators, with attributes for party (
The resulting graph is the input for community detection, centrality, and all downstream modules. Colors follow PARTY_COLORS (matplotlib palette), and node ordering respects PARTY_ORDER.
Community Detection (Louvain)
The pipeline uses nx.community.louvain_communities() from NetworkX (not the standalone python-louvain package) to detect communities of legislators who vote similarly, going beyond formal party labels to reveal actual voting blocs.
Algorithm
detect_communities(graph, resolution=1.0, seed=42)
Louvain performs two-phase iterative optimization:
- Local moving — each node moves to the neighbor community that yields the largest modularity gain.
- Aggregation — communities are collapsed into super-nodes, and the process repeats.
The seed=42 parameter (LOUVAIN_SEED) ensures reproducibility across runs. The resolution parameter (LOUVAIN_RESOLUTION, default 1.0) controls community granularity:
| Resolution | Effect |
|---|---|
| < 1.0 | Fewer, larger communities (coarser) |
| 1.0 (default) | Standard modularity |
| > 1.0 | More, smaller communities (finer) |
Output
detect_communities() returns a partition dict: {node_id: community_id}.
analyze_communities() produces detailed analysis per community:
- num_communities — total communities detected
- sizes — member count per community
- composition — count and percentage of each party within the community
- purity — percentage of the dominant party (100% = pure party bloc)
- cross-party legislators — individuals whose community differs from their formal party
- MORENA sub-blocks — detection of internal factions within MORENA
- modularity — overall modularity score of the partition
:::tip A community with purity below 70% signals a genuine cross-party coalition, not just a party label. These mixed communities often reveal real legislative alliances. :::
Temporal Window Strategies
The dynamic co-votation module (covotacion_dinamica.py, 829 lines) segments voting records into temporal windows and analyzes each independently. Three strategies are available:
Fixed Windows
The timeline is divided into equal-length intervals. Each window contains all vote events within its date range. This provides consistent, non-overlapping snapshots.
Expanding Windows
Each subsequent window includes all previous data plus the new period. Window 1 = period 1; Window 2 = periods 1–2; Window N = periods 1–N. Useful for tracking cumulative coalition formation.
Rolling Windows
A fixed-width window slides forward in time, producing overlapping snapshots. This smooths out noise from individual votes and highlights gradual shifts in alignment.
Minimum Window Size
All strategies enforce a minimum threshold:
MIN_EVENTS_PER_WINDOW = 30
Windows with fewer than 30 vote events are merged with the preceding window to ensure statistical validity. This prevents spurious community detection from tiny samples.
ARI (Adjusted Rand Index)
The Adjusted Rand Index measures the stability of community partitions between consecutive temporal windows. It compares two clusterings by counting pairs of items that are assigned to the same or different clusters in both partitions.
| ARI Value | Interpretation |
|---|---|
| 1.0 | Perfect agreement — identical partitions |
| 0.0 | Random overlap — no more similar than chance |
| < 0.0 | Systematic disagreement — less overlap than expected by chance |
A persistently high ARI (above 0.7) across windows indicates stable coalition structure. A sudden drop signals a realignment event — a major legislative vote, leadership change, or party split that disrupted normal voting patterns.
Bicameral and Coalition Filtering
The pipeline supports separate and combined analysis of Mexico’s bicameral legislature:
- Chamber mapping:
CAMARA_MAPmapsdiputados→Dandsenado→S. - Per-chamber analysis: co-voting graphs can be built for either chamber independently, reflecting the distinct political dynamics of each.
- Coalition filtering: the pipeline can filter vote records to include only events where specific coalitions were active, enabling targeted analysis of alliance behavior.
This is critical because cross-chamber voting patterns differ significantly — senators tend to show higher party discipline than deputies, and coalition structures vary between chambers.
Cross-Legislature Support
The module supports analysis that spans multiple legislatures using LEGISLATURAS_ORDERED, the canonical ordering of legislative periods. This enables:
- Longitudinal discipline tracking — how a party’s internal cohesion changes across successive legislatures.
- Community evolution — which legislators shift communities between legislatures, and what that implies about coalition realignment.
- Cross-period comparison — legislators who served in multiple legislatures can be tracked as they move through the political space.
The cross-legislature mode differs from per-legislature analysis in that it preserves a consistent node identity across windows, allowing direct longitudinal comparison of the same legislator’s co-voting behavior over time.
Dealignment Detection
The pipeline includes automated detection of dealignment — significant shifts in co-voting patterns that indicate a legislator or party is drifting away from its traditional allies.
Mechanism
DEALIGNMENT_THRESHOLD = -0.05
For each legislator, the module computes the change in average co-voting similarity with their own party between consecutive windows. If the change falls below DEALIGNMENT_THRESHOLD (a drop of more than 0.05), that legislator is flagged as dealigned.
Interpretation
Dealignment does not necessarily mean a legislator is defecting. Common causes include:
- Issue-specific splits — the party line diverged from the legislator’s position on a series of votes.
- Coalition realignment — the party shifted its alliance structure, leaving some members behind.
- Personal ideological drift — the legislator is genuinely moving away from the party center.
The module also tracks TOP_DISSIDENTS_PER_WINDOW (default: 5), ranking the most frequent dissenters in each window by the number of votes cast against the party majority.
:::tip Persistent dealignment across multiple windows is a strong early signal of party switching or faction formation. Single-window dealignment is often just noise from an unusual vote. :::