Weight ballots by their their best option

If the only options left are ones where a person can't make it, their
ballot shouldn't influence the result any more. Similarily when there
are only options which are questionable that ballot should have a lower
weight.

We do this by attaching a weight to each option, where real
choices get a weight of 1 and pseudo-choices (like "none of the
above") get a lower weight (theoretically that should be 0, but let's
keep that configurable). When constructing the ballots each vote gets a
monotonically decreasing weight. As options are eliminated in each
round, the weight of the ballot is recalculated as the maximum weight of
all remaining options. So this starts at 1 for all ballots, but drops as
more favourable options are eliminated, possibly down to 0 where a
ballot ceases to have an effect.
This commit is contained in:
Peter J. Holzer 2023-05-14 12:59:11 +02:00 committed by Peter J. Holzer
parent 34f361a4a6
commit 187718f360
2 changed files with 8 additions and 4 deletions

6
app.py
View File

@ -412,7 +412,8 @@ def get_ballots(meet_id, kind):
csr = get_cursor()
q = f"""
select {kind}.*, bod, position, email
select {kind}.*, bod, position, email,
min(w) over(partition by bod order by position) as vote_w
from {kind}
join {kind}_vote on {kind}.id = {kind}_vote.{kind}
join bod on bod = bod.id
@ -447,11 +448,12 @@ def runoff(ballots):
count[r.id] = [0] * len(ballot)
candidates[r.id] = r
for ballot in ballots:
weight = max(r.vote_w for r in ballot)
for pos, r in enumerate(ballot):
log.debug("count = %s", count)
log.debug("r.id = %s", r.id)
log.debug("pos = %s", pos)
count[r.id][pos] += 1
count[r.id][pos] += weight
log.debug("count[%d][%d]) = %d", r.id, pos, count[r.id][pos])
if direction == "backward":
result = sorted(count.keys(), key=lambda i: list(reversed(count[i])), reverse=True)

View File

@ -21,7 +21,8 @@ def get_ballots():
kind = args.kind
q = f"""
select {kind}.*, bod, position
select {kind}.*, bod, position,
min(w) over(partition by bod order by position) as vote_w
from {kind} join {kind}_vote on {kind}.id = {kind}_vote.{kind}
where meet = %s
order by bod, position
@ -52,8 +53,9 @@ def runoff(ballots):
count[r.id] = [0] * len(ballot)
candidates[r.id] = r
for ballot in ballots:
weight = max(r.vote_w for r in ballot)
for pos, r in enumerate(ballot):
count[r.id][pos] += 1
count[r.id][pos] += weight
if args.reverse:
result = sorted(count.keys(), key=lambda i: list(reversed(count[i])), reverse=True)
else: