Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add percents #50

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion evaltools/plotting/boxplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def boxplot(
ax.scatter(
boxplot + 1 + jitter_val,
score,
color=districtr(plan+1).pop(),
color=proposed_info['colors'][plan],
edgecolor='black',
s=100,
alpha=0.9,
Expand Down
2 changes: 1 addition & 1 deletion evaltools/plotting/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def histogram(ax,
# Plot vertical line.
ax.axvline(
s + bin_width / 2 + jitter_val,
color=districtr(i+1).pop(),
color=proposed_info['colors'][i],
lw=2,
label=f"{proposed_info['names'][i]}: {round(s,2)}",
)
Expand Down
7 changes: 4 additions & 3 deletions evaltools/plotting/sealevel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numpy as np
import random

def sealevel(ax, scores, num_districts, proposed_info, ticksize=12):
def sealevel(ax, scores, num_districts, proposed_info, rotation=0, ticksize=12):
"""
Plot a sea level plot: Each plan is a line across our elections on the
x-axis, with Democratic vote share on the y-axis. The statewide Dem. vote
Expand All @@ -17,6 +17,7 @@ def sealevel(ax, scores, num_districts, proposed_info, ticksize=12):
proposed_info (dict, optional): Dictionary with keys of `colors`, `names`;
the \(i\)th color in `color` corresponds to the \(i\)th name in `names`.
ticksize (float, optional): Font size for tick labels.
rotation (float): degrees to rotate xticklabels.
"""
assert "statewide" in scores
elections = sort_elections(scores["statewide"].keys())
Expand All @@ -43,13 +44,13 @@ def sealevel(ax, scores, num_districts, proposed_info, ticksize=12):
)
ax.legend()
if num_districts <= 20:
yticks = np.arange(0, 1 + 1/num_districts, 1/num_districts)
yticks = np.arange(0, 1 + 0.5/num_districts, 1/num_districts)
yticklabels = [f"{i}/{num_districts}" for i in range(num_districts + 1)]
ax.set_yticks(yticks)
ax.set_yticklabels(yticklabels)
ax.axhline(0.5, color=defaultGray, label="50%")
ax.set_xticks(range(len(elections)))
ax.set_xticklabels(elections, fontsize=ticksize)
ax.set_xticklabels(elections, fontsize=ticksize, rotation=rotation)
ax.set_ylim(-0.02, 1)


Expand Down
2 changes: 1 addition & 1 deletion evaltools/plotting/violin.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def violin(
ax.scatter(
violin + 1 + jitter_val,
score,
color=districtr(plan+1).pop(),
color=proposed_info['colors'][plan],
edgecolor='black',
s=100,
alpha=0.9,
Expand Down
1 change: 1 addition & 0 deletions evaltools/scoring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"opp_party_districts",
"party_wins_by_district",
"seats",
"percents",
"aggregate_seats",
"responsive_proportionality",
"stable_proportionality",
Expand Down
4 changes: 4 additions & 0 deletions evaltools/scoring/partisan.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def _seats(part: Partition, election_cols: Iterable[str], party: str, mean: bool
result = {e: sum([part[e].won(party, d) for d in part.parts.keys() if d != -1]) for e in election_cols}
return float(np.mean(list(result.values()))) if mean else result

def _percents(part: Partition, election_cols: Iterable[str], party:str, mean: bool = False) -> ScoreValue:
result = {e: part[e].percent(party) for e in election_cols}
return float(np.mean(list(result.values()))) if mean else result

def _responsive_proportionality(part: Partition, election_cols: Iterable[str], party: str) -> PlanWideScoreValue:
result = [(part[e].seats(party) / len(part)) - (part[e].percent(party)) for e in election_cols]
return float(np.mean(result))
Expand Down
21 changes: 21 additions & 0 deletions evaltools/scoring/scores.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
_opp_party_districts,
_party_wins_by_district,
_seats,
_percents,
_aggregate_seats,
_efficiency_gap,
_simplified_efficiency_gap,
Expand Down Expand Up @@ -280,6 +281,26 @@ def seats(election_cols: Iterable[str], party: str, mean: bool = False) -> Score
prefix = "mean_" if mean else ""
return Score(f"{prefix}{party}_seats", partial(_seats, election_cols=election_cols, party=party, mean=mean))

def percents(election_cols: Iterable[str], party: str, mean: bool = False) -> Score:
"""
Score representing how the percent of the vote the POV party won in each election (this is
constant regardless of districting plan).

Args:
election_cols (Iterable[str]): The names of the election updaters over which to compute
results for.
party (str): The "point of view" political party.
mean (bool): Whether to return the mean of the score over all elections, or a dictionary
of the score for each election.

Returns:
A score object with name `"{party}_percents"` and associated function that takes a partition and
returns an ScoreValue for the percent of the statewide vote won by the POV party for each
election.
"""
prefix = "mean_" if mean else ""
return Score(f"{prefix}{party}_percents", partial(_percents, election_cols=election_cols, party=party, mean=mean))

def aggregate_seats(election_cols: Iterable[str], party: str) -> Score:
"""
Score representing how many total seats (districts) within a given plan the POV party won across
Expand Down