Analiza 2019¶

Prezentacja faktycznego i hipotetycznego podziału mandatów w wyborach do Sejmu 2019.

© Patryk Czarnik

Operacje wstępne¶

Skrypt korzysta z biblioteki Pandas.

In [1]:
import pandas as pd
import numpy as np
In [2]:
#pd.set_option('display.float_format', '{:.2f}'.format)
In [3]:
path = 'dane/2019/robocze/'
In [4]:
MANDATY_SEJM = 460

Wczytanie danych¶

Dane pochodzą z serwisu wybory.gov.pl i odpowiadają oficjalnym wynikom wyborów. Korzystam z plików wstępnie przygotowanych przez skrypt przygotuj_dane_2019.

In [5]:
def_kom = pd.read_csv(path + 'def_kom_2019.csv', sep=';', index_col=0)
def_okr = pd.read_csv(path + 'def_okr_2019.csv', sep=';', index_col=0)
wyn_okr = pd.read_csv(path + 'kom_okr_2019.csv', sep=';', index_col=0)
wyn_okr.columns = wyn_okr.columns.astype('int')
wyn_okr.columns.name = 'Nr listy'
stat_okr = pd.read_csv(path + 'stat_okr_2019.csv', sep=';', index_col=0)
In [6]:
def_kom
Out[6]:
Nazwa pełna Nazwa Skrót Próg
Nr listy
1 KOMITET WYBORCZY POLSKIE STRONNICTWO LUDOWE POLSKIE STRONNICTWO LUDOWE PSL 5.0
2 KOMITET WYBORCZY PRAWO I SPRAWIEDLIWOŚĆ PRAWO I SPRAWIEDLIWOŚĆ PiS 5.0
3 KOMITET WYBORCZY SOJUSZ LEWICY DEMOKRATYCZNEJ SOJUSZ LEWICY DEMOKRATYCZNEJ Lewica 5.0
4 KOMITET WYBORCZY KONFEDERACJA WOLNOŚĆ I NIEPOD... KONFEDERACJA WOLNOŚĆ I NIEPODLEGŁOŚĆ Konfederacja 5.0
5 KOALICYJNY KOMITET WYBORCZY KOALICJA OBYWATELS... KOALICJA OBYWATELSKA PO .N IPL ZIELONI KO 8.0
6 KOMITET WYBORCZY PRAWICA PRAWICA KW Prawica 5.0
7 KOMITET WYBORCZY AKCJA ZAWIEDZIONYCH EMERYTÓW ... AKCJA ZAWIEDZIONYCH EMERYTÓW RENCISTÓW AZER 5.0
8 KOMITET WYBORCZY WYBORCÓW KOALICJA BEZPARTYJNI... KOALICJA BEZPARTYJNI I SAMORZĄDOWCY BS 5.0
9 KOMITET WYBORCZY SKUTECZNI PIOTRA LIROYA-MARCA SKUTECZNI PIOTRA LIROYA-MARCA Liroy 5.0
10 KOMITET WYBORCZY WYBORCÓW MNIEJSZOŚĆ NIEMIECKA MNIEJSZOŚĆ NIEMIECKA MN 0.0
In [7]:
map_kom_skrot = dict(def_kom["Skrót"])
In [8]:
def_okr
Out[8]:
Okręg Liczba mandatów
Nr okręgu
1 Legnica 12
2 Wałbrzych 8
3 Wrocław 14
4 Bydgoszcz 12
5 Toruń 13
6 Lublin 15
7 Chełm 12
8 Zielona Góra 12
9 Łódź 10
10 Piotrków Trybunalski 9
11 Sieradz 12
12 Kraków 8
13 Kraków 14
14 Nowy Sącz 10
15 Tarnów 9
16 Płock 10
17 Radom 9
18 Siedlce 12
19 Warszawa 20
20 Warszawa 12
21 Opole 12
22 Krosno 11
23 Rzeszów 15
24 Białystok 14
25 Gdańsk 12
26 Słupsk 14
27 Bielsko-Biała 9
28 Częstochowa 7
29 Katowice 9
30 Bielsko-Biała 9
31 Katowice 12
32 Katowice 9
33 Kielce 16
34 Elbląg 8
35 Olsztyn 10
36 Kalisz 12
37 Konin 9
38 Piła 9
39 Poznań 10
40 Koszalin 8
41 Szczecin 12
In [9]:
map_okr = dict(def_okr["Okręg"])
In [10]:
assert def_okr["Liczba mandatów"].sum() == MANDATY_SEJM
In [11]:
wyn_okr
Out[11]:
Nr listy 1 2 3 4 5 6 7 8 9 10
Numer okręgu
1 31006 183364 71061 25319 108191 NaN NaN 13495.0 NaN NaN
2 20528 114728 34957 15346 90812 NaN NaN 6631.0 NaN NaN
3 42269 226915 100843 48775 214629 NaN NaN 21024.0 NaN NaN
4 41497 167550 69763 32406 142844 NaN NaN 5922.0 NaN NaN
5 49225 182648 67076 28625 119526 NaN NaN 5230.0 NaN NaN
6 51474 313284 44152 40012 109185 NaN NaN 7490.0 NaN NaN
7 47604 238802 27404 23439 59401 NaN NaN 4668.0 NaN NaN
8 50943 150188 68341 31490 136955 NaN NaN NaN NaN NaN
9 18828 136731 83524 27627 148830 NaN NaN NaN NaN NaN
10 36151 194658 37930 23427 54160 NaN NaN NaN NaN NaN
11 47373 229245 55116 27054 94268 NaN NaN 7183.0 NaN NaN
12 24996 169106 26909 22334 72869 NaN NaN NaN NaN NaN
13 47219 256847 84457 51855 197930 1765.0 NaN 9214.0 NaN NaN
14 27203 243583 22483 25747 51183 NaN NaN NaN NaN NaN
15 46333 206845 20618 24695 48597 NaN NaN NaN NaN NaN
16 56227 194371 32448 19405 62429 NaN NaN 5681.0 NaN NaN
17 34185 193709 24884 19724 57449 NaN NaN 2555.0 2503.0 NaN
18 54085 270641 29235 29390 63124 NaN 1412.0 5019.0 NaN NaN
19 65683 379880 251434 103843 581077 NaN NaN NaN NaN NaN
20 51484 244823 78348 39675 171286 NaN NaN 13111.0 NaN NaN
21 41901 152999 47699 23176 108570 NaN NaN NaN NaN 32094.0
22 30655 247488 23577 26615 62246 NaN NaN NaN NaN NaN
23 45868 367268 38817 48600 84703 NaN NaN NaN 3530.0 NaN
24 48566 270888 47342 36207 109527 NaN 1775.0 4001.0 2272.0 NaN
25 31203 169753 71236 38153 218484 NaN NaN NaN NaN NaN
26 46132 211582 72436 42364 208208 NaN NaN NaN NaN NaN
27 27752 182027 44701 28900 105876 NaN NaN NaN NaN NaN
28 24704 125990 44354 17278 64374 NaN NaN 7817.0 NaN NaN
29 20408 128579 45583 26114 111078 NaN NaN 8885.0 NaN NaN
30 18816 161160 32300 23939 92493 NaN NaN 5128.0 NaN NaN
31 20512 184030 55992 34416 174683 NaN NaN NaN NaN NaN
32 16263 124553 73466 21650 99499 NaN NaN NaN NaN NaN
33 56289 314455 56699 33895 94880 NaN NaN 5400.0 8273.0 NaN
34 27319 102478 29196 14187 71320 NaN NaN 6319.0 NaN NaN
35 43758 128760 45912 23134 87780 NaN NaN NaN 2340.0 NaN
36 58759 195053 61674 30177 113489 NaN NaN NaN NaN NaN
37 34620 166965 53090 23810 72295 NaN 2261.0 NaN NaN NaN
38 48371 124392 46355 23123 106810 NaN NaN NaN NaN NaN
39 31875 130319 84835 34024 233474 NaN NaN NaN NaN NaN
40 25632 100078 41943 16259 87799 NaN NaN NaN NaN NaN
41 34807 165200 71756 30744 168022 NaN NaN NaN NaN NaN

Wyniki w skali kraju i przekroczenie progu¶

In [12]:
kraj_liczbowo = wyn_okr.agg('sum').astype('int')
kraj_liczbowo.index = kraj_liczbowo.index

Surowa liczba głosów oddanych na poszczególne komitety wyborcze.

In [13]:
kraj_liczbowo
Out[13]:
Nr listy
1     1578523
2     8051935
3     2319946
4     1256953
5     5060355
6        1765
7        5448
8      144773
9       18918
10      32094
dtype: int64
In [14]:
suma_wszystkich_glosow = kraj_liczbowo.sum()
In [15]:
suma_wszystkich_glosow
Out[15]:
18470710

W wyborach do Sejmu w 2019 oddano łącznie {{suma_wszystkich_glosow}} 18.5 mln ważnych głosów.

In [16]:
procenty = 100 * kraj_liczbowo / suma_wszystkich_glosow
kraj = pd.DataFrame({
    "Skrót": def_kom["Skrót"],
    "Liczba głosów": kraj_liczbowo,
    "Procent ważnych głosów": procenty,
    "Próg": def_kom["Próg"],
    "Powyżej progu": procenty >= def_kom["Próg"],
})

Wyniki procentowe wszystkich komitetów¶

w skali całago kraju oraz informacja czy przekroczono próg wyborczy określony dla danego rodzaju komitetu.

