Partisan Bias & Responsiveness

You can compute a full complement of partisan analytics for a set of districts all at once, described next, or you can compute individual metrics, described in the subsequent sections.

Partisan Analytics for a Set of Districts

To calculate all partisan analytics for a statewide two-party vote share and two-party vote shares by district:

def calc_partisan_metrics(Vf: float, Vf_array: list[float]) -> dict:

where “Vf” is the statewide two-party vote share, and “Vf_array” is a list of two-party vote shares by district.

Examples can be found in the “partisanship” section of the profiles in the testdata/CD116 directory.

This returns a dictionary of results:

results: dict = {
    "bias": bias_measurements,
    "responsiveness": responsiveness_measurements,
    "dSVpoints": dSVpoints,
    "rSVpoints": rSVpoints,
    "averageDVf": averageDVf,
    "averageRVf": averageRVf,
}

# where:

bias_measurements: dict = {
    "bestS": bestS,
    "estS": estS,
    "deviation": deviation,
    "tOf": TOf,
    "fptpS": fptpS,
    "bS50": Bs50f,
    "bV50": Bv50f,
    "decl": decl,
    "rvPoints": rvPoints,
    "eG": EG,
    "eGFPTP": EG_FPTP,
    "bSV": BsGf,
    "prop": prop,
    "mMs": mMs,
    "mMd": mMd,
    "lO": LO,
}

responsiveness_measurements: dict = {
    "cSimple": Cn,
    "cD": cD,
    "bigR": bigR,
    "littleR": littleR,
    "mIR": MIR,
    "rD": rD,
    "rDf": rDf,
    "averageMargin": average_margin,
}

The dictionary key names here match those in the relevant sections of the Map Analytics Format.

Measures of Bias

The bias measures above are described in Advanced Measures of Bias & Responsiveness. They may be calculated individually.

In the functions below:

def calc_best_seats(N: int, Vf: float) -> int:
def calc_disproportionality_from_best(est_Sf: float, best_Sf: float) -> float:
def calc_disproportionality(Vf: float, Sf: float) -> float:
def calc_efficiency_gap(Vf: float, Sf: float) -> float:
def calc_efficiency_gap_wasted_votes(dem_votes_by_district: List[int], rep_votes_by_district: List[int]) -> float
def calc_gamma(Vf: float, Sf: float, r: float) -> float:
def est_seats_bias(sv_curve_pts: list[tuple[float, float]], N: int) -> float:
def est_votes_bias(sv_curve_pts: list[tuple[float, float]], N: int) -> float:
def est_geometric_seats_bias(
    Vf: float,
    d_sv_pts: list[tuple[float, float]],
    r_sv_pts: list[tuple[float, float]],
) -> float:
def calc_global_symmetry(
    d_sv_pts: list[tuple[float, float]],
    r_sv_pts: list[tuple[float, float]],
    S50V: float,
) -> float:
def calc_declination(Vf_array: list[float]) -> Optional[float]:

Note: This function supports both median difference using average district vote share and median difference using statewide vote share, if provided.

def calc_mean_median_difference(
    Vf_array: list[float], Vf: Optional[float] = None
) -> float:
def calc_turnout_bias(Vf: float, Vf_array: list[float]) -> float:
def calc_lopsided_outcomes(Vf_array: list[float]) -> Optional[float]:

Measures of Responsiveness

Similarly, you can compute the responsiveness measures individually. These responsiveness measures are also described in Advanced Measures of Bias & Responsiveness.

def count_competitive_districts(Vf_array: list[float]) -> int:
def est_competitive_districts(Vf_array: list[float]) -> float:
def est_district_competitiveness(Vf: float) -> float:
def est_responsiveness(
    Vf: float, sv_curve_pts: list[tuple[float, float]], N: int
) -> float:
def calc_big_R(Vf: float, Sf: float) -> Optional[float]:
def calc_minimal_inverse_responsiveness(Vf: float, r: float) -> Optional[float]:
def est_responsive_districts(Vf_array: list[float]) -> float:
def calc_average_margin(Vf_array: List[float]) -> float:

Nagle’s Method

Finally, several functions expose John Nagle’s method for estimating fractional seat probabilities and district responsiveness and inferring seats-votes curves.

To estimate the fractional probability of a seat win for district, given a two-party vote share:

def est_seat_probability(Vf: float) -> float:

Similarly, to estimate the responsiveness of a district, given a two-party vote share:

def est_district_responsiveness(Vf: float) -> float:

To estimate the fractional # of seats for a set of two-party vote shares, using seat probabilities:

def est_seats(Vf_array: list[float]) -> float:

To estimate the fractional # of seats for a set of two-party vote shares, using first past the post accounting:

def est_fptp_seats(Vf_array: list[float]) -> int:

To infer the points of an seats-votes curve from a set of two-party vote shares by district, using either proportional or uniform shifts in statewide vote share:

def infer_sv_points(Vf: float, Vf_array: list[float], proportional=True) -> list[tuple]:

Finally, to infer the points of an inverse points from a set of inferred seats-votes curve points:

def infer_inverse_sv_points(sv_pts: list[tuple], N: int) -> list[tuple[float, float]]: