Kandidatų į 2016 metų Seimą analizė

Kandidatų duomenų analizė remiasi A. Tapino pasiūlytu metodu.

Rinkimų dieną reikės išsirinkti:

  1. Vienmandatės apygardos kandidatą.

  2. Daugiamandatės apygardos partiją (sąrašą).

  3. Surašyti 5 pirmumo balsus iš pasirinktos partijos.

Tam, kad geriau suprastumėte partijų sudėtį, žemiau rasite visų vienmandačių apygardų ir daugiamandačių partijų sudėčių vizualizaciją sudarytą pagal A. Tapino pasiūlytus vertinimo kriterijus.

Duomenys analizei surinkti iš vrk.lt svetainės.

Jei jus domina, tik rezultatai, galite peršokti tiesiai prie rezultatų dalies. Jei taip pat domina kaip buvo surinkti duomenys ir kaip atlikti skaičiavimai, galite skaityti nuo pradžių.

Jei pastebėjote klaidų, praneškite https://gist.github.com/sirex/e63f65fed33c81a45f6f9dc908afc560 komentaruose.

Kandidatų vertinimui naudoti skaičiai

Perbraukti punktai nebuvo įtraukti į vertinimą, dėl sunkiai prieinamų duomenų.

  • Keitęs partiją: -3 balai
  • Nemoka anglų kalbos: -4 balai
  • Moka daugiau kalbų (be anglų ir rusų): +2 balai už kiekvieną kalbą
  • Neturi aukštojo išsilavinimo: -6 balai
  • Turi magistro aukštąjį išsilavinimą: +2 balai
  • Turi mokslų daktaro laipsnį: +4 balai
  • Turi tik sovietinį aukštąjį išsilavinimą: -3 balai
  • Turi aukštąjį išsilavinimą užsienio universitete: +3 balai
  • Išsilavinimas nesusijęs su teise, politika ar vadyba: -1 balas
  • Ėjo aukštas pareigas sovietiniame režime: -3 balai
  • Turi patirties valstybės tarnyboje ar viešajame sektoriuje užsienyje: nuo -3 iki +3 jūsų nuožiūra (buhalterė kolūkyje šiek tiek skiriasi nuo Europos audito rūmų seniūno)
  • Visą gyvenimą užsiėmė veikla, niekuo nesusijusia nei su valstybės valdymu, nei su viešuoju administravimu: -4 balai
  • Daugiau kaip penkias kadencijas Seime: -3 balai
  • Daugiau kaip tris kadencijas Seime: -1 balas
  • Neturi patirties Seime: patys nuspręskite, kaip vertinti - pliusu ar minusu
  • Daugiau nei 65 metai: -2 balai
  • Pajamų ir interesų deklaracija jums kelia įtarimų: -2 balai.

Duomenų surinkimas

Vyriausioji rinkimų komisija nepateikia „žalių“ duomenų apie kandidatus, todėl juos tenka susirinkti sunkiuoju būdu iš vrk.lt svetainės. Atliekant analizę, duomenų surinkimas pareikalavo apie 80% viso laiko.

Kadangi duomenys iš vrk.lt svetainės jau surinkti, kad kitiems nereikėtų to kartoti, visus „žalius“ duomenis galite rasti adresu http://atviriduomenys.lt/data/vrk/rinkimai/2016/

In [1]:
%matplotlib inline
In [2]:
import databot
import urllib.parse
import base64
import imghdr
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import datetime
import re
import textwrap
from IPython.display import display, HTML
In [3]:
mpl.rc('font', family='Ubuntu', size=16)
mpl.rc('figure', figsize=(16, 10))
In [12]:
bot = databot.Bot('vrk_kadidatai.db')
table_page = bot.define('kandidatų-lentelės-puslapis')
table = bot.define('kandidatų-lentelė')
questions_page = bot.define('anketos-puslapis')
questions = bot.define('anketa')
degree = bot.define('mokslo-laipsnis')
photo = bot.define('nuotrauka')
experience = bot.define('patirtis')
education = bot.define('išsilavinimas')
profile_links = bot.define('profilio-nuorodos')
bio_page = bot.define('biografijos-puslapis')
bio = bot.define('biografija')
assets_page = bot.define('turto-deklaracijos-puslapis')
assets = bot.define('turto-deklaracijos')
interests_page = bot.define('interesų-deklaracijos-puslapis')
interests = bot.define('interesų-dekraracijos')
campaign_page = bot.define('kampanijos-lėšų-puslapis')
other_page = bot.define('kita-puslapis')

Atsisiunčiame pirmą puslapį su visų kandidatų sąrašu. vrk.lt svetainėje, nuorodos rodomos naršyklėje užkrauna tik puslapio rėmus, o turinys siunčiamas atskirai, JavaScript pagalba, todėl su nuorodomis reikia tam tikro „masažavimo“.

In [21]:
table_page.download('http://vrk.lt/pub/webcontent/kandidatai/lrsKandidataiPilnasSarasasHtml?rnkId=426')
Out[21]:
<databot.Pipe[1]: kandidatų-lentelės-puslapis>

Iš pirmo puslapio išsirenkame nuorodas į kandidatų profilius ir duomenis iš kandidatų lentelės.

In [38]:
@databot.func(skipna=True)
def fixlink(link):
    url = dict(urllib.parse.parse_qsl(urllib.parse.urlparse(link).query))['srcUrl']
    return 'http://vrk.lt/pub/webcontent/' + url

@databot.func()
def vardas(name):
    return ' '.join([w for w in name.split() if w[-1].islower()])

@databot.func()
def pavarde(name):
    return ' '.join([w for w in name.split() if w[-1].isupper()])

with table_page:
    table.select([
        'table.partydata tbody tr', (
            fixlink('td[1] [email protected]'), {
                'vardas': vardas('td[1] a:text'),
                'pavardė': pavarde('td[1] a:text'),
                'sąrašas': {
                    'pavadinimas': 'td[2] a:text?',
                    'nuoroda': 'td[2] [email protected]?',
                },
                'numeris sąraše': 'td[3]:text',
                'vienmadatė apygarda': {
                    'pavadinimas': 'td[5] a:text?',
                    'nuoroda': 'td[5] [email protected]?',
                },
                'iškėlė vienmandatėje': {
                    'pavadinimas': databot.first('td[6] a:text?', 'td[6]:text'),
                    'nuoroda': 'td[6] [email protected]?',
                },
            }
        )
    ])
kandidatų-lentelės-puslapis -> kandidatų-lentelė: 100%|██████████| 1/1 [00:04<00:00,  4.08s/it]
In [145]:
with table:
    questions_page.download()
kandidatų-lentelė -> anketos-puslapis: 100%|██████████| 1415/1415 [02:46<00:00,  9.10it/s]
In [189]:
@databot.func(skipna=True)
def join(value):
    return ' '.join(value)

with questions_page:
    questions.select([
        'body > div[3] > table > tr', (
            databot.row.key, {
                'klausimas': join(['xpath:./td/text()']),
                'atsakymas': 'xpath:./td/b/text()?',
            }
        )
    ])
anketos-puslapis -> anketa: 100%|██████████| 1415/1415 [00:26<00:00, 52.53it/s]
In [13]:
with questions_page:
    degree.select(databot.row.key, 'xpath://td[contains(text(), "Jei turite, nurodykite pedagoginį vardą, mokslo laipsnį")]/b/text()')
In [188]:
@databot.func(skipna=True)
def decode_photo(value):
    return base64.b64decode(value[len('data:;base64,'):])

@databot.func(skipna=True)
def detect_image_type(blob):
    return imghdr.what(None, blob)

with questions_page:
    photo.clean().reset().select(databot.row.key, {
        'body': decode_photo('body > div[1] [email protected]?'),
        'type': detect_image_type(decode_photo('body > div[1] [email protected]?')),
    })
anketos-puslapis -> nuotrauka: 100%|██████████| 1415/1415 [00:17<00:00, 82.98it/s]
In [170]:
with questions_page:
    profile_links.clean().reset().select(
        databot.row.key, {
            'biografija': fixlink('xpath://body/ul[1]/li/a[contains(text(), "Biografija")]/@href'),
            'turtas': fixlink('xpath://body/ul[1]/li/a[contains(text(), "Turto ir pajamų")]/@href'),
            'interesai': fixlink('xpath://body/ul[1]/li/a[contains(text(), "Privačių interesų")]/@href'),
            'kampanija': fixlink('xpath://body/ul[1]/li/a[contains(text(), "Politinės kampanijos")]/@href?'),
            'kita': fixlink('xpath://body/ul[1]/li/a[contains(text(), "Kita")]/@href'),
        },
    )
