Skip to content

Commit 6ee772b

Browse files
committed
Edit docs for metrics, partition, and proposals modules
1 parent 8a15cb9 commit 6ee772b

File tree

8 files changed

+409
-86
lines changed

8 files changed

+409
-86
lines changed

gerrychain/metrics/compactness.py

+24-3
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
11
import math
2+
from typing import Dict
23

34

4-
def compute_polsby_popper(area, perimeter):
5+
def compute_polsby_popper(area: float, perimeter: float) -> float:
6+
"""
7+
Computes the Polsby-Popper score for a single district.
8+
9+
:param area: The area of the district
10+
:type area: float
11+
:param perimeter: The perimeter of the district
12+
:type perimeter: float
13+
14+
:returns: The Polsby-Popper score for the district
15+
:rtype: float
16+
"""
517
try:
618
return 4 * math.pi * area / perimeter ** 2
719
except ZeroDivisionError:
820
return math.nan
921

1022

11-
def polsby_popper(partition):
12-
"""Computes Polsby-Popper compactness scores for each district in the partition.
23+
# Partition type hint left out due to circular import
24+
# def polsby_popper(partition: Partition) -> Dict[int, float]:
25+
def polsby_popper(partition) -> Dict[int, float]:
26+
"""
27+
Computes Polsby-Popper compactness scores for each district in the partition.
28+
29+
:param partition: The partition to compute scores for
30+
:type partition: Partition
31+
32+
:returns: A dictionary mapping each district ID to its Polsby-Popper score
33+
:rtype: Dict[int, float]
1334
"""
1435
return {
1536
part: compute_polsby_popper(

gerrychain/metrics/partisan.py

+59-24
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,47 @@
1+
"""
2+
The partisan metrics in this file are later used in the module
3+
gerrychain.updaters.election.py. Thus, all of the election
4+
results objects here are implicilty typed as ElectionResults,
5+
but cannot be given an explicit type annotation due to problems
6+
with circular imports.
7+
"""
8+
19
import numpy
10+
from typing import Tuple
211

312

4-
def mean_median(election_results):
13+
def mean_median(election_results) -> float:
514
"""
615
Computes the Mean-Median score for the given ElectionResults.
716
A positive value indicates an advantage for the first party listed
817
in the Election's parties_to_columns dictionary.
18+
19+
:param election_results: An ElectionResults object
20+
:type election_results: ElectionResults
21+
22+
:returns: The Mean-Median score for the given ElectionResults
23+
:rtype: float
924
"""
1025
first_party = election_results.election.parties[0]
1126
data = election_results.percents(first_party)
1227

1328
return numpy.median(data) - numpy.mean(data)
1429

1530

16-
def mean_thirdian(election_results):
31+
def mean_thirdian(election_results) -> float:
1732
"""
1833
Computes the Mean-Median score for the given ElectionResults.
1934
A positive value indicates an advantage for the first party listed
2035
in the Election's parties_to_columns dictionary.
2136
2237
The motivation for this score is that the minority party in many
2338
states struggles to win even a third of the seats.
39+
40+
:param election_results: An ElectionResults object
41+
:type election_results: ElectionResults
42+
43+
:returns: The Mean-Thirdian score for the given ElectionResults
44+
:rtype: float
2445
"""
2546
first_party = election_results.election.parties[0]
2647
data = election_results.percents(first_party)
@@ -31,24 +52,35 @@ def mean_thirdian(election_results):
3152
return thirdian - numpy.mean(data)
3253

3354

34-
def efficiency_gap(results):
55+
def efficiency_gap(election_results) -> float:
3556
"""
3657
Computes the efficiency gap for the given ElectionResults.
3758
A positive value indicates an advantage for the first party listed
3859
in the Election's parties_to_columns dictionary.
60+
61+
:param election_results: An ElectionResults object
62+
:type election_results: ElectionResults
63+
64+
:returns: The efficiency gap for the given ElectionResults
65+
:rtype: float
3966
"""
40-
party1, party2 = [results.counts(party) for party in results.election.parties]
67+
party1, party2 = [election_results.counts(party) for party in election_results.election.parties]
4168
wasted_votes_by_part = map(wasted_votes, party1, party2)
42-
total_votes = results.total_votes()
69+
total_votes = election_results.total_votes()
4370
numerator = sum(waste2 - waste1 for waste1, waste2 in wasted_votes_by_part)
4471
return numerator / total_votes
4572

4673

47-
def wasted_votes(party1_votes, party2_votes):
74+
def wasted_votes(party1_votes: int, party2_votes: int) -> Tuple[int, int]:
4875
"""
4976
Computes the wasted votes for each party in the given race.
50-
:party1_votes: the number of votes party1 received in the race
51-
:party2_votes: the number of votes party2 received in the race
77+
:param party1_votes: the number of votes party1 received in the race
78+
:type party1_votes: int
79+
:param party2_votes: the number of votes party2 received in the race
80+
:type party2_votes: int
81+
82+
:returns: a tuple of the wasted votes for each party
83+
:rtype: Tuple[int, int]
5284
"""
5385
total_votes = party1_votes + party2_votes
5486
if party1_votes > party2_votes:
@@ -60,12 +92,18 @@ def wasted_votes(party1_votes, party2_votes):
6092
return party1_waste, party2_waste
6193

6294

63-
def partisan_bias(election_results):
95+
def partisan_bias(election_results) -> float:
6496
"""
6597
Computes the partisan bias for the given ElectionResults.
6698
The partisan bias is defined as the number of districts with above-mean
6799
vote share by the first party divided by the total number of districts,
68100
minus 1/2.
101+
102+
:param election_results: An ElectionResults object
103+
:type election_results: ElectionResults
104+
105+
:returns: The partisan bias for the given ElectionResults
106+
:rtype: float
69107
"""
70108
first_party = election_results.election.parties[0]
71109
party_shares = numpy.array(election_results.percents(first_party))
@@ -74,37 +112,34 @@ def partisan_bias(election_results):
74112
return (above_mean_districts / len(party_shares)) - 0.5
75113

76114

77-
def partisan_gini(election_results):
115+
def partisan_gini(election_results) -> float:
78116
"""
79117
Computes the partisan Gini score for the given ElectionResults.
80118
The partisan Gini score is defined as the area between the seats-votes
81119
curve and its reflection about (.5, .5).
120+
121+
For more information on the computation, see Definition 1 in:
122+
https://arxiv.org/pdf/2008.06930.pdf
123+
124+
:param election_results: An ElectionResults object
125+
:type election_results: ElectionResults
126+
127+
:returns: The partisan Gini score for the given ElectionResults
128+
:rtype: float
82129
"""
83130
# For two parties, the Gini score is symmetric--it does not vary by party.
84131
party = election_results.election.parties[0]
85132

86-
# To find seats as a function of votes, we assume uniform partisan swing.
87-
# That is, if the statewide popular vote share for a party swings by some
88-
# delta, the vote share for that party swings by that delta in each
89-
# district.
90-
# We calculate the necessary delta to shift the district with the highest
91-
# vote share for the party to a vote share of 0.5. This delta, subtracted
92-
# from the original popular vote share, gives the minimum popular vote
93-
# share that yields 1 seat to the party.
94-
# We repeat this process for the district with the second-highest vote
95-
# share, which gives the minimum popular vote share yielding 2 seats,
96-
# and so on.
97133
overall_result = election_results.percent(party)
98134
race_results = sorted(election_results.percents(party), reverse=True)
99135
seats_votes = [overall_result - r + 0.5 for r in race_results]
100136

101137
# Apply reflection of seats-votes curve about (.5, .5)
102138
reflected_sv = reversed([1 - s for s in seats_votes])
103139
# Calculate the unscaled, unsigned area between the seats-votes curve
104-
# and its reflection. For each possible number of seats attained, we find
105-
# the area of a rectangle of unit height, with a width determined by the
106-
# horizontal distance between the curves at that number of seats.
140+
# and its reflection.
107141
unscaled_area = sum(abs(s - r) for s, r in zip(seats_votes, reflected_sv))
142+
108143
# We divide by area by the number of seats to obtain a partisan Gini score
109144
# between 0 and 1.
110145
return unscaled_area / len(race_results)

0 commit comments

Comments
 (0)