In [17]:
kraj.round(2)
Out[17]:
Skrót Liczba głosów Procent ważnych głosów Próg Powyżej progu
Nr listy
1 PSL 1578523 8.55 5.0 True
2 PiS 8051935 43.59 5.0 True
3 Lewica 2319946 12.56 5.0 True
4 Konfederacja 1256953 6.81 5.0 True
5 KO 5060355 27.40 8.0 True
6 KW Prawica 1765 0.01 5.0 False
7 AZER 5448 0.03 5.0 False
8 BS 144773 0.78 5.0 False
9 Liroy 18918 0.10 5.0 False
10 MN 32094 0.17 0.0 True

Komitety, które uzyskały wynik powyżej progu i będą uwzględniane w podziale mandatów.

In [18]:
kraj_pow = kraj[kraj["Powyżej progu"]].drop(columns=["Próg", "Powyżej progu"])
kom_pow_idx = kraj_pow.index
In [19]:
kraj_pow["Procent dzielonych głosów"] = 100 * kraj_pow["Liczba głosów"] / kraj_pow["Liczba głosów"].sum()
kraj_pow["Mandaty proporcjonalnie"] = round(MANDATY_SEJM * kraj_pow["Procent dzielonych głosów"] / 100, 1)

Dodaję kolumnę zawierającą wynik procentowy wśród komitetów, które przekroczyły próg. Teoretycznie procentowy podział mandatów powinien być zbliżony do tej liczby.

Gdyby podział w skali kraju był proporcjonalny, komitety powinny otrzymać liczbę mandatów obliczoną w ostatniej kolumnie.

In [20]:
kraj_pow.round(2).sort_values("Liczba głosów", ascending=False)
Out[20]:
Skrót Liczba głosów Procent ważnych głosów Procent dzielonych głosów Mandaty proporcjonalnie
Nr listy
2 PiS 8051935 43.59 44.00 202.4
5 KO 5060355 27.40 27.65 127.2
3 Lewica 2319946 12.56 12.68 58.3
1 PSL 1578523 8.55 8.63 39.7
4 Konfederacja 1256953 6.81 6.87 31.6
10 MN 32094 0.17 0.18 0.8

Faktyczny podział mandatów¶

Podział mandatów obliczony zgodnie z obowiązującymi zasadami: niezależnie w każdym okręgu.

Odrzucenie komitetów nieprzekraczających progu¶

W wynikach per okręg uwzględniam tylko te komitety, które przekroczyły próg.

In [21]:
wyn_okr_pow = wyn_okr[kom_pow_idx].copy()
In [22]:
wyn_okr_pow
Out[22]:
Nr listy 1 2 3 4 5 10
Numer okręgu
1 31006 183364 71061 25319 108191 NaN
2 20528 114728 34957 15346 90812 NaN
3 42269 226915 100843 48775 214629 NaN
4 41497 167550 69763 32406 142844 NaN
5 49225 182648 67076 28625 119526 NaN
6 51474 313284 44152 40012 109185 NaN
7 47604 238802 27404 23439 59401 NaN
8 50943 150188 68341 31490 136955 NaN
9 18828 136731 83524 27627 148830 NaN
10 36151 194658 37930 23427 54160 NaN
11 47373 229245 55116 27054 94268 NaN
12 24996 169106 26909 22334 72869 NaN
13 47219 256847 84457 51855 197930 NaN
14 27203 243583 22483 25747 51183 NaN
15 46333 206845 20618 24695 48597 NaN
16 56227 194371 32448 19405 62429 NaN
17 34185 193709 24884 19724 57449 NaN
18 54085 270641 29235 29390 63124 NaN
19 65683 379880 251434 103843 581077 NaN
20 51484 244823 78348 39675 171286 NaN
21 41901 152999 47699 23176 108570 32094.0
22 30655 247488 23577 26615 62246 NaN
23 45868 367268 38817 48600 84703 NaN
24 48566 270888 47342 36207 109527 NaN
25 31203 169753 71236 38153 218484 NaN
26 46132 211582 72436 42364 208208 NaN
27 27752 182027 44701 28900 105876 NaN
28 24704 125990 44354 17278 64374 NaN
29 20408 128579 45583 26114 111078 NaN
30 18816 161160 32300 23939 92493 NaN
31 20512 184030 55992 34416 174683 NaN
32 16263 124553 73466 21650 99499 NaN
33 56289 314455 56699 33895 94880 NaN
34 27319 102478 29196 14187 71320 NaN
35 43758 128760 45912 23134 87780 NaN
36 58759 195053 61674 30177 113489 NaN
37 34620 166965 53090 23810 72295 NaN
38 48371 124392 46355 23123 106810 NaN
39 31875 130319 84835 34024 233474 NaN
40 25632 100078 41943 16259 87799 NaN
41 34807 165200 71756 30744 168022 NaN

Metoda d'Honta¶

Definicja funkcji obliczającej liczbę mandatów zgodnie z metodą d'Honta.

In [23]:
def dhont_podzial_liczbowy(liczba_mandatow, wyniki):
    ilorazy = [(nr, dzielnik, wynik/dzielnik)
               for nr, wynik in enumerate(wyniki)
               for dzielnik in range(1, liczba_mandatow+1)]
    posortowane_ilorazy = sorted(ilorazy, key=(lambda t: t[2]), reverse=True)
    mandaty = [0 for w in wyniki]
    for (nr, _, _) in posortowane_ilorazy[:liczba_mandatow]:
        mandaty[nr] += 1
    return mandaty

Zastosowanie metody d'Honta w poszczególnych okręgach.

In [24]:
mandaty_surowe_okr = pd.DataFrame([
        dhont_podzial_liczbowy(def_okr.loc[nr_okr, "Liczba mandatów"], row)
        for nr_okr, row in wyn_okr_pow.iterrows()
    ],
    index=wyn_okr_pow.index,
    columns = wyn_okr_pow.columns)
In [25]:
# mandaty_surowe_okr
In [26]:
mandaty_okr = def_okr.copy()
for kom in kom_pow_idx:
    mandaty_okr[kom] = mandaty_surowe_okr[kom]
In [27]:
mandaty_okr_prezentacja = mandaty_okr.rename(columns = map_kom_skrot)

Prawidzwe wyniki¶

Podział mandatów między komitety w poszcególnych okręgach

In [28]:
mandaty_okr_prezentacja
Out[28]:
Okręg Liczba mandatów PSL PiS Lewica Konfederacja KO MN
Nr okręgu
1 Legnica 12 1 6 2 0 3 0
2 Wałbrzych 8 0 4 1 0 3 0
3 Wrocław 14 1 5 2 1 5 0
4 Bydgoszcz 12 1 5 2 0 4 0
5 Toruń 13 1 6 2 0 4 0
6 Lublin 15 1 9 1 1 3 0
7 Chełm 12 1 8 1 0 2 0
8 Zielona Góra 12 1 4 2 1 4 0
9 Łódź 10 0 4 2 0 4 0
10 Piotrków Trybunalski 9 1 6 1 0 1 0
11 Sieradz 12 1 7 1 0 3 0
12 Kraków 8 0 6 0 0 2 0
13 Kraków 14 1 6 2 1 4 0
14 Nowy Sącz 10 1 8 0 0 1 0
15 Tarnów 9 1 7 0 0 1 0
16 Płock 10 1 6 1 0 2 0
17 Radom 9 1 6 0 0 2 0
18 Siedlce 12 1 9 0 0 2 0
19 Warszawa 20 1 6 3 1 9 0
20 Warszawa 12 1 6 1 0 4 0
21 Opole 12 1 5 1 0 4 1
22 Krosno 11 1 8 0 0 2 0
23 Rzeszów 15 1 10 1 1 2 0
24 Białystok 14 1 8 1 1 3 0
25 Gdańsk 12 0 4 1 1 6 0
26 Słupsk 14 1 5 2 1 5 0
27 Bielsko-Biała 9 0 5 1 0 3 0
28 Częstochowa 7 0 4 1 0 2 0
29 Katowice 9 0 4 1 0 4 0
30 Bielsko-Biała 9 0 5 1 0 3 0
31 Katowice 12 0 5 1 1 5 0
32 Katowice 9 0 4 2 0 3 0
33 Kielce 16 1 10 1 1 3 0
34 Elbląg 8 1 4 1 0 2 0
35 Olsztyn 10 1 5 1 0 3 0
36 Kalisz 12 1 6 2 0 3 0
37 Konin 9 1 5 1 0 2 0
38 Piła 9 1 4 1 0 3 0
39 Poznań 10 0 3 2 0 5 0
40 Koszalin 8 1 3 1 0 3 0
41 Szczecin 12 1 4 2 0 5 0

Podsumowanie krajowe¶

In [29]:
mandaty_kraj = mandaty_okr.iloc[:, 2:].agg('sum')
In [30]:
mandaty_kraj
Out[30]:
1      30
2     235
3      49
4      11
5     134
10      1
dtype: int64
In [31]:
mandaty_kraj_prezentacja = pd.DataFrame({
    "Skrót": kraj_pow["Skrót"],
    "Przyznanych mandatów": mandaty_kraj
}).sort_values("Przyznanych mandatów", ascending=False)
In [32]:
mandaty_kraj_prezentacja
Out[32]:
Skrót Przyznanych mandatów
2 PiS 235
5 KO 134
3 Lewica 49
1 PSL 30
4 Konfederacja 11
10 MN 1
In [33]:
mandaty_kraj_prezentacja.plot(kind='pie',
      figsize=(8,6),
      title='Faktyczny podział mandatów',
      y="Przyznanych mandatów",
      labels=[f'{komitet}: {mandaty}' for i, (komitet, mandaty) in mandaty_kraj_prezentacja.iterrows()])
Out[33]:
<Axes: title={'center': 'Faktyczny podział mandatów'}, ylabel='Przyznanych mandatów'>
No description has been provided for this image
In [34]:
assert mandaty_kraj.sum() == MANDATY_SEJM

Globalny podział mandatów¶

Gdyby podział liczby mandatów między komitety dokonywany był w skali kraju, liczba mandatów znacznie bliżej odpowiadałaby podziałowi proporcjonalnemu, a każdy głos wyborcy ważyłby tyle samo, niezależnie od okręgu, w którym głosuje.

Dokonam podziału mandatów metodą d'Honta na podstawie liczby głosów oddanej na poszczególne komitety w skali całego kraju.

In [35]:
mandaty_globalne = pd.Series(
    dhont_podzial_liczbowy(MANDATY_SEJM, kraj_pow["Liczba głosów"]),
    index = kom_pow_idx)
In [36]:
mandaty_globalne
Out[36]:
Nr listy
1      39
2     204
3      58
4      31
5     128
10      0
dtype: int64
In [37]:
mandaty_globalne_prezentacja = pd.DataFrame({
    "Skrót": kraj_pow["Skrót"],
    "Przyznanych mandatów": mandaty_globalne
}).sort_values("Przyznanych mandatów", ascending=False)
In [38]:
mandaty_globalne_prezentacja
Out[38]:
Skrót Przyznanych mandatów
Nr listy
2 PiS 204
5 KO 128
3 Lewica 58
1 PSL 39
4 Konfederacja 31
10 MN 0
In [39]:
mandaty_globalne_prezentacja.plot(kind='pie',
      figsize=(8,6),
      title='Potencjalny podział mandatów',
      y="Przyznanych mandatów",
      labels=[f'{komitet}: {mandaty}' for i, (komitet, mandaty) in mandaty_globalne_prezentacja.iterrows()])
