mirror of
https://github.com/tgorordo/smithy.git
synced 2026-06-13 02:42:14 -07:00
some formatting and add cli compilation
This commit is contained in:
parent
8836c49091
commit
4a624a4847
9 changed files with 115 additions and 61 deletions
|
|
@ -0,0 +1,4 @@
|
|||
from smithy import cli
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
|
|
@ -7,23 +7,20 @@ from rich.panel import Panel
|
|||
|
||||
from .rcv import smith_set
|
||||
|
||||
console = Console()
|
||||
|
||||
@click.command()
|
||||
@click.argument(
|
||||
"spreadsheet",
|
||||
type=click.Path(exists=True, dir_okay=False)
|
||||
)
|
||||
def main(spreadsheet: str) -> None:
|
||||
@click.argument("spreadsheet", type=click.Path(exists=True, dir_okay=False))
|
||||
def cli(spreadsheet: str) -> None:
|
||||
"""
|
||||
Compute the Smith set from a ranked-choice ballot spreadsheet.
|
||||
|
||||
|
||||
The Smith set is the minimal set of candidates which can beat all others pairwise - if there is a single winner
|
||||
in the set they are guaranteed the Condorcet i.e. Majority winner.
|
||||
"""
|
||||
|
||||
try:
|
||||
console = Console()
|
||||
|
||||
try:
|
||||
# Load spreadsheet
|
||||
if spreadsheet.endswith(".csv"):
|
||||
df = pl.read_csv(spreadsheet)
|
||||
|
|
@ -33,17 +30,21 @@ def main(spreadsheet: str) -> None:
|
|||
|
||||
else:
|
||||
console.print(
|
||||
"[bold red]Unsupported file type.[/bold red]\n"
|
||||
"Use CSV or Excel."
|
||||
"[bold red]Unsupported file type.[/bold red]\nUse CSV or Excel."
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
# Normalize numerical dataframe entries
|
||||
df = df.with_columns([ pl.col(c)
|
||||
.cast(pl.Utf8)
|
||||
.str.strip_chars()
|
||||
.cast(pl.Int64, strict=False).fill_null(0)
|
||||
for c in df.columns ])
|
||||
df = df.with_columns(
|
||||
[
|
||||
pl.col(c)
|
||||
.cast(pl.Utf8)
|
||||
.str.strip_chars()
|
||||
.cast(pl.Int64, strict=False)
|
||||
.fill_null(0)
|
||||
for c in df.columns
|
||||
]
|
||||
)
|
||||
|
||||
# Compute Smith set
|
||||
smiths = smith_set(df)
|
||||
|
|
@ -66,14 +67,11 @@ def main(spreadsheet: str) -> None:
|
|||
Panel.fit(
|
||||
"\n".join(f"• {c}" for c in smiths),
|
||||
title="Resulting Smith Set",
|
||||
border_style="green"
|
||||
border_style="green",
|
||||
)
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
console.print(
|
||||
f"[bold red]Error:[/bold red] {e}"
|
||||
)
|
||||
console.print(f"[bold red]Error:[/bold red] {e}")
|
||||
|
||||
raise SystemExit(1)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import polars as pl
|
||||
from itertools import combinations
|
||||
|
||||
|
||||
def smith_set(df: pl.DataFrame) -> list:
|
||||
"""
|
||||
Compute the Smith set from a Ranked-Choice ballot.
|
||||
|
||||
|
||||
The Smith set is the minimal set of candidates which can beat all others pairwise - if there is a single winner
|
||||
in the set they are guaranteed the Condorcet i.e. Majority winner.
|
||||
|
||||
|
||||
parameters
|
||||
---
|
||||
df : pl.DataFrame
|
||||
|
|
@ -27,7 +28,7 @@ def smith_set(df: pl.DataFrame) -> list:
|
|||
candidates = df.columns
|
||||
|
||||
# Build pairwise majority graph
|
||||
graph: dict[str, set[str]] = { c: set() for c in candidates }
|
||||
graph: dict[str, set[str]] = {c: set() for c in candidates}
|
||||
|
||||
for a, b in combinations(candidates, 2):
|
||||
result = df.select(
|
||||
|
|
@ -46,17 +47,13 @@ def smith_set(df: pl.DataFrame) -> list:
|
|||
|
||||
# Find Smith set
|
||||
for size in range(1, len(candidates) + 1):
|
||||
|
||||
for sub in combinations(candidates, size):
|
||||
|
||||
subset = set(sub)
|
||||
out = set(candidates) - subset
|
||||
|
||||
dom = True
|
||||
|
||||
for member in subset:
|
||||
|
||||
# DIRECT dominance only
|
||||
if not out.issubset(graph[member]):
|
||||
dom = False
|
||||
break
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue