def scores_(self) -> NiceDict: if self.ballot_.candidate is None: if self.count_abstention: return NiceDict({c: 0 for c in self.candidates_}) else: return NiceDict() scores = NiceDict({c: 0 for c in self.candidates_}) scores[self.ballot_.candidate] = 1 return scores
def _gross_scores_and_weights_quicker_(self) -> dict: if not isinstance(self.scorer, ScorerPlurality): return self._gross_scores_and_weights_ # If it is a ScorerPlurality, we have a quicker method. gross_scores = NiceDict({c: 0 for c in self.candidates_}) total_weight = 0 for ballot, weight, _ in self.profile_converted_.items(): if ballot.candidate is None: if self.scorer.count_abstention: total_weight += weight continue gross_scores[ballot.candidate] += weight total_weight += weight weights = NiceDict({c: total_weight for c in self.candidates_}) return {'gross_scores': gross_scores, 'weights': weights}
def candidates_indexes_(self) -> NiceDict: """ The candidates as a dictionary. :return: a dictionary. To each candidate, it associates its index in :attr:`candidates_as_list_`. """ return NiceDict({c: i for i, c in enumerate(self.candidates_as_list_)})
def scores_as_floats_(self) -> NiceDict: """ Scores as floats. :return: :attr:`scores_` converted to floats. """ return NiceDict({c: float(v) for c, v in self.scores_.items()})
def detailed_scores_(self) -> list: """ Detailed scores. :return: a list of :class:`NiceDict`. The first dictionary gives the scores of the first round, etc. """ n_candidates = len(self.candidates_) detailed_scores = [] for k in range(1, n_candidates + 1): self.scorer.k = k gross_scores = NiceDict({c: 0 for c in self.candidates_}) weights = NiceDict({c: 0 for c in self.candidates_}) for ballot, weight, voter in self.profile_converted_.items(): for c, value in self.scorer( ballot=ballot, voter=voter, candidates=self.candidates_).scores_.items(): gross_scores[c] += weight * value weights[c] += weight scores = NiceDict({ c: my_division(score, weights[c], divide_by_zero=0) for c, score in gross_scores.items() }) detailed_scores.append(scores) if max(scores.values()) > Fraction(1, 2): break return detailed_scores
def scores_(self) -> NiceDict: matrix = self.matrix_weighted_majority_ return NiceDict({ c: min({ v for (i, j), v in matrix.as_dict_.items() if i == c and j != c }) for c in matrix.candidates_ })
def scores_(self) -> NiceDict: matrix = self.matrix_weighted_majority_ return NiceDict({ c: convert_number( sum([ v for (i, j), v in matrix.as_dict_.items() if i == c and j != c and v < 0 ])) for c in matrix.candidates_ })
def as_dict_(self): def convert(x, y): if x == y: return self.equal return self.greater if x > y else self.lower weighted_as_dict = self.matrix_weighted_majority_.as_dict_ return NiceDict({ (c, d): self.diagonal if c == d else convert(weighted_as_dict[(c, d)], weighted_as_dict[(d, c)]) for (c, d) in weighted_as_dict.keys() })
def _parse(self, b: dict) -> None: """ For this subclass, the internal representation is of the form {'a': 10, 'b': 7, 'c': 3}, meaning that a has evaluation 10; b, 7; and c, 3. :param b: a dictionary. """ self._internal_representation = NiceDict( {c: convert_number(v) for c, v in b.items()})
def scores_as_floats_(self) -> NiceDict: """ :return: :attr:`scores_`, converted to floats. """ def my_float(x): try: return float(x) except ValueError: return x return NiceDict({c: (my_float(s), float(x), float(y)) for c, (s, x, y) in self.scores_.items()})
def detailed_scores_as_floats_(self) -> list: """ Detailed scores, as floats. :return: :attr:`detailed_scores_`, converted to floats. """ return [ NiceDict({c: float(v) for c, v in counting_round.items()}) for counting_round in self.detailed_scores_ ]
def scores_as_floats_(self) -> NiceDict: """ The scores, given as floats. :return: :attr:`scores_`, converted to floats. :raise ValueError: if the scores cannot be converted to floats. Like all conversions to floats, it is advised to use this attribute for display purposes only. For computation, you should always use :attr:`scores_`, which usually manipulates fractions and therefore allows for exact computation. """ try: return NiceDict({c: float(v) for c, v in self.scores_.items()}) except ValueError: raise ValueError('These scores cannot be converted to floats: %r.' % self.scores_)
def scores_(self) -> NiceDict: """ The scores. :return: a :class:`NiceDict` of triples. """ levels_ = NiceDict({c: [] for c in self.candidates_}) weights_ = NiceDict({c: [] for c in self.candidates_}) for ballot, weight, voter in self.profile_converted_.items(): for c, level in self.scorer(ballot=ballot, voter=voter, candidates=self.candidates_).scores_.items(): levels_[c].append(level) weights_[c].append(weight) scores_ = NiceDict() for c in self.candidates_: if not levels_[c]: scores_[c] = (self.default_median, 0, 0) continue indexes = self.scorer.scale.argsort(levels_[c]) levels_[c] = [levels_[c][i] for i in indexes] weights_[c] = [weights_[c][i] for i in indexes] total_weight = sum(weights_[c]) half_total_weight = my_division(total_weight, 2) cumulative_weight = 0 median = None for i, weight in enumerate(weights_[c]): cumulative_weight += weight if cumulative_weight >= half_total_weight: median = levels_[c][i] break p = sum([weights_[c][i] for i, level in enumerate(levels_[c]) if self.scorer.scale.gt(level, median)]) q = sum([weights_[c][i] for i, level in enumerate(levels_[c]) if self.scorer.scale.lt(level, median)]) if p > q: scores_[c] = (median, my_division(p, total_weight), -my_division(q, total_weight)) else: scores_[c] = (median, -my_division(q, total_weight), my_division(p, total_weight)) return scores_
def scores_(self) -> NiceDict: scores = NiceDict(self.ballot_.as_dict.copy()) if self.level_absent is not None: scores.update({ c: self.level_absent for c in self.candidates_ - self.ballot_.candidates }) if self.level_ungraded is not None: scores.update({ c: self.level_ungraded for c in self.ballot_.candidates_not_in_b }) return scores
def scores_(self) -> NiceDict: scores = NiceDict() l_points_scheme = len(self.points_scheme) for i, c in enumerate(self.ballot_): if i < l_points_scheme: scores[c] = self.points_scheme[i] else: if self.points_fill is None: break scores[c] = self.points_fill if self.points_unordered is not None: scores.update({ c: self.points_unordered for c in self.ballot_.candidates_not_in_b }) if self.points_absent is not None: scores.update({ c: self.points_absent for c in self.candidates_ - self.ballot_.candidates }) return scores
def scores_(self) -> NiceDict: scores = NiceDict() points_remaining = self.k # Ordered candidates for indifference_class in self.ballot_.as_weak_order[:]: n_indifference = len(indifference_class) if n_indifference <= points_remaining: scores.update({c: 1 for c in indifference_class}) points_remaining -= n_indifference else: scores.update({ c: my_division(points_remaining, n_indifference) for c in indifference_class }) points_remaining = 0 # Unordered candidates if self.unordered_receive_points is False: scores.update({c: 0 for c in self.ballot_.candidates_not_in_b}) elif self.unordered_receive_points is True: unordered = self.ballot_.candidates_not_in_b n_unordered = len(unordered) if n_unordered <= points_remaining: scores.update({c: 1 for c in unordered}) points_remaining -= n_unordered else: scores.update({ c: my_division(points_remaining, n_unordered) for c in unordered }) points_remaining = 0 # Absent candidates if self.absent_receive_points is False: scores.update( {c: 0 for c in self.candidates_ - self.ballot_.candidates}) elif self.absent_receive_points is True: absent = self.candidates_ - self.ballot_.candidates n_absent = len(absent) if n_absent <= points_remaining: scores.update({c: 1 for c in absent}) else: scores.update({ c: my_division(points_remaining, n_absent) for c in absent }) return scores
def scores_(self) -> NiceDict: scores = NiceDict() points_from_lower_candidates = 0 # Absent candidates if self.absent_give_points or self.absent_receive_points is not None: absent = self.candidates_ - self.ballot_.candidates n_absent = len(absent) if self.absent_receive_points is False: scores.update({c: 0 for c in absent}) if self.absent_receive_points is True: points_temp = my_division(n_absent - 1, 2) if self.absent_give_points else 0 scores.update({c: points_temp for c in absent}) if self.absent_give_points: points_from_lower_candidates += n_absent # Unordered candidates if self.unordered_give_points or self.unordered_receive_points is not None: unordered = self.ballot_.candidates_not_in_b n_unordered = len(unordered) if self.unordered_receive_points is False: scores.update({c: 0 for c in unordered}) if self.unordered_receive_points is True: points_temp = points_from_lower_candidates if self.unordered_give_points: points_temp += my_division(n_unordered - 1, 2) scores.update({c: points_temp for c in unordered}) if self.unordered_give_points: points_from_lower_candidates += n_unordered # Ordered candidates for indifference_class in self.ballot_.as_weak_order[::-1]: n_indifference = len(indifference_class) points_temp = points_from_lower_candidates + my_division( n_indifference - 1, 2) scores.update({c: points_temp for c in indifference_class}) points_from_lower_candidates += n_indifference return scores
def _gross_and_weights_(self): gross = NiceDict({(c, d): 0 for c in self.candidates_ for d in self.candidates_}) weights = NiceDict({(c, d): 0 for c in self.candidates_ for d in self.candidates_}) for ballot, weight, _ in self.profile_converted_.items(): absent = self.candidates_ - ballot.candidates for i_class, indifference_class in enumerate(ballot.as_weak_order): indifference_class_as_list = list(indifference_class) for i, c in enumerate(indifference_class_as_list): # Deal with other candidates of the indifference class if self.indifference is not None: for d in indifference_class_as_list[i + 1:]: gross[(c, d)] += weight * self.indifference gross[(d, c)] += weight * self.indifference weights[(c, d)] += weight weights[(d, c)] += weight # Deal with ordered candidates with lower ranks if self.higher_vs_lower is not None or self.lower_vs_higher is not None: for lower_indifference_class in ballot.as_weak_order[ i_class + 1:]: for d in lower_indifference_class: if self.higher_vs_lower is not None: gross[(c, d)] += weight * self.higher_vs_lower weights[(c, d)] += weight if self.lower_vs_higher is not None: gross[(d, c)] += weight * self.lower_vs_higher weights[(d, c)] += weight # Deal with unordered candidates if self.ordered_vs_unordered is not None or self.unordered_vs_ordered is not None: for d in ballot.candidates_not_in_b: if self.ordered_vs_unordered is not None: gross[( c, d)] += weight * self.ordered_vs_unordered weights[(c, d)] += weight if self.unordered_vs_ordered is not None: gross[( d, c)] += weight * self.unordered_vs_ordered weights[(d, c)] += weight # Deal with absent candidates if self.ordered_vs_absent is not None or self.absent_vs_ordered is not None: for d in absent: if self.ordered_vs_absent is not None: gross[(c, d)] += weight + self.ordered_vs_absent weights[(c, d)] += weight if self.absent_vs_ordered is not None: gross[(d, c)] += weight + self.absent_vs_ordered weights[(d, c)] += weight if (self.unordered_vs_unordered is not None or self.unordered_vs_absent is not None or self.absent_vs_unordered is not None): unordered_as_list = list(ballot.candidates_not_in_b) for i, c in enumerate(unordered_as_list): # Deal with other unordered candidates if self.unordered_vs_unordered is not None: for d in unordered_as_list[i + 1:]: gross[(c, d)] += weight * self.unordered_vs_unordered gross[(d, c)] += weight * self.unordered_vs_unordered weights[(c, d)] += weight weights[(d, c)] += weight # Deal with absent candidates for d in absent: if self.unordered_vs_absent is not None: gross[(c, d)] += weight * self.unordered_vs_absent weights[(c, d)] += weight if self.absent_vs_unordered is not None: gross[(d, c)] += weight * self.absent_vs_unordered weights[(d, c)] += weight if self.absent_vs_absent is not None: absent_as_list = list(absent) for i, c in enumerate(absent_as_list): for d in absent_as_list[i + 1:]: gross[(c, d)] += weight * self.absent_vs_absent gross[(d, c)] += weight * self.absent_vs_absent weights[(c, d)] += weight weights[(d, c)] += weight return {'gross': gross, 'weights': weights}
def as_dict_(self): return NiceDict({(c, d): self.as_array_[self.candidates_indexes_[c], self.candidates_indexes_[d]] for c in self.candidates_ for d in self.candidates_})
def scores_(self) -> NiceDict: m = self.matrix_ return NiceDict({c: sum([v for (i, j), v in m.as_dict_.items() if i == c and j != c]) for c in m.candidates_})