Out[39]:
<Axes: title={'center': 'Potencjalny podział mandatów'}, ylabel='Przyznanych mandatów'>
No description has been provided for this image
In [40]:
assert mandaty_globalne.sum() == MANDATY_SEJM

Porównanie obu metod¶

Proporcjonaność¶

Powszechne jest publicystyczne stwierdzenie, że metoda d'Honta premiuje duże ugrupowania i koalicje, a jest nieopłacalna dla małych (w sensie osiągniętego wyniku) komitetów startujących samodzielnie. Moim zdaniem metoda d'Honta dąży do zachowania proporcjonalności, ale dopiero w dużej skali, a zaburzenia proporcjonalności pojawiają się ze względu na podział mandatów w zbyt małych okręgach (zwykle na korzyść dużych kosztem małych, ale zaraz zobaczymy, że jest to bardziej złożony problem).

In [41]:
tabela1 = kraj_pow.copy()
tabela1["Liczba mandatów"] = mandaty_kraj
tabela1["Procent mandatów"] = 100 * mandaty_kraj / MANDATY_SEJM
tabela1["Różnica % bezwzgl"] = tabela1["Procent mandatów"] - tabela1["Procent dzielonych głosów"]
tabela1["Różnica % wzgl"] = 100 * (tabela1["Procent mandatów"] - tabela1["Procent dzielonych głosów"]) / tabela1["Procent dzielonych głosów"]
tabela1["Różnica mandatów"] = tabela1["Liczba mandatów"] - tabela1["Mandaty proporcjonalnie"]
In [42]:
tabela1.sort_values("Liczba głosów", ascending=False)
Out[42]:
Skrót Liczba głosów Procent ważnych głosów Procent dzielonych głosów Mandaty proporcjonalnie Liczba mandatów Procent mandatów Różnica % bezwzgl Różnica % wzgl Różnica mandatów
Nr listy
2 PiS 8051935 43.592991 44.000111 202.4 235 51.086957 7.086845 16.106426 32.6
5 KO 5060355 27.396646 27.652506 127.2 134 29.130435 1.477929 5.344646 6.8
3 Lewica 2319946 12.560134 12.677435 58.3 49 10.652174 -2.025261 -15.975322 -9.3
1 PSL 1578523 8.546087 8.625900 39.7 30 6.521739 -2.104161 -24.393524 -9.7
4 Konfederacja 1256953 6.805115 6.868668 31.6 11 2.391304 -4.477364 -65.185329 -20.6
10 MN 32094 0.173756 0.175379 0.8 1 0.217391 0.042012 23.955216 0.2

Gdyby dzielić mandaty pomiędzy komitety w skali całego kraju, system ten byłby niemal idealnie proporcjonalny - czyli sprawiedliwy i zgodny z zasadami zapisanymi w konstytucji.

In [43]:
tabela2 = kraj_pow.copy()
tabela2["Liczba mandatów"] = mandaty_globalne
tabela2["Procent mandatów"] = 100 * mandaty_globalne / MANDATY_SEJM
tabela2["Różnica % bezwzgl"] = tabela2["Procent mandatów"] - tabela2["Procent dzielonych głosów"]
tabela2["Różnica % wzgl"] = 100 * (tabela2["Procent mandatów"] - tabela2["Procent dzielonych głosów"]) / tabela2["Procent dzielonych głosów"]
tabela2["Różnica mandatów"] = tabela2["Liczba mandatów"] - tabela2["Mandaty proporcjonalnie"]
In [44]:
tabela2.sort_values("Liczba głosów", ascending=False)
Out[44]:
Skrót Liczba głosów Procent ważnych głosów Procent dzielonych głosów Mandaty proporcjonalnie Liczba mandatów Procent mandatów Różnica % bezwzgl Różnica % wzgl Różnica mandatów
Nr listy
2 PiS 8051935 43.592991 44.000111 202.4 204 44.347826 0.347715 0.790259 1.6
5 KO 5060355 27.396646 27.652506 127.2 128 27.826087 0.173581 0.627721 0.8
3 Lewica 2319946 12.560134 12.677435 58.3 58 12.608696 -0.068739 -0.542218 -0.3
1 PSL 1578523 8.546087 8.625900 39.7 39 8.478261 -0.147639 -1.711582 -0.7
4 Konfederacja 1256953 6.805115 6.868668 31.6 31 6.739130 -0.129538 -1.885926 -0.6
10 MN 32094 0.173756 0.175379 0.8 0 0.000000 -0.175379 -100.000000 -0.8