anketos-puslapis -> profilio-nuorodos: 100%|██████████| 1415/1415 [00:09<00:00, 150.52it/s]

Atsisiunčiame visus kandidato profilio puslapius iš profilio meniu.

In [173]:
with profile_links:
    bio_page.download(databot.row.value.biografija)
    assets_page.download(databot.row.value.turtas)
    interests_page.download(databot.row.value.interesai)
    campaign_page.download(databot.row.value.kampanija)
    other_page.download(databot.row.value.kita)
profilio-nuorodos -> biografijos-puslapis: 100%|██████████| 1415/1415 [02:24<00:00,  9.72it/s]
profilio-nuorodos -> turto-deklaracijos-puslapis: 100%|██████████| 1415/1415 [02:34<00:00,  9.80it/s]
profilio-nuorodos -> interesų-deklaracijos-puslapis: 100%|██████████| 1415/1415 [03:04<00:00,  7.65it/s]
profilio-nuorodos -> kampanijos-lėšų-puslapis: 100%|██████████| 1415/1415 [01:05<00:00, 21.77it/s]
profilio-nuorodos -> kita-puslapis: 100%|██████████| 1415/1415 [02:18<00:00, 10.22it/s]
In [22]:
with questions_page:
    experience.select([
        'xpath://table[contains(thead/tr/th/text(), "Institucijos pavadinimas, pareigos")]/tr', (
            databot.row.key, {
                'institucija': split('td[1]:text', 1, ',', 0),
                'pareigos': split('td[1]:text', 1, ',', 1),
                'laikotarpis': split('td[2]:text', 1, ',', 0),
        })
    ])
anketos-puslapis -> patirtis: 100%|██████████| 1415/1415 [00:09<00:00, 145.74it/s]
In [27]:
with questions_page:
    education.select([
        'xpath://table[contains(thead/tr/th/text(), "Išsilavinimas")]/tr', (
            databot.row.key, {
                'išsilavinimas': 'td[1]:text',
                'mokymo įstaiga': 'td[2]:text',
                'specialybė': 'td[3]:text',
                'baigimo metai': 'td[4]:text',
        })
    ])
anketos-puslapis -> išsilavinimas: 100%|██████████| 1415/1415 [00:10<00:00, 139.51it/s]
In [46]:
@databot.func(skipna=True)
def euro(value):
    return int(value.replace('Eur', '').strip())

with assets_page:
    assets.select(databot.row.key, {
        'turtas': euro('xpath://tr[contains(td/text(), "Privalomas registruoti turtas")]/td[2]/b/text()'),
        'vertybės': euro('xpath://tr[contains(td/text(), "Vertybiniai popieriai")]/td[2]/b/text()'),
        'pinigai': euro('xpath://tr[contains(td/text(), "Piniginės lėšos")]/td[2]/b/text()'),
        'suteiktos paskolos': euro('xpath://tr[contains(td/text(), "Suteiktos paskolos")]/td[2]/b/text()'),
        'gautos paskolos': euro('xpath://tr[contains(td/text(), "Gautos paskolos")]/td[2]/b/text()'),
    })
turto-deklaracijos-puslapis -> turto-deklaracijos: 100%|██████████| 1395/1395 [00:06<00:00, 222.86it/s]
In [5]:
with bio_page:
    bio.select(databot.row.key, databot.text('body > div[3]'))

Patikriname ar visi puslapiai sėkmingai atsisiuntė ir ar duomenų išrinkimas praėjo be klaidų.

profilio-nuorodos -> (biografijos-puslapis, kampanijos-lėšų-puslapis turi klaidų, bet tos klaidos susiję su tuo, kad ne visi kandidatai turi biografijos puslapius ir ne visi asmeniškai administruoja savo kampanijos lėšas, todėl tų puslapių tiesiog nėra.

In [16]:
bot.main(argv=['status'])
   id              rows  source
       errors      left    target
=========================================================
    1                 1  kandidatų-lentelės-puslapis
            0         0    kandidatų-lentelė
---------------------------------------------------------
    2              1415  kandidatų-lentelė
            0         0    anketos-puslapis
---------------------------------------------------------
    3              1415  anketos-puslapis
            0         0    anketa
            0         0    nuotrauka
            0         0    profilio-nuorodos
            0         0    patirtis
            0         0    išsilavinimas
            0         0    mokslo-laipsnis
---------------------------------------------------------
    4             43014  anketa
---------------------------------------------------------
   18              1415  mokslo-laipsnis
---------------------------------------------------------
   12              1415  nuotrauka
---------------------------------------------------------
   16              1181  patirtis
---------------------------------------------------------
   17              1708  išsilavinimas
---------------------------------------------------------
   13              1415  profilio-nuorodos
            0         0    biografijos-puslapis
           20         0    turto-deklaracijos-puslapis
            0         0    interesų-deklaracijos-puslapis
          742         0    kampanijos-lėšų-puslapis
            0         0    kita-puslapis
---------------------------------------------------------
    5              1415  biografijos-puslapis
            0         0    biografija
---------------------------------------------------------
    6              1415  biografija
---------------------------------------------------------
    7              1395  turto-deklaracijos-puslapis
            3         0    turto-deklaracijos
---------------------------------------------------------
    8              1392  turto-deklaracijos
---------------------------------------------------------
    9              1415  interesų-deklaracijos-puslapis
---------------------------------------------------------
   10                 0  interesų-dekraracijos
---------------------------------------------------------
   14               673  kampanijos-lėšų-puslapis
---------------------------------------------------------
   15              1415  kita-puslapis
---------------------------------------------------------
Out[16]:
<databot.bot.Bot at 0x7f298a3aa9b0>

Eksportuojame, tai kas buvo išrinkta į csv formatą, tam, kad būtų galima duomenis importuoti į Pandas įrankį.

In [17]:
questions.export('anketa.csv')
experience.export('patirtis.csv')
education.export('išsilavinimas.csv')
bio.export('bio.csv')
table.export('kandidatai.csv')
degree.export('mokslo-laipsnis.csv')

Duomenų valymas

Importuojam visus išrinktus duomenis į Pandas.

In [33]:
anketa = pd.read_csv('anketa.csv').pivot(values='atsakymas', index='key', columns='klausimas')
patirtis = pd.read_csv('patirtis.csv')
išsilavinimas = pd.read_csv('išsilavinimas.csv')
biografija = pd.read_csv('bio.csv')
biografija['key'] = biografija.key.str.replace('KandidatasBiografijaHtml', 'KandidatasAnketaHtml')
biografija = biografija.set_index('key')
kandidatai = pd.read_csv('kandidatai.csv', index_col='key')
mokslolaipsnis = pd.read_csv('mokslo-laipsnis.csv', index_col='key', na_values='Nenurodė').dropna()

Panašu, kad to, kas suvesta į kandidatų anketas, niekas netikrina. Dalis duomenų surašyti laisvu tekstu, be jokios tvarkos, kaip kam patinka. Tai labai apsunkina duomenų surinkimą. Geras pavyzdys yra mokymo įstaigos pavadinimas, kur vieni pavadinimą rašo sutrumpintai, kiti pateikia pilną pavadinimą, kiti pavadinimą nurodo su gramatinėmis klaidomis, treti nurodo ne oficialų pavadinimą, o kokį nors alternatyvų, trumpesnį pavadinimą.

In [34]:
lietuvos = [
    'lietuvos', 'vilni?ai?us', 'vilnius', 'vilniuas', 'kau[nt]o', 'klaipėdos', 'šiaulių', 'utenos',
    'marijampolės', 'kelmės', 'vilkijos', 'švenčioni?ų', 'rietavo',
    'joniškėlio', 'alytaus', 'smalininkų', 'švenčionėlių', 'šalčininkų', 'tauragės', 'plungės',
    'vytauto', 'ri?omerio', 'stulginskio',
    'žemės ūkio', 'kolegija', 'gimnazija', 'mokykla', 'valstybinė', 'technikumas',
    '^teisės universitetas$', '^teisės magistras$', '^vadybos ir ekonomikos universitetas$',
    '^psichologijos akademija$',
    'policijos', 'medicinos', 'nenurodė', r'Elekt\.tech',
    r'^.{1,3}[UAIMRĮ](\b|$)', '^.{1,5}$', '^LT ', '^kmaik$', '^važum$', '^mruni$',
]
tarptautinis_išsilavinimas = ~(
    išsilavinimas['mokymo įstaiga'].str.contains(re.compile('|'.join(lietuvos), flags=re.IGNORECASE))
)
išsilavinimas[tarptautinis_išsilavinimas]['mokymo įstaiga'].value_counts().head()
/home/sirex/.venvs/databot/lib/python3.5/site-packages/ipykernel/__main__.py:13: UserWarning: This pattern has match groups. To actually get the groups, use str.extract.
Out[34]:
Maskvos Lomonosovo universitetas                  3
Balstogės universitetas                           2
Maskvos M.Lomonosovo universitetas                2
Varšuvos universitetas                            2
Kristiansand'o Agder universitetas (Norvegija)    1
Name: mokymo įstaiga, dtype: int64
In [35]:
specialybės = [
    'teisė', 'viešasis administravimas', 'teisinink(as|ė)', 'politikos mokslai', 'politologija', 'vadyb(a|os)'
]
tinkamas_išsilavinimas = (
    išsilavinimas.specialybė.str.contains(re.compile('|'.join(specialybės), flags=re.IGNORECASE))
)
išsilavinimas[tinkamas_išsilavinimas].specialybė.value_counts().head()
/home/sirex/.venvs/databot/lib/python3.5/site-packages/ipykernel/__main__.py:5: UserWarning: This pattern has match groups. To actually get the groups, use str.extract.
Out[35]:
Teisė                       37
Viešasis administravimas    29
Teisininkas                 27
viešasis administravimas    20
Politikos mokslai           15
Name: specialybė, dtype: int64
In [36]:
magistras = mokslolaipsnis.value.str.contains('magistr', case=False)
In [37]:
daktaras = mokslolaipsnis.value.str.contains('daktar', case=False)
In [38]:
komunistas = ['komjaunimo', 'komunistų partijos', 'tskp narys']
komunistas = biografija.value.fillna('').str.contains(re.compile('|'.join(komunistas), flags=re.IGNORECASE))
nekomunistas = ['išaiškinant buvusių komjaunimo veikėjų', 'baigė kauno komjaunimo']
nekomunistas = biografija.value.fillna('').str.contains(re.compile('|'.join(nekomunistas), flags=re.IGNORECASE))
komunistas = komunistas & ~nekomunistas
display(biografija[komunistas].head())
biografija[komunistas].shape
value
key
http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1103800&rnkId=426 VYDAS BARAVYKAS\n\nLietuvos socialdemokratų pa...
http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1104668&rnkId=426 VINCAS BOTYRIUS\n\nAntikorupcinės N. Puteikio ...
http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1102222&rnkId=426 ALGIRDAS CEGELIS\n\nPartijos Tvarka ir teising...
http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1102220&rnkId=426 ALGIMANTAS DUMBRAVA\n\nPartijos Tvarka ir teis...
http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1103926&rnkId=426 LIUDAS JONAITIS\n\nLietuvos socialdemokratų pa...
Out[38]:
(25, 1)

Galiausiai išvalius visus duomenis, sudedame juos į vieną didelė lentelę, ir transformuojame duomenis į tokį pavidalį, kurį bus patogu nadoti tolesniuose skaičiavimuose.

In [40]:
now = datetime.datetime.now()
aukštasis = išsilavinimas.išsilavinimas == 'Aukštasis universitetinis'
kitos_kalbos = lambda v: sum(1 for x in v.split(',') if x.lower().strip() not in ('anglų', 'rusų', 'nenurodė'))
data = pd.DataFrame({
    'amžius': (now - pd.to_datetime(anketa['5. Gimimo data '])).dt.days // 365,
    'patirtis': patirtis[patirtis.pareigos == 'Seimo narys'].groupby('key').size(),
    'anglų kalba': anketa['13. Kokias užsienio kalbas mokate '].str.contains('anglų', case=False),
    'kitos kalbos': anketa['13. Kokias užsienio kalbas mokate '].apply(kitos_kalbos),
    'aukštasis išsilavinimas': išsilavinimas[aukštasis].groupby('key').size() > 0,
    'tarptautinis išsilavinimas': išsilavinimas[tarptautinis_išsilavinimas].groupby('key').size() > 0,
    'tinkamas išsilavinimas': išsilavinimas[tinkamas_išsilavinimas].groupby('key').size() > 0,
    'magistras': magistras,
    'daktaras': daktaras,
    'komunistas': komunistas,
})
data['patirtis'] = data['patirtis'].fillna(0).astype(int)
data['anglų kalba'] = data['anglų kalba'].fillna(False).astype(int)
data['kitos kalbos'] = data['kitos kalbos'].fillna(0).astype(int)
data['daktaras'] = data.daktaras.fillna(False).astype(int)
data['magistras'] = data.magistras.fillna(False).astype(int) | data.daktaras
data['aukštasis išsilavinimas'] = data['aukštasis išsilavinimas'].fillna(False).astype(int) | data.magistras
data['tarptautinis išsilavinimas'] = data['tarptautinis išsilavinimas'].fillna(False).astype(int)
data['tinkamas išsilavinimas'] = data['tinkamas išsilavinimas'].fillna(False).astype(int)
data['komunistas'] = data.komunistas.astype(int)
data['amžius'] = (data.amžius > 65).fillna(False).astype(int)
data.head(2).T
Out[40]:
http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1098320&rnkId=426 http://vrk.lt/pub/webcontent/kandidatai/lrsKandidatasAnketaHtml?rkndId=1098342&rnkId=426
amžius 0 1
anglų kalba 1 0
aukštasis išsilavinimas 1 1
daktaras 0 1
kitos kalbos 1 0
komunistas 0 0
magistras 0 1
patirtis 0 0
tarptautinis išsilavinimas 0 0
tinkamas išsilavinimas 0 0

Skaičiavimų dalis

Žemiau panaudojus išvalytus duomenis sudedami visi balai ir gaunama viena didelė rezultatų lentelė. Balai paimti iš A. Tapino pasiūlytos formulės.

Deje, iš vrk pateiktų duomenų ne viską pavyko išgauti, todėl, ne visi kriterijai yra įvertinti.

Taip pat, tokie kriterijai, kaip išsilavinimas, nėra 100% patikimi, kadangi kai kurie kandidatai nenurodė išsilavinimo, arba išsilavinimą nurodį tik biografijoje, bet ne anketoje.

Atpažinimas ar kandidatas ėjo aukštas pareigas sovietiniame režime, taip pat nėra 100% patikimas, kadangi tokio požymio nustatymui buvo tiesiog ieškoma tam tikrų raktinių žodžių biografijoje. Jei pastebėjote, klaidų rašykite komentaruose.

In [41]:
def tapino_formule(res):
    # - Keitęs partiją: -3 balai
    #
    # + Nemoka anglų kalbos: -4 balai
    res['Nemoka anglų kalbos'] = (data['anglų kalba'] - 1) * 4
    # + Moka daugiau kalbų (be anglų ir rusų): +2 balai už kiekvieną kalbą
    res['Moka daugiau kalbų (be anglų ir rusų)'] = data['kitos kalbos'] * 2
    # + Neturi aukštojo išsilavinimo: -6 balai
    res['Neturi aukštojo išsilavinimo'] = (data['aukštasis išsilavinimas'] - 1) * 6
    # + Turi magistro aukštąjį išsilavinimą: +2 balai
    res['Turi magistro aukštąjį išsilavinimą'] = data.magistras * 2
    # + Turi mokslų daktaro laipsnį: +4 balai
    res['Turi mokslų daktaro laipsnį'] = data.daktaras * 4
    # - Turi tik sovietinį aukštąjį išsilavinimą: -3 balai
    #
    # + Turi aukštąjį išsilavinimą užsienio universitete: +3 balai
    res['Turi aukštąjį išsilavinimą užsienio universitete'] = data['tarptautinis išsilavinimas'] * 3
    # + Išsilavinimas nesusijęs su teise, politika ar vadyba: -1 balas
    res['Išsilavinimas nesusijęs su teise, politika ar vadyba'] = (data['tinkamas išsilavinimas'] - 1) * 1
    # + Ėjo aukštas pareigas sovietiniame režime: -3 balai
    res['Ėjo aukštas pareigas sovietiniame režime'] = data.komunistas * -3
    # - Turi patirties valstybės tarnyboje ar viešajame sektoriuje užsienyje: nuo -3
    #   iki +3 jūsų nuožiūra (buhalterė kolūkyje šiek tiek skiriasi nuo Europos audito
    #   rūmų seniūno)
    #
    # - Visą gyvenimą užsiėmė veikla, niekuo nesusijusia nei su valstybės valdymu,
    #   nei su viešuoju administravimu: -4 balai
    #
    # - Daugiau kaip penkias kadencijas Seime: -3 balai
    #
    # - Daugiau kaip tris kadencijas Seime: -1 balas
    #
    # - Neturi patirties Seime: patys nuspręskite, kaip vertinti - pliusu ar minusu
    #
    # + Daugiau nei 65 metai: -2 balai (jeigu norite, kad į valdžią ateitų jaunesni,
    #   jeigu manote, kad valdžioje turi būti pensinio amžiaus žmonės, tada skirkite
    #   pliusinius balus )
    res['Daugiau nei 65 metai'] = data.amžius * -2
    # - Pajamų ir interesų deklaracija jums kelia įtarimų: -2 balai.
    #
    # Skaitykite daugiau:
    # http://www.delfi.lt/news/ringas/lit/a-tapinas-lemiamas-ir-trumpas-gidas-uz-ka-balsuoti.d?id=72463294
    
    return res

Rezultatai

Kandidatai vienmadatėse

In [42]:
vienmadačiai = kandidatai[~kandidatai['vienmadatė apygarda.pavadinimas'].isnull()][[
    'vardas',
    'pavardė',
    'vienmadatė apygarda.pavadinimas',
]]

vienmadačiai['kandidatas'] = vienmadačiai.vardas + ' ' + vienmadačiai.pavardė

apygardos = vienmadačiai['vienmadatė apygarda.pavadinimas'].value_counts().index
apygardos = sorted(apygardos, key=lambda x: int(x.split(None, 1)[0].rstrip('.')))
vienmadačiai['apygarda'] = pd.Categorical(vienmadačiai['vienmadatė apygarda.pavadinimas'], categories=apygardos)

vienmadačiai = vienmadačiai[['kandidatas', 'apygarda']]
In [43]:
res = tapino_formule(pd.DataFrame(vienmadačiai))
res = res.sort_values('apygarda').set_index(['apygarda', 'kandidatas'])

Kandidatų pasiskirstymas viendamdatėse apygardose pagal balą

Grafikas žemiau parodo kiekvienos viemandatės kandidatų pasiskirstimą, pagal įvertinimo balą. Kaip interpretuoti šį grafiką galite sužinoti pasiskaitę Vikipedijoje.

Mėlina dėžutė rodo kandiatus nuo 25 iki 75 procentų, raudona juostelė rodo medianą, raudonas kvardatėlis rodo vidurkį, juodos juostelės iš šonų rodo ribas kur patenka mažiausią ir didžiausią balus turintys kandidatai, pluso ženklai rodo pavienius kandidatus, kurie stipriai skiriasi nuo visų kitų kandidatų.

Bendras šio grafiko vertinimas yra toks, kad visų kandidatų vidurkis yra minusinis ir tik retais atvejais kandidatai turi teigiamą įvertinimą.

In [44]:
columns = (res.sum(axis=1).median(axis=0, level=0)).sort_values().index.get_values()
ax = res.sum(axis=1).unstack().T[columns].plot.box(vert=False, showmeans=True, grid=True, figsize=(16, 42))
ax.set_yticklabels(['\n'.join(textwrap.wrap(x, 30)[:3]) for x in columns])
None

Detalus kiekvienos vienmandatės apygardos kandidų įvertinimas

In [45]:
frame = pd.DataFrame({'balas': res.sum(axis=1)})
frame = frame.reset_index(level=1).sort_index()
for i in range(len(frame.groupby(level=0).groups)):
    label = apygardos[i]
    group = frame.ix[label].set_index('kandidatas').sort_values('balas')
    fig = plt.figure(figsize=(16, group.shape[0] // 3))
    ax = fig.add_axes([0, 0, 0.5, 1])
    x = group.balas.values
    y = np.arange(len(x))
    categories = [
       ((x >= 0), 'green'),
       ((x < 0), 'red'),
    ]
    for category, color in categories:
        if len(category) > 0:
            cx = x[category]
            cy = y[category]
            ax.hlines(cy, 0, cx, color=color)
            ax.plot(cx, cy, 'o', color=color)
    ax.plot([0, 0], [-1, len(y)], '--', color='blue')
    display(HTML('<h3>%s</h3>' % label))
    ax.set_yticks(y)
    ax.set_yticklabels(['%30s' % group.index[0]] + list(group.index)[1:], family='Ubuntu Mono')
    ax.set_xlabel('Balas')
    ax.set_ylabel('Kandidatas')
    plt.xlim(-20, 15)
    plt.ylim(-1, len(y))
    plt.grid()
    plt.show()

1. Naujamiesčio

2. Senamiesčio

3. Antakalnio

4. Žirmūnų

5. Fabijoniškių

6. Šeškinės

7. Justiniškių

8. Karoliniškių