Tutorial 6: References

In this tutorial we will learn how to re-use data from other nodes using references.

Spain - General elections - Parliament - Full process

The elections for the National Parliament requires two steps:

Now we will compose the full schema:

We now have to sets of votes:

  • census aggregated by province

  • election vote to party lists

  1. Census aggregated by province: we just need to copy the schema from the previous tutorial (Spain - National Parliament - Districts apportionment) and make some adjustments:

     1name: Elecciones a Cortes Generales
     2type: group
     3divisions:
     4
     5- name: Distribución de escaños
     6  type: group
     7  aggregate: yes
     8  meta:
     9    notes: |
    10        Seats apportionment based on provintial census
    11  divisions:
    12  - name: Ciudades autónomas
    13    method: noop
    14    initial_seats: 1
    15  - name: Provincias
    16    seats: 248
    17    method: largest_remainder
    18    method_params:
    19      quota_f: hare
    20    initial_seats: 2
    21    resume_allocation: no
    

    In line 7 we enable the generation of a result, so that we can have a flattened list for both autonomous cities and provinces.

    In line 8-9, a comment was added. Custom properties can be added to a district using the meta container.

  2. Election vote to party lists: a schema similar to the one we used for Spain - Andalusian regional election will be addded, but we will use references to the seats computed by the node Distribución de escaños:

     1name: Elecciones a Cortes Generales
     2type: group
     3divisions:
     4
     5- name: Distribución de escaños
     6  type: group
     7  aggregate: yes
     8  meta:
     9    notes: |
    10        Seats apportionment based on provintial census
    11  divisions:
    12  - name: Ciudades autónomas
    13    method: noop
    14    initial_seats: 1
    15  - name: Provincias
    16    seats: 248
    17    method: largest_remainder
    18    method_params:
    19      quota_f: hare
    20    initial_seats: 2
    21
    22- name: Circunscripciones
    23  type: group
    24  aggregate: yes
    25  method: highest_averages
    26  method_params:
    27    divisor_f: dhondt
    28  exclude: valid_votes < 3%
    29  meta:
    30    notes: |
    31        Seats allocation based on votes
    32  divisions:
    33  - name: Andalucía
    34    type: group
    35    aggregate: yes
    36    divisions:
    37    - name: Almería
    38      seats: Distribución de escaños
    39    - name: Cádiz
    40      seats: Distribución de escaños
    41    - name: Córdoba
    42      seats: Distribución de escaños
    43    - name: Granada
    44      seats: Distribución de escaños
    45    - name: Huelva
    46      seats: Distribución de escaños
    47    - name: Jaén
    48      seats: Distribución de escaños
    49    - name: Málaga
    50      seats: Distribución de escaños
    51    - name: Sevilla
    52      seats: Distribución de escaños
    53  ...
    54  - name: País Vasco
    55    type: group
    56    aggregate: yes
    57    divisions:
    58    - name: Araba/Álava
    59      seats: Distribución de escaños
    60    - name: Bizkaia
    61      seats: Distribución de escaños
    62    - name: Gipuzkoa
    63      seats: Distribución de escaños
    

    There are two differences from the old version:

    • An intermediate group level was added in order to get results aggregated by autonomous regions.

    • The seats for provinces are no longed hard-coded. Now they are using the values computed by the node Distribución de escaños. If a candidate with the same name as the district who references is found, the computed seats will be used.

Zurich - Bi-proportional system

We will use more references defining a new kind of method, the bi-proportional allocation ([Zachariasen:2006], [Pukelsheim:2013], [Oelbermann:2016])

In 2006, the Canton of Zürich 125 seats were allocated among 9 districts keeping two proportionalities:

  • The number of seats assigned to districts is proportional.

  • The number of seats assigned to parties is also proportional.

  • Seats are assigned to parties in district keeping the restrictions above.

In the first step party seats and district seats are computed for the whole Canton. Then, seats are reapportioned to parties in districts in a single allocation process using the Sainte-Laguë divisor method.

 1name: Zürich Canton Parliament 2006
 2type: group
 3divisions:
 4- name: biproportional
 5  method: alternate_scaling
 6  method_params:
 7      round_f: sainte_lague
 8      nice_quota: yes
 9      sort_parties: yes