Waga głosu¶

W poniższym fragmencie pod uwagę biorę wszystkie głosy ważne; także te oddane na komitety, które nie przekroczyły progu.

In [45]:
kol = 'Liczba głosów ważnych oddanych łącznie na wszystkie listy kandydatów'

Średnia liczba głosów na mandat

In [46]:
srednia = stat_okr[kol].sum() / MANDATY_SEJM
srednia
Out[46]:
40153.717391304344

Teraz zobaczymy tę liczbę w podziale na okręgi. Im mniejsza liczba, tym „ważniejszy” jest jeden głos wyborcy; im większa liczba, tym mniej ważny jest pojedynczy głos.

In [47]:
wagi_glosow = pd.concat([def_okr, stat_okr[kol]], axis=1)
wagi_glosow["Głosów na mandat"] = (wagi_glosow[kol] / wagi_glosow["Liczba mandatów"]).round().astype('int')
In [48]:
wagi_glosow.sort_values("Głosów na mandat")
Out[48]:
Okręg Liczba mandatów Liczba głosów ważnych oddanych łącznie na wszystkie listy kandydatów Głosów na mandat
34 Elbląg 8 250819 31352
35 Olsztyn 10 331684 33168
7 Chełm 12 401318 33443
21 Opole 12 406439 33870
40 Koszalin 8 271711 33964
5 Toruń 13 452330 34795
2 Wałbrzych 8 283002 35375
22 Krosno 11 390581 35507
33 Kielce 16 569891 35618
1 Legnica 12 432436 36036
8 Zielona Góra 12 437917 36493
14 Nowy Sącz 10 370199 37020
16 Płock 10 370561 37056
30 Bielsko-Biała 9 333836 37093
24 Białystok 14 520578 37184
17 Radom 9 335009 37223
32 Katowice 9 335431 37270
6 Lublin 15 565597 37706
18 Siedlce 12 452906 37742
29 Katowice 9 340647 37850
36 Kalisz 12 459152 38263
4 Bydgoszcz 12 459982 38332
11 Sieradz 12 460239 38353
10 Piotrków Trybunalski 9 346326 38481
15 Tarnów 9 347088 38565
38 Piła 9 349051 38783
31 Katowice 12 469633 39136
41 Szczecin 12 470529 39211
37 Konin 9 353041 39227
23 Rzeszów 15 588786 39252
12 Kraków 8 316214 39527
28 Częstochowa 7 284517 40645
26 Słupsk 14 580722 41480
9 Łódź 10 415540 41554
27 Bielsko-Biała 9 389256 43251
25 Gdańsk 12 528829 44069
13 Kraków 14 649287 46378
3 Wrocław 14 654455 46747
20 Warszawa 12 598727 49894
39 Poznań 10 514527 51453
19 Warszawa 20 1381917 69096
In [49]:
wagi_glosow["Głosów na mandat"].describe()
Out[49]:
count       41.000000
mean     39450.292683
std       6358.334138
min      31352.000000
25%      36493.000000
50%      38263.000000
75%      39527.000000
max      69096.000000
Name: Głosów na mandat, dtype: float64
In [50]:
wagi_glosow["Głosów na mandat"].max() / wagi_glosow["Głosów na mandat"].min()
Out[50]:
2.203878540443991
In [51]:
wykres = pd.Series(wagi_glosow["Głosów na mandat"])
wykres.index = wagi_glosow["Okręg"]
wykres.sort_values(inplace=True, ascending=False)
wykres.plot(kind='barh', figsize=(6, 16), grid=True)
Out[51]:
<Axes: ylabel='Okręg'>
No description has been provided for this image

