Czech municipal elections 2018: Nové Město nad Metují

Czech municipal elections use an open-list proportional system that allows panachage - marking candidates across parties. Here, we give an example of how to evaluate such a composite election system.

import sys
import os
import csv
import decimal

sys.path.append(os.path.join('..', '..'))
import votelib.candidate
import votelib.convert
import votelib.evaluate.core
import votelib.evaluate.threshold
import votelib.evaluate.proportional
import votelib.evaluate.openlist

Vote loading

The voters are given a huge ballot where they can vote for arbitrary candidates across parties, with the number of votes equal to the number of seats in the municipal council. In addition to this, they can also vote for a party, whose candidates obtain the votes not assigned to candidates elsewhere, counting from the top. The votes are thus counted for the candidates and can be aggregated to the parties.

We use the Person and PoliticalParty object to determine the relationships of candidates to their parties and aggregate their votes accordingly later.

fpath = os.path.join('..', '..', 'tests', 'real', 'data', 'nmnmet_cc_2018.csv')
votes = {}
party_objs = {}
party_lists = {}
with open(fpath, encoding='utf8') as infile:
    for party, name, n_pers_votes in csv.reader(infile, delimiter=';'):
        # For each candidate: Get the according party object;
        party_obj = party_objs.setdefault(party, votelib.candidate.PoliticalParty(party))
        # Construct the person object with a reference to the party;
        person = votelib.candidate.Person(name, candidacy_for=party_obj)
        # Record the candidate's votes;
        votes[person] = int(n_pers_votes)
        # Append the candidate to the party list of his or her party.
        party_lists.setdefault(party_obj, []).append(person)

An example of the votes and party list for the incumbent ruling party:

vpm_object = party_objs['VPM']
print([ for cand in party_lists[vpm_object]])
{ n_votes for cand, n_votes in votes.items() if cand.candidacy_for == vpm_object}
['Hable Petr', 'Beseda Michal Ing. MBA', 'Maur Vilém Ing. MBA', 'Němeček Jan Ing.', 'Petruželková Marie', 'Hladík Jiří', 'Neumann Jan Ing.', 'Novotný Jiří', 'Prouza Radek', 'Hrnčíř Pavel', 'Bureš Michal Mgr.', 'Vlček Petr Mgr. A.', 'Rydlová Hana Mgr.', 'Minařík Jan Mgr.', 'Zimlová Věra', 'Roštlapil Tomáš Ing.', 'Krákorová Andrea Mgr.', 'Vintera Miroslav Ing.', 'Mach Martin Ing.', 'Reichmann David', 'Volf Martin Mgr.']
{'Hable Petr': 1479,
 'Beseda Michal Ing. MBA': 962,
 'Maur Vilém Ing. MBA': 1235,
 'Němeček Jan Ing.': 1013,
 'Petruželková Marie': 1053,
 'Hladík Jiří': 1200,
 'Neumann Jan Ing.': 1005,
 'Novotný Jiří': 808,
 'Prouza Radek': 1084,
 'Hrnčíř Pavel': 722,
 'Bureš Michal Mgr.': 793,
 'Vlček Petr Mgr. A.': 770,
 'Rydlová Hana Mgr.': 815,
 'Minařík Jan Mgr.': 770,
 'Zimlová Věra': 777,
 'Roštlapil Tomáš Ing.': 1006,
 'Krákorová Andrea Mgr.': 828,
 'Vintera Miroslav Ing.': 682,
 'Mach Martin Ing.': 664,
 'Reichmann David': 651,
 'Volf Martin Mgr.': 835}

Evaluator construction

Each Czech municipality forms a single constituency for the election.

The evaluation proceeds by first evaluating party results, so the results for the individual candidates must be grouped by their party. This mapping is defined by the candidates’ candidacy_for attribute, which is recognized by the IndividualToPartyMapper object by default. Because independent candidates are not allowed to stand in the election, we add the behavior to recognize them as errors:

vote_grouper = votelib.convert.GroupVotesByParty(

The seats are allocated to the parties by the proportional D’Hondt system with a 5 % municipal vote threshold. We thus construct the proportional evaluator conditioned by the vote threshold and pre-aggregated by summing the grouped votes for parties:

party_evaluator = votelib.evaluate.core.PreConverted(
            decimal.Decimal('.05'), accept_equal=True

Next, the party open lists are evaluated by the votes for their individual candidates. The candidate can advance forward in the list ranking if he or she has more than 5 % of the votes for the list; all such candidates are ranked first by the number of votes in descending order, and the rest goes after them in list order. We can use PartyListEvaluator to manage the list election and have ThresholdOpenList determine the elected candidates for each party. We use the vote grouper in two places to group both the party votes and list votes, which are passed separately:

list_evaluator = votelib.evaluate.core.PreConverted(

Finally, we fix the number of seats - the municipal council of Nové Město nad Metují has 21 seats:

evaluator = votelib.evaluate.core.FixedSeatCount(
    list_evaluator, 21

Performing the evaluation

With the evaluator set up, we obtain the evaluation as lists of candidates per party.

list_results = evaluator.evaluate(
for party, mandates in list_results.items():
    print(, ', '.join([ for cand in mandates]))
VPM             Hable Petr, Maur Vilém Ing. MBA, Hladík Jiří, Prouza Radek, Petruželková Marie, Němeček Jan Ing.
ODS             Hovorka Libor Ing., Kupková Irena Mgr., Slavík Milan Ing., Jarolímek Miroslav
NM              Sláma Jiří Bc., Žahourková Markéta, Paarová Soňa Mgr.
VČ              Tymel Jiří Ing., Prouza Martin Ing., Balcarová Jana Mgr.
KDU-ČSL         Hylský Josef Mgr., Neumann Petr Ing., Dostál Pavel Ing. et Ing.
ČSSD            Čopík Jan Ing. Ph.D.
KSČM            Kulhavá Zdeňka PhDr.

We can see that VPM, the incumbent ruling party, has defended its first place with six seats, but its second place candidate from the original list was not elected because other candidates from the party with more votes jumped over him during open list evaluation.