10  candidates: Wahlkreise
11  party_seats:
12      name: party seats
13      method: highest_averages
14      method_params:
15        divisor_f: sainte_lague
16      seats: 125
17      candidates: id[Wahlkreise]
18  district_seats:
19      method: noop
20      candidates:
21      - {name: "WK1+2", min_seats: 12}
22      - {name: "WK3", min_seats: 16}
23      - {name: "WK4+5", min_seats: 13}
24      - {name: "WK6", min_seats: 10}
25      - {name: "WK7+8", min_seats: 17}
26      - {name: "WK9", min_seats: 16}
27      - {name: "WK10", min_seats: 12}
28      - {name: "WK11", min_seats: 19}
29      - {name: "WK12", min_seats: 10}
30
31- name: Wahlkreise
32  method: noop
33  candidates:
34  - { district: WK1+2, name: SP, votes: 28518 }
35  - { district: WK1+2, name: SVP, votes: 15305 }
36  - { district: WK1+2, name: FDP, votes: 21833 }
37  - { district: WK1+2, name: Grüne, votes: 12401 }
38  - { district: WK1+2, name: CVP, votes: 7318 }
39  - { district: WK1+2, name: EVP, votes: 2829 }
40  - { district: WK1+2, name: AL, votes: 2413 }
41  - { district: WK1+2, name: SD, votes: 1651 }
42
43  ...
44
45  - { district: WK12, name: SP, votes: 13215 }
46  - { district: WK12, name: SVP, votes: 10248 }
47  - { district: WK12, name: FDP, votes: 3066 }
48  - { district: WK12, name: Grüne, votes: 2187 }
49  - { district: WK12, name: CVP, votes: 4941 }
50  - { district: WK12, name: EVP, votes: 0 }
51  - { district: WK12, name: AL, votes: 429 }
52  - { district: WK12, name: SD, votes: 2078 }
  1. Create the electoral districts:

    31- name: Wahlkreise
    32  method: noop
    33  candidates:
    34  - { district: WK1+2, name: SP, votes: 28518 }
    35  - { district: WK1+2, name: SVP, votes: 15305 }
    36  - { district: WK1+2, name: FDP, votes: 21833 }
    37  - { district: WK1+2, name: Grüne, votes: 12401 }
    38  - { district: WK1+2, name: CVP, votes: 7318 }
    39  - { district: WK1+2, name: EVP, votes: 2829 }
    40  - { district: WK1+2, name: AL, votes: 2413 }
    41  - { district: WK1+2, name: SD, votes: 1651 }
    42
    43  ...
    44
    45  - { district: WK12, name: SP, votes: 13215 }
    46  - { district: WK12, name: SVP, votes: 10248 }
    47  - { district: WK12, name: FDP, votes: 3066 }
    48  - { district: WK12, name: Grüne, votes: 2187 }
    49  - { district: WK12, name: CVP, votes: 4941 }
    50  - { district: WK12, name: EVP, votes: 0 }
    51  - { district: WK12, name: AL, votes: 429 }
    52  - { district: WK12, name: SD, votes: 2078 }
    

    This node will be a simple container, with no allocation per se. Note that the field district is specified explicitly for contenders. This is the equivalent of this hierarchical schema:

    31- name: Wahlkreise
    32  type: group
    33  method: noop
    34  divisions:
    35  - name: WK1+2
    36    candidates:
    37    - { name: SP, votes: 28518 }
    38    - { name: SVP, votes: 15305 }
    39    - { name: FDP, votes: 21833 }
    40    - { name: Grüne, votes: 12401 }
    41    - { name: CVP, votes: 7318 }
    42    - { name: EVP, votes: 2829 }
    43    - { name: AL, votes: 2413 }
    44    - { name: SD, votes: 1651 }
    45
    46  ...
    47
    48  - name: WK12
    49    candidates:
    50    - { name: SP, votes: 13215 }
    51    - { name: SVP, votes: 10248 }
    52    - { name: FDP, votes: 3066 }
    53    - { name: Grüne, votes: 2187 }
    54    - { name: CVP, votes: 4941 }
    55    - { name: EVP, votes: 0 }
    56    - { name: AL, votes: 429 }
    57    - { name: SD, votes: 2078 }
    
  2. Create a node for district seats. This is not computed here:

    18district_seats:
    19    method: noop
    20    candidates:
    21    - {name: "WK1+2", min_seats: 12}
    22    - {name: "WK3", min_seats: 16}
    23    - {name: "WK4+5", min_seats: 13}
    24    - {name: "WK6", min_seats: 10}
    25    - {name: "WK7+8", min_seats: 17}
    26    - {name: "WK9", min_seats: 16}
    27    - {name: "WK10", min_seats: 12}
    28    - {name: "WK11", min_seats: 19}
    29    - {name: "WK12", min_seats: 10}
    

Seats are set using min_seats.

  1. Allocate seats to parties:

    11party_seats:
    12    name: party seats
    13    method: highest_averages
    14    method_params:
    15        divisor_f: sainte_lague
    16    seats: 125
    17    candidates: id[Wahlkreise]
    

Note that instead of giving a list of candidates, a reference is used: id[Wahlkreise]. This will get the Wahlkreise node’s candidates and will apply a transformation by id (it will remove districts).

  1. Allocate bi-proportionally:

     4- name: biproportional
     5  method: alternate_scaling
     6  method_params:
     7    round_f: sainte_lague
     8    nice_quota: yes
     9    sort_parties: yes
    10  candidates: Wahlkreise
    11  party_seats:
    12    ...
    13  district_seats:
    14    ...
    

    It will use the alternate scaling method, and the candidates will be retrieved from Wahlkreise again, but no transformations are made (name, alliance and district is preserved).