Sprawiedliwa liczba mandatów¶

Gdyby liczba mandatów dla danego okręgu była proporcjonalna do liczby faktycznie oddanych głosów, wartości byłyby zupełnie inne. Obliczmy je używając… metody d'Honta.

Jak już wiemy, w dużej skali metoda ta jest proporcjonalna. Możemy to potwierdzić ponownie obliczając współczynnik głosów na mandat dla hipotetycznej liczby mandatów - wychodzą wartości znacznie bliższe sobie (mniejsze odchylenie standardowe, radykalnie mniejszy stosunek między ekstremalnymi wartościami).

In [52]:
wagi_glosow["Sprawiedliwa liczba mandatów"] = dhont_podzial_liczbowy(MANDATY_SEJM, stat_okr[kol])
wagi_glosow["Sprawiedliwe głosy na mandat"] = (wagi_glosow[kol] / wagi_glosow["Sprawiedliwa liczba mandatów"]).round().astype('int')
In [53]:
wagi_glosow
Out[53]:
Okręg Liczba mandatów Liczba głosów ważnych oddanych łącznie na wszystkie listy kandydatów Głosów na mandat Sprawiedliwa liczba mandatów Sprawiedliwe głosy na mandat
1 Legnica 12 432436 36036 11 39312
2 Wałbrzych 8 283002 35375 7 40429
3 Wrocław 14 654455 46747 17 38497
4 Bydgoszcz 12 459982 38332 11 41817
5 Toruń 13 452330 34795 11 41121
6 Lublin 15 565597 37706 14 40400
7 Chełm 12 401318 33443 10 40132
8 Zielona Góra 12 437917 36493 11 39811
9 Łódź 10 415540 41554 10 41554
10 Piotrków Trybunalski 9 346326 38481 9 38481
11 Sieradz 12 460239 38353 11 41840
12 Kraków 8 316214 39527 8 39527
13 Kraków 14 649287 46378 16 40580
14 Nowy Sącz 10 370199 37020 9 41133
15 Tarnów 9 347088 38565 9 38565
16 Płock 10 370561 37056 9 41173
17 Radom 9 335009 37223 8 41876
18 Siedlce 12 452906 37742 11 41173
19 Warszawa 20 1381917 69096 35 39483
20 Warszawa 12 598727 49894 15 39915
21 Opole 12 406439 33870 10 40644
22 Krosno 11 390581 35507 10 39058
23 Rzeszów 15 588786 39252 15 39252
24 Białystok 14 520578 37184 13 40044
25 Gdańsk 12 528829 44069 13 40679
26 Słupsk 14 580722 41480 15 38715
27 Bielsko-Biała 9 389256 43251 10 38926
28 Częstochowa 7 284517 40645 7 40645
29 Katowice 9 340647 37850 8 42581
30 Bielsko-Biała 9 333836 37093 8 41730
31 Katowice 12 469633 39136 12 39136
32 Katowice 9 335431 37270 8 41929
33 Kielce 16 569891 35618 14 40706
34 Elbląg 8 250819 31352 6 41803
35 Olsztyn 10 331684 33168 8 41460
36 Kalisz 12 459152 38263 11 41741
37 Konin 9 353041 39227 9 39227
38 Piła 9 349051 38783 9 38783
39 Poznań 10 514527 51453 13 39579
40 Koszalin 8 271711 33964 7 38816
41 Szczecin 12 470529 39211 12 39211
In [54]:
wagi_glosow["Sprawiedliwe głosy na mandat"].describe()
Out[54]:
count       41.000000
mean     40280.097561
std       1182.023304
min      38481.000000
25%      39227.000000
50%      40400.000000
75%      41173.000000
max      42581.000000
Name: Sprawiedliwe głosy na mandat, dtype: float64
In [55]:
wagi_glosow["Sprawiedliwe głosy na mandat"].max() / wagi_glosow["Sprawiedliwe głosy na mandat"].min()
Out[55]:
1.106546087679634

Wartość 35 dla Warszawy może być szokująca dla Polaków spoza stolicy (a może też działaczy partyjnych), ale właśnie taka liczba mandatów poselskich odpowiada liczbie oddanych głosów.