Seats-votes plots

SeatsVotesPlot applies a uniform swing to district vote shares and draws the resulting step curve. The observed election appears as a point.

from pathlib import Path

import geopandas as gpd
import pandas as pd

from gerrytools.plotting import SeatsVotesPlot

precincts = gpd.read_file(Path("data/ga_2016_precincts.gpkg"))
vote_columns = ["PRES16D", "PRES16R", "SEN16D", "SEN16R"]
precincts[vote_columns] = precincts[vote_columns].apply(pd.to_numeric)
district_votes = precincts.groupby("CD")[vote_columns].sum()

presidential_dem = district_votes["PRES16D"].to_numpy()
presidential_total = (district_votes["PRES16D"] + district_votes["PRES16R"]).to_numpy()

add_election() accepts target-party district votes and district total votes. Counts retain the turnout weighting used to place the observed statewide point.

plot = SeatsVotesPlot()
plot.add_election(presidential_dem, presidential_total)
plot.show()
../../../../_images/9d0e0df6b095268773af54d437a52c602549b02a9c224346a7d4a8ff9d6a86e5.png

When the input already contains district shares, omit total_votes. In that form, the observed x-coordinate is the unweighted mean of the district shares.

Compare elections

Repeated add_election() calls place several curves on the same axes. Reference lines can be added after the election data.

senate_dem = district_votes["SEN16D"].to_numpy()
senate_total = (district_votes["SEN16D"] + district_votes["SEN16R"]).to_numpy()

plot = SeatsVotesPlot(legend=True)
plot.add_election(
    presidential_dem,
    presidential_total,
    linecolor="denim",
    name="President 2016",
    marker_label="Observed President 2016",
)
plot.add_election(
    senate_dem,
    senate_total,
    linecolor="alizarin",
    name="Senate 2016",
    markerfacecolor="cherryblossompink",
    marker_label="Observed Senate 2016",
)
plot.add_proportionality_line()
plot.show()
../../../../_images/c9a84bd90e2f9964506c1c1af9539a9ba08a6037a8062aac39e28d4bc0df643a.png

Uniform swing changes every district by the same amount. It does not model differential turnout, incumbency, uncontested races, or candidate effects. The builder also provides an efficiency-gap line, custom reference lines, and controls for election markers and crosshairs.

Shared axes, labels, legends, and output controls are covered in shared statistical controls.