<jupyter_start><jupyter_text>Czech 2017 parliamentary electionsThis example shows proportional elections in the presence of multiple constituencies, where seats are allocated to constituencies on-line by the number of votes cast in them.<jupyter_code>import sys
import os
import csv
import decimal

sys.path.append(os.path.join('..', '..'))
import votelib.candidate
import votelib.evaluate.core
import votelib.evaluate.threshold
import votelib.evaluate.proportional<jupyter_output><empty_output><jupyter_text>Evaluator constructionFirst, we construct the evaluator.Czechia uses the d'Hondt highest averages proportional system in fourteen regions that form separate constituencies with separate regional lists. There is a national 5% electoral threshold.<jupyter_code>eliminator = votelib.evaluate.threshold.RelativeThreshold(
    decimal.Decimal('.05'), accept_equal=True
)
regional_evaluator = votelib.evaluate.proportional.HighestAverages('d_hondt')<jupyter_output><empty_output><jupyter_text>The number of seats for each region is determined by the total number of votes cast in the region by the Hare quota from a total of 200 seats. The candidates elected are determined through open lists, but we will not go into that detail here.<jupyter_code>apportioner = votelib.evaluate.proportional.LargestRemainder('hare')
combined_evaluator = votelib.evaluate.core.ByConstituency(
    regional_evaluator, apportioner, preselector=eliminator
)
evaluator = votelib.evaluate.core.FixedSeatCount(
    votelib.evaluate.core.PostConverted(
        combined_evaluator,
        votelib.convert.VoteTotals(),
    ),
    200
)<jupyter_output><empty_output><jupyter_text>Vote loadingNow we load the vote counts, per region.<jupyter_code>fpath = os.path.join('..', '..', 'tests', 'real', 'data', 'cz_psp_2017.csv')
with open(fpath, encoding='utf8') as infile:
    rows = list(csv.reader(infile, delimiter=';'))
region_names = rows[0][1:]
votes = {region: {} for region in region_names}
for row in rows[1:]:
    party = row[0]
    for regname, n_votes in zip(region_names, row[1:]):
        votes[regname][party] = int(n_votes)
print(dict(sorted(votes['Královéhradecký kraj'].items(), key=lambda x: x[1], reverse=True)))<jupyter_output>{'ANO': 88551, 'ODS': 32242, 'Piráti': 29932, 'SPD': 28038, 'KSČM': 19792, 'ČSSD': 18128, 'KDU-ČSL': 16294, 'TOP 09': 14308, 'STAN': 14184, 'Svobodní': 4310, 'Zelení': 3650, 'Rozumní': 2388, 'REAL': 2037, 'SPO': 851, 'SPRRSČ M.Sládka': 619, 'DSSS': 600, 'ŘN - VU': 531, 'SPORTOVCI': 431, 'DV 2016': 380, 'ODA': 322, 'CESTA': 297, 'BPI': 284, 'Referendum o EU': 276, 'RČ': 275, 'PB': 0, 'SPDV': 0, 'OBČANÉ 2011': 0, 'Unie H.A.V.E.L.': 0, 'ČNF': 0, 'ČSNS': 0, 'NáS': 0}<jupyter_text>Performing the evaluationWhen the evaluator is set up correctly, obtaining the result is simple.<jupyter_code>print(evaluator.evaluate(votes))<jupyter_output>{'ANO': 78, 'Piráti': 22, 'ODS': 25, 'TOP 09': 7, 'SPD': 22, 'ČSSD': 15, 'STAN': 6, 'KDU-ČSL': 10, 'KSČM': 15}<jupyter_text>We can also check the results for individual regions by taking the outputs of `combined_evaluator`.<jupyter_code>print(combined_evaluator.evaluate(votes, 200)['Královéhradecký kraj'])<jupyter_output>{'ANO': 5, 'ODS': 1, 'Piráti': 1, 'SPD': 1, 'KSČM': 1, 'ČSSD': 1, 'KDU-ČSL': 1}<jupyter_text>Alternative systemsIn this section, we examine the results that would be achieved under different alternative voting systems.  Sainte-Laguë divisorUsing the Sainte-Laguë divisor helps smaller and mid-sized parties that d'Hondt tends to disfavor; however, the small size of some regions will still impact them. We construct the evaluator analogously, just using the different divisor.<jupyter_code>sainte_lague_evaluator = votelib.evaluate.core.PostConverted(
    votelib.evaluate.core.ByConstituency(
        votelib.evaluate.proportional.HighestAverages('sainte_lague'),
        apportioner,
        preselector=eliminator
    ),
    votelib.convert.VoteTotals(),
)
print(sainte_lague_evaluator.evaluate(votes, 200))<jupyter_output>{'ANO': 63, 'Piráti': 22, 'ODS': 24, 'TOP 09': 11, 'SPD': 23, 'ČSSD': 16, 'STAN': 14, 'KDU-ČSL': 11, 'KSČM': 16}<jupyter_text>Abolishing the electoral thresholdIf we abolish the electoral threshold and keep the d'Hondt divisor, nothing happens in this particular case - no party apart from those over 5% is strong enough to get hold of a single seat. (Under Sainte-Laguë, Zelení and Svobodní would each get a single seat.)<jupyter_code>no_threshold_evaluator = votelib.evaluate.core.PostConverted(
    votelib.evaluate.core.ByConstituency(
        votelib.evaluate.proportional.HighestAverages('d_hondt'),
        apportioner,
    ),
    votelib.convert.VoteTotals(),
)
print(no_threshold_evaluator.evaluate(votes, 200))<jupyter_output>{'ANO': 78, 'Piráti': 22, 'ODS': 25, 'TOP 09': 7, 'SPD': 22, 'ČSSD': 15, 'STAN': 6, 'KDU-ČSL': 10, 'KSČM': 15}<jupyter_text>Biproportional apportionmentApplying biproportional apportionment results in a similar result to applying the Sainte-Laguë divisor in this case even with the d'Hondt divisor, since we honor nationwide party vote fractions.Since `BiproportionalEvaluator` removes the need for `ByConstituency`, if we want to keep the electoral threshold, we need to introduce it using a separate `Conditioned` composite .<jupyter_code>biprop_evaluator = votelib.evaluate.core.PostConverted(
    votelib.evaluate.Conditioned(
        votelib.evaluate.PreConverted(votelib.convert.VoteTotals(), eliminator),
        votelib.evaluate.proportional.BiproportionalEvaluator('d_hondt'),
        # depth=1 because the votes are nested by region and we want to subset parties
        subsetter=votelib.convert.SubsettedVotes(depth=1),
    ),
    votelib.convert.VoteTotals(),
)
print(biprop_evaluator.evaluate(votes, 200))<jupyter_output>{'ANO': 64, 'ODS': 24, 'Piráti': 23, 'SPD': 23, 'KSČM': 17, 'ČSSD': 15, 'KDU-ČSL': 12, 'TOP 09': 11, 'STAN': 11}
{'fraction_non_alphanumeric': 0.12540302053283556, 'fraction_numerical': 0.039538435431868316, 'mean_word_length': 5.697727272727272, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '9', 'dir': 'jupyter-structured-clean-dedup', 'id': '212616', 'license': 'MIT', 'n_tokens_mistral': 2318, 'n_tokens_neox': 2101, 'n_words': 560, 'path': 'docs/examples/cz_psp_2017.ipynb', 'repo_name': 'simberaj/votelib', 'size': '5893'}