python-vote-core icon indicating copy to clipboard operation
python-vote-core copied to clipboard

weighted voting

Open antviro opened this issue 10 years ago • 1 comments

I introduced a function for weighted voting with a St. Lagüe weight function given by 1/(1+2(n-1)) http://www.uv.es/~pla/sistelec/propabga.htm

ponderado.py: from abstract_classes import MultipleWinnerVotingSystem from common_functions import matching_keys import types import copy

class ponderado(MultipleWinnerVotingSystem):

def __init__(self, ballots, tie_breaker=None, required_winners=1):
    super(Ponderado, self).__init__(ballots, tie_breaker=tie_breaker, required_winners=required_winners)

def calculate_results(self):

    # Standardize the ballot format and extract the candidates
    self.candidates = set()
    for ballot in self.ballots:

        # Convert single candidate ballots into ballot lists
        if not isinstance(ballot["ballot"], types.ListType):
            ballot["ballot"] = [ballot["ballot"]]

        # Ensure no ballot has an excess of votes
        if len(ballot["ballot"]) > self.required_winners:
            raise Exception("A ballot contained too many candidates")

        # Add all candidates on the ballot to the set
        self.candidates.update(set(ballot["ballot"]))

    # Sum up all votes for each candidate with weight!!
    self.tallies = dict.fromkeys(self.candidates, 0)
    for ballot in self.ballots:
        for icand in range(0,len(ballot["ballot"])):
            candidate = ballot["ballot"][icand]
            peso = 1./(1+2*icand)
            self.tallies[candidate] += ballot["count"]*peso
    tallies = copy.deepcopy(self.tallies)

    # Determine which candidates win
    winning_candidates = set()
    while len(winning_candidates) < self.required_winners:

        # Find the remaining candidates with the most votes
        largest_tally = max(tallies.values())
        top_candidates = matching_keys(tallies, largest_tally)

        # Reduce the found candidates if there are too many
        if len(top_candidates | winning_candidates) > self.required_winners:
            self.tied_winners = top_candidates.copy()
            while len(top_candidates | winning_candidates) > self.required_winners:
                top_candidates.remove(self.break_ties(top_candidates, True))

        # Move the top candidates into the winning pile
        winning_candidates |= top_candidates
        for candidate in top_candidates:
            del tallies[candidate]

    self.winners = winning_candidates

def as_dict(self):
    data = super(Ponderado, self).as_dict()
    data["tallies"] = self.tallies
    return data

antviro avatar Oct 22 '14 16:10 antviro

Perhaps, it could be added to this suggested new method a selector for different weighting functions. For instance the above included St. Lague weighting function given by 1/(1+2(n-1)) as default and another extra option like a Hont based weighting function 1/(1+n-1)=1/n

Where n is the position on the preferential vote from n=1 for the first selection to the last one

antviro avatar Oct 28 '14 11:10 antviro