In [1]:
import os
import gzip
import json

import requests
from bs4 import BeautifulSoup

import pandas as pd
import numpy as np
import re

import seaborn as sns
import matplotlib.pyplot as plt

%matplotlib inline

Daten aus dem Internet laden

In [2]:
BASIS_URL = 'https://www.parlament.gv.at'

#Plenarsitzungen des Nationalrats für eine gegebene Gesetzesperiode
LISTE_URL = '/PAKT/STPROT/index.shtml?SUCH=&xdocumentUri=%2FPAKT%2FSTPROT%2Findex.shtml&pageNumber=&R_PLSO=PL' + \
            '&GP={gp}&STEP=&INTRANET=N&feldRnr=1&STPROT=ALLE&ascDesc=DESC&FBEZ=FP_011&NRBRBV=NR&BEZ=FP_211' + \
            '&requestId=D6CBFB9744&LISTE=&jsMode=&listeId=211&NUR_VORL=N'

Falls schon heruntergeladene Protokolle existieren, diese laden.

In [3]:
%%time

FILE = 'protokolle_txt.json.zip'

if os.path.exists(FILE):
    # Protokolle aus Datei laden
    with gzip.open(FILE, 'rt') as f:
        protokolle_txt = json.load(f)
else:
    # Protokolle herunterladen
    protokolle_txt = dict()
Wall time: 5.55 s

Hier werden Protokolle geladen, für angegebene Gesetzesperiode. Falls bestehende Protokolle schon geladen sind, nicht nötig auszuführen.

In [4]:
%%time

# Status 8.8.2016
# Stenographische Protokolle in strukturiertem HTML Format ab XXII. Gesetzgebungsperiode (23.05.2006, 150. NR Sitzung)
# Ab XXIII. Gesetzgebungsperiode (1. Sitzung 30.10.2006) durchgehend neues Format.
# Analysiere daher ab XXIII. Gesetzgebungsperiode
# Aktuellstest Protokoll: XXV. Gesetzgebungsperiode, 24.02.2016
gesetzgebungsperioden = ('XXV', 'XXIV', 'XXIII')

for gp in gesetzgebungsperioden:
    print(' ')
    print(gp)

    # Internetseite mit Links zu den Protokollen der entsprechenden Gesetzgebungsperidoe wird abgerufen
    r = requests.get(BASIS_URL + LISTE_URL.format(gp = gp))

    soup = BeautifulSoup(r.text, 'lxml')

    # Alle Links in der Ergebnistabelle
    links = soup.find('table', class_='tabelle filter').find_all('a')
    # Davon alle auf Stenographisches Protokoll in HTML Format
    links = [link for link in links if link.text == 'HTML']
    # Davon die URL
    links = [link.get('href') for link in links]

    for link in links:
        # In der URL steckt die Nummer der Sitzung, der zur Information ausgegeben wird
        print(link.split('/')[5].split('_')[1], end=' ')
        # Eindeutiger key des Protokolls, bestehend aus Gesetzperiode, Art der Sitzung (Nationalrat) und Nummer
        key = gp + '_' + link.split('/')[5]
        r_temp = requests.get(BASIS_URL + link)
        protokolle_txt[key] = r_temp.text
        
print()
# Protokolle in gezippten JSON File speichern
with gzip.open(FILE, mode="wt") as f:
    json.dump(protokolle_txt, f)
 
XXV
00114 00113 00112 00111 00110 00109 00108 00107 00106 00105 00103 00102 00101 00100 00099 00098 00097 00096 00095 00094 00093 00092 00091 00090 00089 00088 00087 00086 00085 00084 00083 00082 00081 00080 00079 00078 00077 00076 00075 00074 00073 00072 00071 00070 00069 00068 00067 00066 00065 00064 00063 00062 00061 00060 00059 00058 00057 00056 00055 00054 00053 00052 00051 00050 00049 00048 00047 00046 00045 00044 00043 00042 00041 00040 00039 00038 00037 00036 00035 00034 00033 00032 00031 00030 00029 00028 00027 00026 00025 00024 00023 00022 00021 00020 00019 00018 00017 00016 00015 00014 00013 00012 00011 00010 00009 00008 00007 00006 00005 00004 00003 00002 00001  
XXIV
00220 00219 00218 00217 00216 00215 00214 00213 00212 00211 00210 00209 00208 00207 00206 00205 00204 00203 00202 00201 00200 00199 00198 00197 00196 00195 00194 00193 00192 00191 00190 00189 00188 00187 00186 00185 00184 00183 00182 00181 00180 00179 00178 00177 00176 00175 00174 00173 00172 00171 00170 00169 00168 00167 00166 00165 00164 00163 00162 00161 00160 00159 00158 00157 00156 00155 00154 00153 00152 00151 00150 00149 00148 00147 00146 00145 00144 00143 00142 00141 00140 00139 00138 00137 00136 00135 00134 00133 00132 00131 00130 00129 00128 00127 00126 00125 00124 00123 00122 00121 00120 00119 00118 00117 00116 00115 00114 00113 00112 00111 00110 00109 00108 00107 00106 00105 00104 00103 00102 00101 00100 00099 00098 00097 00096 00095 00094 00093 00092 00091 00090 00089 00088 00087 00086 00085 00084 00083 00082 00081 00080 00079 00078 00077 00076 00075 00074 00073 00072 00071 00070 00069 00068 00067 00066 00065 00064 00063 00062 00061 00060 00059 00058 00057 00056 00055 00054 00053 00052 00051 00050 00049 00048 00047 00046 00045 00044 00043 00042 00041 00040 00039 00038 00037 00036 00035 00034 00033 00032 00031 00030 00029 00028 00027 00026 00025 00024 00023 00022 00021 00020 00019 00018 00017 00016 00015 00014 00013 00012 00011 00010 00009 00008 00007 00006 00005 00004 00003 00002 00001  
XXIII
00076 00075 00074 00073 00072 00071 00070 00069 00068 00067 00066 00065 00064 00063 00062 00061 00060 00059 00058 00057 00056 00055 00054 00053 00052 00051 00050 00049 00048 00047 00046 00045 00044 00043 00042 00041 00040 00039 00038 00037 00036 00035 00034 00033 00032 00031 00030 00029 00028 00027 00026 00025 00024 00023 00022 00021 00020 00019 00018 00017 00016 00015 00014 00013 00012 00011 00010 00009 00008 00007 00006 00005 00004 00003 00002 00001 Wall time: 5min 30s
In [4]:
len(protokolle_txt)
Out[4]:
409

Wir haben also 409 Nationalratssitzungen. Jeder der Sitzungen ist im HTML-Format gespeichert.

In [5]:
protokolle_txt['XXIII_NRSITZ_00043'][:400]
Out[5]:
'<html>\r\n<head>\r\n<meta http-equiv=Content-Type content="text/html; charset=us-ascii">\r\n<meta name=Generator content="Microsoft Word 11 (filtered)">\r\n<title>Parlamentarische Materialien</title>\r\n<style>\r\n</style>\r\n<link rel=stylesheet type=text/css href="/PAKT/VHG/XXIII/NRSITZ/NRSITZ_00043/NRSP_043.css"><link rel="stylesheet" type="text/css" href="/styles/PDSteno.css"></head>\r\n<body lang=DE-AT>\r\n<di'

Protokolle analysieren und Reden auslesen

Falls die Protokolle schon ausgelsen wurden und strukturiert in einem Datenframe gespeichert wurden, wird die Datei nun eingelesen.

In [6]:
%%time

FILE = 'df_protokolle.pkl'

if os.path.exists(FILE):
    # Protokolle aus Datei laden
    df = pd.read_pickle(FILE)
Wall time: 375 ms

Hier werden Protokolle ausgelesen und ausgewählte Elemente in einen Datenframe gespeichert. Das dauert einige Zeit. Falls entsprechende Datenframe schon geladen ist und kein neues Parsen gewünscht wird, nicht ausführen.

In [79]:
%%time

# key_okay speichert alle Protokolle, die bereits ausgelesen wurden.
# Nun sollte das Auslesen in einem Lauf funktioneren. Während des Entwickeln des Codes, 
# wurde der folgende Code öfters ausgeführt und ich wollte nicht jedesmal ganz von vorne anfangen.
key_okay=[]

# Ergebnis wird in einem Datenframe gespeichert
df = pd.DataFrame()
# Countdown damit man sieht, wie viele Protokolle noch ausgelesen werden müssen
countdown = len(protokolle_txt) - len(key_okay)

for key in protokolle_txt:
#for key in ('XXIV_NRSITZ_00207',):
    if key in key_okay:
        continue
    countdown = countdown - 1
    
    # HTML-Protokoll parsen mittels BeautifulSoup
    txt = protokolle_txt[key]
    soup = BeautifulSoup(txt, 'lxml')
    
    # Bei manchen Sitzungen gibt es Fehler bei den IDs (Name, Datum, ...).
    # Diese werden manuell überschrieben
    if key == 'XXIV_NRSITZ_00026':
        # Falsche IDs in entsprechendem Protokoll
        sitzung = '26. Sitzung des\r\nNationalrates der Republik Österreich'
        periode = 'XXIV. Gesetzgebungsperiode'
        datum = 'Dienstag, 16. Juni 2009'
    elif key == 'XXIII_NRSITZ_00060':    
        # Falsche IDs in entsprechendem Protokoll
        sitzung = '60. Sitzung des\r\nNationalrates der Republik Österreich'
        periode = 'XXIII. Gesetzgebungsperiode'
        datum = 'Donnerstag, 8. Mai 2008'        
    # Normalerweise lassen sich die IDs auslesen, Identifikation anhand HTML-Class IDs
    else:
        sitzung = soup.find('p', class_='DBl02').get_text()
        periode = soup.find('p', class_='DBl04').get_text()
        datum = soup.find('p', class_='DBl05').get_text()

    # Nochmals eine manuelle Datenbereinigung
    if key == 'XXIV_NRSITZ_00220':
        sitzung = '220. Sitzung des\r\nNationalrates der Republik Österreich'
        
    if key == 'XXV_NRSITZ_00001':
        datum = 'Dienstag, 29. Oktober 2013'

    if key == 'XXIII_NRSITZ_00021':    
        datum = 'Donnerstag, 3. Mai 2007' 
    
    print(countdown, ' - ', key, ' - ', periode[:periode.find('.')], ' - ', sitzung[:sitzung.find('.')])
    
    # Normalerweise sind Reden an HTML-Class 'StandardRB' (RB scheint für Rede-Beginn zu stehen) erkennbar
    rede_pos = soup.find('p', class_='StandardRB')

    while rede_pos != None:
        # Nun wird der Text der Rede ausgelesen.
        # Zuerst der Text des Paragraphs mit der 'StandardRB'-Class
        rede_txt = rede_pos.get_text()
        # Doch leider kann der Text über mehrere Paragraphen gehen.
        # Zusätzlich Reden des Schriftführers/der Schriftführerin ausschließen, gibt es bei den Angelobungen neuer
        # Abgeordneter, inklusive der Antworten "Ich gelobe." 
        # Hier gibt es oft Probleme mit den HTML-Classes, ebenso bei 
        while ((rede_txt.startswith('Schriftführer') == False) & 
               (max(rede_txt.find('Ich gelobe.'),rede_txt.find('Ich\r\ngelobe.')) == -1)):
            # Identifikation der HTML Klasse des nächsten Paragraphs
            rede_pos = rede_pos.find_next('p')
            klasse = rede_pos['class'] 
            # Paragraphen mit den folgenden Klassen sind eine Fortsetzung der Rede.
            # Daher wird der Text zur bestehenden Rede hinzugefügt 
            if klasse in (['MsoNormal'], ['StandardRB'], ['MsoBodyText'], ['MsoListBullet'], 
                          ['INHANTR'], 
                          ['MsoListBulletCxSpFirst'], ['MsoListBulletCxSpMiddle'], ['MsoListBulletCxSpLast'], 
                          ['MsoBodyText2'], ['MsoNormalCxSpMiddle']) :
                # INHANTR z.B. wegen XXII_NRSITZ_00058
                # MsoListBulletCxSpFirst, ...Middle und ... Last z.B. wegen XXV_NRSITZ_00012
                # MsoBodyText2 z.B wegen XXII_NRSITZ_00051, könnte man auch als Ende verwenden 
                # MsoNormalCxSpMiddle z.B. wegen XXIV_NRSITZ_00159
                rede_txt = rede_txt + rede_pos.get_text()
            # Die folgenden Klassen markieren das Ende einer Rede 
            elif klasse in (['StandardRE'], ['RE'], ['StandardR'], ['SB'], ['RE0']):
                # RE0 z.B. wegen XXII_NRSITZ_00035
                rede_txt = rede_txt + rede_pos.get_text()
                df = df.append({'key':key, 'sitzung': sitzung, 'periode': periode, 'datum': datum, 'rede': rede_txt}, 
                               ignore_index=True)
                break
            # Die folgenden Klassen markieren einen Zwischentext, der nicht Teil der Rede ist
            elif klasse in (['ZM'],['RB'], ['Format1']):
                # 'ZM' - Entschließungsantrag
                # 'RB' - Redebeginn, nicht benötigt, verwende StandardRB
                pass
            # Nicht oben erwähnte Klasse führt zu Fehlermeldung und speichert Dokument, 
            # damit es genauer analysiert werden kann.
            # Wurde während der Entwicklung verwendet, sollte jetzt nicht vorkommen, es sei denn,
            # es kommen neue Protokolle mit neuen Formatierungen vor
            else:
                print(key)
                print(klasse)
                print(rede_txt)
                with open('temp.html', 'w', encoding='utf8') as fp:
                    fp.write(protokolle_txt[key])
                raise ValueError('Unerwartetes Element in Rede')
        
        # Finde Start der nächsten Rede
        if key == 'XXIV_NRSITZ_00120':
            # 'Falsches StandardR in entsprechendem Protokoll
            rede_pos = rede_pos.find_next('p', class_=lambda x: x in ('StandardRB', 'StandardR'))
        else:
            rede_pos = rede_pos.find_next('p', class_='StandardRB')
            
            
    key_okay.append(key)
    
df.to_pickle('df_protokolle.pkl')
408  -  XXIII_NRSITZ_00028  -  XXIII  -  28
407  -  XXIV_NRSITZ_00022  -  XXIV  -  22
406  -  XXV_NRSITZ_00013  -  XXV  -  13
405  -  XXIII_NRSITZ_00043  -  XXIII  -  43
404  -  XXIV_NRSITZ_00065  -  XXIV  -  65
403  -  XXIV_NRSITZ_00213  -  XXIV  -  213
402  -  XXIII_NRSITZ_00018  -  XXIII  -  18
401  -  XXIV_NRSITZ_00050  -  XXIV  -  50
400  -  XXIV_NRSITZ_00146  -  XXIV  -  146
399  -  XXIV_NRSITZ_00026  -  XXIV  -  26
398  -  XXIV_NRSITZ_00205  -  XXIV  -  205
397  -  XXV_NRSITZ_00083  -  XXV  -  83
396  -  XXV_NRSITZ_00086  -  XXV  -  86
395  -  XXIV_NRSITZ_00006  -  XXIV  -  6
394  -  XXIII_NRSITZ_00036  -  XXIII  -  36
393  -  XXIV_NRSITZ_00057  -  XXIV  -  57
392  -  XXIII_NRSITZ_00008  -  XXIII  -  8
391  -  XXIV_NRSITZ_00017  -  XXIV  -  17
390  -  XXV_NRSITZ_00092  -  XXV  -  92
389  -  XXIII_NRSITZ_00002  -  XXIII  -  2
388  -  XXIV_NRSITZ_00049  -  XXIV  -  49
387  -  XXIV_NRSITZ_00148  -  XXIV  -  148
386  -  XXIV_NRSITZ_00158  -  XXIV  -  158
385  -  XXIII_NRSITZ_00065  -  XXIII  -  65
384  -  XXIV_NRSITZ_00089  -  XXIV  -  89
383  -  XXIV_NRSITZ_00112  -  XXIV  -  112
382  -  XXIV_NRSITZ_00087  -  XXIV  -  87
381  -  XXIV_NRSITZ_00129  -  XXIV  -  129
380  -  XXIII_NRSITZ_00039  -  XXIII  -  39
379  -  XXIV_NRSITZ_00206  -  XXIV  -  206
378  -  XXIV_NRSITZ_00115  -  XXIV  -  115
377  -  XXIV_NRSITZ_00134  -  XXIV  -  134
376  -  XXV_NRSITZ_00029  -  XXV  -  29
375  -  XXIV_NRSITZ_00160  -  XXIV  -  160
374  -  XXIV_NRSITZ_00010  -  XXIV  -  10
373  -  XXIV_NRSITZ_00150  -  XXIV  -  150
372  -  XXV_NRSITZ_00062  -  XXV  -  62
371  -  XXV_NRSITZ_00068  -  XXV  -  68
370  -  XXV_NRSITZ_00072  -  XXV  -  72
369  -  XXIV_NRSITZ_00166  -  XXIV  -  166
368  -  XXIII_NRSITZ_00009  -  XXIII  -  9
367  -  XXV_NRSITZ_00004  -  XXV  -  4
366  -  XXIV_NRSITZ_00183  -  XXIV  -  183
365  -  XXV_NRSITZ_00020  -  XXV  -  20
364  -  XXIV_NRSITZ_00153  -  XXIV  -  153
363  -  XXV_NRSITZ_00090  -  XXV  -  90
362  -  XXIII_NRSITZ_00054  -  XXIII  -  54
361  -  XXIV_NRSITZ_00210  -  XXIV  -  210
360  -  XXIV_NRSITZ_00123  -  XXIV  -  123
359  -  XXIV_NRSITZ_00121  -  XXIV  -  121
358  -  XXIII_NRSITZ_00056  -  XXIII  -  56
357  -  XXIV_NRSITZ_00168  -  XXIV  -  168
356  -  XXV_NRSITZ_00030  -  XXV  -  30
355  -  XXIV_NRSITZ_00061  -  XXIV  -  61
354  -  XXIV_NRSITZ_00078  -  XXIV  -  78
353  -  XXIV_NRSITZ_00185  -  XXIV  -  185
352  -  XXV_NRSITZ_00028  -  XXV  -  28
351  -  XXV_NRSITZ_00089  -  XXV  -  89
350  -  XXIII_NRSITZ_00031  -  XXIII  -  31
349  -  XXIV_NRSITZ_00167  -  XXIV  -  167
348  -  XXIV_NRSITZ_00131  -  XXIV  -  131
347  -  XXV_NRSITZ_00034  -  XXV  -  34
346  -  XXIV_NRSITZ_00119  -  XXIV  -  119
345  -  XXV_NRSITZ_00063  -  XXV  -  63
344  -  XXIV_NRSITZ_00218  -  XXIV  -  218
343  -  XXV_NRSITZ_00026  -  XXV  -  26
342  -  XXIV_NRSITZ_00203  -  XXIV  -  203
341  -  XXV_NRSITZ_00056  -  XXV  -  56
340  -  XXV_NRSITZ_00015  -  XXV  -  15
339  -  XXIV_NRSITZ_00128  -  XXIV  -  128
338  -  XXIV_NRSITZ_00099  -  XXIV  -  99
337  -  XXIV_NRSITZ_00077  -  XXIV  -  77
336  -  XXV_NRSITZ_00075  -  XXV  -  75
335  -  XXIV_NRSITZ_00086  -  XXIV  -  86
334  -  XXV_NRSITZ_00008  -  XXV  -  8
333  -  XXIV_NRSITZ_00098  -  XXIV  -  98
332  -  XXIV_NRSITZ_00052  -  XXIV  -  52
331  -  XXIV_NRSITZ_00002  -  XXIV  -  2
330  -  XXIII_NRSITZ_00012  -  XXIII  -  12
329  -  XXIV_NRSITZ_00173  -  XXIV  -  173
328  -  XXIII_NRSITZ_00076  -  XXIII  -  des Nationalrates der
Republik Österreic
327  -  XXV_NRSITZ_00016  -  XXV  -  16
326  -  XXIV_NRSITZ_00113  -  XXIV  -  113
325  -  XXIII_NRSITZ_00006  -  XXIII  -  6
324  -  XXIV_NRSITZ_00174  -  XXIV  -  174
323  -  XXV_NRSITZ_00053  -  XXV  -  53
322  -  XXV_NRSITZ_00010  -  XXV  -  10
321  -  XXIV_NRSITZ_00195  -  XXIV  -  195
320  -  XXV_NRSITZ_00102  -  XXV  -  102
319  -  XXV_NRSITZ_00071  -  XXV  -  71
318  -  XXIV_NRSITZ_00016  -  XXIV  -  16
317  -  XXIII_NRSITZ_00023  -  XXIII  -  23
316  -  XXIII_NRSITZ_00047  -  XXIII  -  47
315  -  XXIV_NRSITZ_00176  -  XXIV  -  176
314  -  XXIV_NRSITZ_00041  -  XXIV  -  41
313  -  XXIV_NRSITZ_00093  -  XXIV  -  93
312  -  XXIV_NRSITZ_00105  -  XXIV  -  105
311  -  XXV_NRSITZ_00114  -  XXV  -  114
310  -  XXV_NRSITZ_00081  -  XXV  -  81
309  -  XXIV_NRSITZ_00151  -  XXIV  -  151
308  -  XXIII_NRSITZ_00037  -  XXIII  -  37
307  -  XXIV_NRSITZ_00091  -  XXIV  -  91
306  -  XXV_NRSITZ_00103  -  XXV  -  103
305  -  XXIV_NRSITZ_00198  -  XXIV  -  198
304  -  XXV_NRSITZ_00049  -  XXV  -  49
303  -  XXIV_NRSITZ_00097  -  XXIV  -  97
302  -  XXIV_NRSITZ_00186  -  XXIV  -  186
301  -  XXIII_NRSITZ_00067  -  XXIII  -  67
300  -  XXV_NRSITZ_00039  -  XXV  -  39
299  -  XXIII_NRSITZ_00034  -  XXIII  -  34
298  -  XXIV_NRSITZ_00062  -  XXIV  -  62
297  -  XXIV_NRSITZ_00154  -  XXIV  -  154
296  -  XXV_NRSITZ_00101  -  XXV  -  101
295  -  XXIV_NRSITZ_00124  -  XXIV  -  124
294  -  XXIV_NRSITZ_00048  -  XXIV  -  48
293  -  XXIII_NRSITZ_00038  -  XXIII  -  38
292  -  XXIV_NRSITZ_00144  -  XXIV  -  144
291  -  XXV_NRSITZ_00031  -  XXV  -  31
290  -  XXV_NRSITZ_00012  -  XXV  -  12
289  -  XXIV_NRSITZ_00133  -  XXIV  -  133
288  -  XXV_NRSITZ_00100  -  XXV  -  100
287  -  XXIV_NRSITZ_00059  -  XXIV  -  59
286  -  XXIV_NRSITZ_00207  -  XXIV  -  207
285  -  XXV_NRSITZ_00058  -  XXV  -  58
284  -  XXIII_NRSITZ_00058  -  XXIII  -  58
283  -  XXIII_NRSITZ_00011  -  XXIII  -  11
282  -  XXIV_NRSITZ_00007  -  XXIV  -  7
281  -  XXIII_NRSITZ_00004  -  XXIII  -  4
280  -  XXIV_NRSITZ_00019  -  XXIV  -  19
279  -  XXIV_NRSITZ_00092  -  XXIV  -  92
278  -  XXIV_NRSITZ_00032  -  XXIV  -  32
277  -  XXIV_NRSITZ_00051  -  XXIV  -  51
276  -  XXV_NRSITZ_00035  -  XXV  -  35
275  -  XXIV_NRSITZ_00042  -  XXIV  -  42
274  -  XXIII_NRSITZ_00073  -  XXIII  -  73
273  -  XXV_NRSITZ_00065  -  XXV  -  65
272  -  XXV_NRSITZ_00011  -  XXV  -  11
271  -  XXIV_NRSITZ_00216  -  XXIV  -  216
270  -  XXIV_NRSITZ_00125  -  XXIV  -  125
269  -  XXIV_NRSITZ_00054  -  XXIV  -  54
268  -  XXIV_NRSITZ_00040  -  XXIV  -  40
267  -  XXV_NRSITZ_00021  -  XXV  -  21
266  -  XXIV_NRSITZ_00108  -  XXIV  -  108
265  -  XXIII_NRSITZ_00021  -  XXIII  -  21
264  -  XXIV_NRSITZ_00075  -  XXIV  -  75
263  -  XXIV_NRSITZ_00136  -  XXIV  -  136
262  -  XXIV_NRSITZ_00214  -  XXIV  -  214
261  -  XXV_NRSITZ_00073  -  XXV  -  73
260  -  XXIV_NRSITZ_00101  -  XXIV  -  101
259  -  XXV_NRSITZ_00066  -  XXV  -  66
258  -  XXIII_NRSITZ_00074  -  XXIII  -  74
257  -  XXIV_NRSITZ_00191  -  XXIV  -  191
256  -  XXIV_NRSITZ_00056  -  XXIV  -  56
255  -  XXIV_NRSITZ_00135  -  XXIV  -  135
254  -  XXIV_NRSITZ_00127  -  XXIV  -  127
253  -  XXIV_NRSITZ_00072  -  XXIV  -  72
252  -  XXIV_NRSITZ_00178  -  XXIV  -  178
251  -  XXIV_NRSITZ_00031  -  XXIV  -  31
250  -  XXIV_NRSITZ_00140  -  XXIV  -  140
249  -  XXIV_NRSITZ_00090  -  XXIV  -  90
248  -  XXIV_NRSITZ_00094  -  XXIV  -  94
247  -  XXV_NRSITZ_00085  -  XXV  -  85
246  -  XXV_NRSITZ_00064  -  XXV  -  64
245  -  XXIV_NRSITZ_00149  -  XXIV  -  149
244  -  XXIV_NRSITZ_00155  -  XXIV  -  155
243  -  XXV_NRSITZ_00097  -  XXV  -  97
242  -  XXIV_NRSITZ_00044  -  XXIV  -  44
241  -  XXIII_NRSITZ_00060  -  XXIII  -  60
240  -  XXIV_NRSITZ_00085  -  XXIV  -  85
239  -  XXIV_NRSITZ_00096  -  XXIV  -  96
238  -  XXIII_NRSITZ_00042  -  XXIII  -  42
237  -  XXIII_NRSITZ_00044  -  XXIII  -  44
236  -  XXIV_NRSITZ_00015  -  XXIV  -  15
235  -  XXIV_NRSITZ_00212  -  XXIV  -  212
234  -  XXV_NRSITZ_00070  -  XXV  -  70
233  -  XXV_NRSITZ_00002  -  XXV  -  2
232  -  XXIV_NRSITZ_00028  -  XXIV  -  28
231  -  XXIV_NRSITZ_00172  -  XXIV  -  172
230  -  XXV_NRSITZ_00094  -  XXV  -  94
229  -  XXIV_NRSITZ_00194  -  XXIV  -  194
228  -  XXIV_NRSITZ_00008  -  XXIV  -  8
227  -  XXIII_NRSITZ_00030  -  XXIII  -  30
226  -  XXIV_NRSITZ_00021  -  XXIV  -  21
225  -  XXIV_NRSITZ_00147  -  XXIV  -  147
224  -  XXIV_NRSITZ_00159  -  XXIV  -  159
223  -  XXIII_NRSITZ_00075  -  XXIII  -  75
222  -  XXV_NRSITZ_00027  -  XXV  -  27
221  -  XXV_NRSITZ_00057  -  XXV  -  57
220  -  XXV_NRSITZ_00042  -  XXV  -  42
219  -  XXIV_NRSITZ_00037  -  XXIV  -  37
218  -  XXV_NRSITZ_00022  -  XXV  -  22
217  -  XXV_NRSITZ_00088  -  XXV  -  88
216  -  XXIII_NRSITZ_00072  -  XXIII  -  72
215  -  XXV_NRSITZ_00014  -  XXV  -  14
214  -  XXIV_NRSITZ_00169  -  XXIV  -  169
213  -  XXV_NRSITZ_00038  -  XXV  -  38
212  -  XXV_NRSITZ_00051  -  XXV  -  51
211  -  XXIV_NRSITZ_00152  -  XXIV  -  152
210  -  XXIV_NRSITZ_00163  -  XXIV  -  163
209  -  XXV_NRSITZ_00047  -  XXV  -  47
208  -  XXIII_NRSITZ_00017  -  XXIII  -  17
207  -  XXV_NRSITZ_00082  -  XXV  -  82
206  -  XXV_NRSITZ_00001  -  XXV  -  1
205  -  XXV_NRSITZ_00040  -  XXV  -  40
204  -  XXIV_NRSITZ_00003  -  XXIV  -  3
203  -  XXIV_NRSITZ_00064  -  XXIV  -  64
202  -  XXIV_NRSITZ_00118  -  XXIV  -  118
201  -  XXIV_NRSITZ_00012  -  XXIV  -  12
200  -  XXIV_NRSITZ_00179  -  XXIV  -  179
199  -  XXIII_NRSITZ_00010  -  XXIII  -  10
198  -  XXV_NRSITZ_00006  -  XXV  -  6
197  -  XXIV_NRSITZ_00197  -  XXIV  -  197
196  -  XXIV_NRSITZ_00164  -  XXIV  -  164
195  -  XXIV_NRSITZ_00080  -  XXIV  -  80
194  -  XXV_NRSITZ_00017  -  XXV  -  17
193  -  XXIV_NRSITZ_00130  -  XXIV  -  130
192  -  XXIII_NRSITZ_00014  -  XXIII  -  14
191  -  XXIV_NRSITZ_00180  -  XXIV  -  180
190  -  XXIV_NRSITZ_00060  -  XXIV  -  60
189  -  XXIV_NRSITZ_00181  -  XXIV  -  181
188  -  XXIV_NRSITZ_00114  -  XXIV  -  114
187  -  XXIV_NRSITZ_00211  -  XXIV  -  211
186  -  XXIV_NRSITZ_00209  -  XXIV  -  209
185  -  XXIII_NRSITZ_00033  -  XXIII  -  33
184  -  XXV_NRSITZ_00018  -  XXV  -  18
183  -  XXV_NRSITZ_00074  -  XXV  -  74
182  -  XXIV_NRSITZ_00116  -  XXIV  -  116
181  -  XXV_NRSITZ_00037  -  XXV  -  37
180  -  XXIII_NRSITZ_00013  -  XXIII  -  13
179  -  XXIV_NRSITZ_00013  -  XXIV  -  13
178  -  XXV_NRSITZ_00098  -  XXV  -  98
177  -  XXIV_NRSITZ_00177  -  XXIV  -  177
176  -  XXV_NRSITZ_00009  -  XXV  -  9
175  -  XXIV_NRSITZ_00202  -  XXIV  -  202
174  -  XXIV_NRSITZ_00046  -  XXIV  -  46
173  -  XXV_NRSITZ_00109  -  XXV  -  109
172  -  XXIV_NRSITZ_00142  -  XXIV  -  142
171  -  XXIII_NRSITZ_00057  -  XXIII  -  57
170  -  XXIV_NRSITZ_00055  -  XXIV  -  55
169  -  XXIV_NRSITZ_00170  -  XXIV  -  170
168  -  XXIV_NRSITZ_00043  -  XXIV  -  43
167  -  XXV_NRSITZ_00080  -  XXV  -  80
166  -  XXV_NRSITZ_00033  -  XXV  -  33
165  -  XXIV_NRSITZ_00068  -  XXIV  -  68
164  -  XXIV_NRSITZ_00157  -  XXIV  -  157
163  -  XXIII_NRSITZ_00019  -  XXIII  -  19
162  -  XXV_NRSITZ_00003  -  XXV  -  3
161  -  XXIV_NRSITZ_00084  -  XXIV  -  84
160  -  XXV_NRSITZ_00045  -  XXV  -  45
159  -  XXIII_NRSITZ_00063  -  XXIII  -  63
158  -  XXIII_NRSITZ_00022  -  XXIII  -  22
157  -  XXV_NRSITZ_00087  -  XXV  -  87
156  -  XXIII_NRSITZ_00066  -  XXIII  -  66
155  -  XXIV_NRSITZ_00030  -  XXIV  -  30
154  -  XXIII_NRSITZ_00027  -  XXIII  -  27
153  -  XXV_NRSITZ_00111  -  XXV  -  111
152  -  XXIV_NRSITZ_00208  -  XXIV  -  208
151  -  XXIV_NRSITZ_00071  -  XXIV  -  71
150  -  XXV_NRSITZ_00023  -  XXV  -  23
149  -  XXIV_NRSITZ_00162  -  XXIV  -  162
148  -  XXIV_NRSITZ_00182  -  XXIV  -  182
147  -  XXIV_NRSITZ_00045  -  XXIV  -  45
146  -  XXIV_NRSITZ_00193  -  XXIV  -  193
145  -  XXIV_NRSITZ_00161  -  XXIV  -  161
144  -  XXIV_NRSITZ_00029  -  XXIV  -  29
143  -  XXIV_NRSITZ_00139  -  XXIV  -  139
142  -  XXV_NRSITZ_00055  -  XXV  -  55
141  -  XXV_NRSITZ_00061  -  XXV  -  61
140  -  XXV_NRSITZ_00024  -  XXV  -  24
139  -  XXIV_NRSITZ_00053  -  XXIV  -  53
138  -  XXIII_NRSITZ_00062  -  XXIII  -  62
137  -  XXIII_NRSITZ_00061  -  XXIII  -  61
136  -  XXV_NRSITZ_00032  -  XXV  -  32
135  -  XXV_NRSITZ_00113  -  XXV  -  113
134  -  XXIII_NRSITZ_00020  -  XXIII  -  20
133  -  XXIII_NRSITZ_00029  -  XXIII  -  29
132  -  XXV_NRSITZ_00079  -  XXV  -  79
131  -  XXIV_NRSITZ_00024  -  XXIV  -  24
130  -  XXIV_NRSITZ_00156  -  XXIV  -  156
129  -  XXIV_NRSITZ_00023  -  XXIV  -  23
128  -  XXIII_NRSITZ_00059  -  XXIII  -  59
127  -  XXIV_NRSITZ_00199  -  XXIV  -  199
126  -  XXIV_NRSITZ_00188  -  XXIV  -  188
125  -  XXIII_NRSITZ_00035  -  XXIII  -  35
124  -  XXIV_NRSITZ_00034  -  XXIV  -  34
123  -  XXIV_NRSITZ_00088  -  XXIV  -  88
122  -  XXIV_NRSITZ_00165  -  XXIV  -  165
121  -  XXIII_NRSITZ_00053  -  XXIII  -  53
120  -  XXIV_NRSITZ_00095  -  XXIV  -  95
119  -  XXIII_NRSITZ_00024  -  XXIII  -  24
118  -  XXIII_NRSITZ_00068  -  XXIII  -  68
117  -  XXIV_NRSITZ_00200  -  XXIV  -  200
116  -  XXV_NRSITZ_00007  -  XXV  -  7
115  -  XXV_NRSITZ_00036  -  XXV  -  36
114  -  XXIV_NRSITZ_00103  -  XXIV  -  103
113  -  XXIV_NRSITZ_00004  -  XXIV  -  4
112  -  XXIV_NRSITZ_00027  -  XXIV  -  27
111  -  XXIII_NRSITZ_00026  -  XXIII  -  26
110  -  XXIV_NRSITZ_00035  -  XXIV  -  35
109  -  XXIV_NRSITZ_00137  -  XXIV  -  137
108  -  XXIV_NRSITZ_00070  -  XXIV  -  70
107  -  XXIII_NRSITZ_00055  -  XXIII  -  55
106  -  XXIII_NRSITZ_00025  -  XXIII  -  25
105  -  XXIV_NRSITZ_00215  -  XXIV  -  215
104  -  XXIV_NRSITZ_00025  -  XXIV  -  25
103  -  XXV_NRSITZ_00043  -  XXV  -  43
102  -  XXIII_NRSITZ_00049  -  XXIII  -  49
101  -  XXIV_NRSITZ_00106  -  XXIV  -  106
100  -  XXIV_NRSITZ_00145  -  XXIV  -  145
99  -  XXIII_NRSITZ_00046  -  XXIII  -  46
98  -  XXV_NRSITZ_00044  -  XXV  -  44
97  -  XXIV_NRSITZ_00005  -  XXIV  -  5
96  -  XXV_NRSITZ_00048  -  XXV  -  48
95  -  XXV_NRSITZ_00019  -  XXV  -  19
94  -  XXIII_NRSITZ_00071  -  XXIII  -  71
93  -  XXIV_NRSITZ_00192  -  XXIV  -  192
92  -  XXIV_NRSITZ_00009  -  XXIV  -  9
91  -  XXIV_NRSITZ_00100  -  XXIV  -  100
90  -  XXV_NRSITZ_00093  -  XXV  -  93
89  -  XXIII_NRSITZ_00005  -  XXIII  -  5
88  -  XXIV_NRSITZ_00141  -  XXIV  -  141
87  -  XXIV_NRSITZ_00109  -  XXIV  -  109
86  -  XXIV_NRSITZ_00102  -  XXIV  -  102
85  -  XXIV_NRSITZ_00069  -  XXIV  -  69
84  -  XXV_NRSITZ_00099  -  XXV  -  99
83  -  XXIV_NRSITZ_00143  -  XXIV  -  143
82  -  XXV_NRSITZ_00067  -  XXV  -  67
81  -  XXIII_NRSITZ_00045  -  XXIII  -  45
80  -  XXIV_NRSITZ_00001  -  XXIV  -  1
79  -  XXIV_NRSITZ_00138  -  XXIV  -  138
78  -  XXV_NRSITZ_00041  -  XXV  -  41
77  -  XXV_NRSITZ_00096  -  XXV  -  96
76  -  XXIV_NRSITZ_00079  -  XXIV  -  79
75  -  XXV_NRSITZ_00025  -  XXV  -  25
74  -  XXIV_NRSITZ_00033  -  XXIV  -  33
73  -  XXV_NRSITZ_00091  -  XXV  -  91
72  -  XXV_NRSITZ_00108  -  XXV  -  108
71  -  XXIV_NRSITZ_00047  -  XXIV  -  47
70  -  XXIII_NRSITZ_00051  -  XXIII  -  51
69  -  XXIV_NRSITZ_00104  -  XXIV  -  104
68  -  XXV_NRSITZ_00059  -  XXV  -  59
67  -  XXIII_NRSITZ_00064  -  XXIII  -  64
66  -  XXIV_NRSITZ_00117  -  XXIV  -  117
65  -  XXV_NRSITZ_00005  -  XXV  -  5
64  -  XXIV_NRSITZ_00063  -  XXIV  -  63
63  -  XXIV_NRSITZ_00126  -  XXIV  -  126
62  -  XXIV_NRSITZ_00196  -  XXIV  -  196
61  -  XXV_NRSITZ_00077  -  XXV  -  77
60  -  XXIV_NRSITZ_00020  -  XXIV  -  20
59  -  XXIV_NRSITZ_00067  -  XXIV  -  67
58  -  XXIV_NRSITZ_00132  -  XXIV  -  132
57  -  XXIII_NRSITZ_00052  -  XXIII  -  52
56  -  XXIV_NRSITZ_00066  -  XXIV  -  66
55  -  XXIV_NRSITZ_00082  -  XXIV  -  82
54  -  XXIV_NRSITZ_00014  -  XXIV  -  14
53  -  XXIV_NRSITZ_00120  -  XXIV  -  120
52  -  XXIII_NRSITZ_00050  -  XXIII  -  50
51  -  XXIII_NRSITZ_00040  -  XXIII  -  40
50  -  XXIII_NRSITZ_00001  -  XXIII  -  1
49  -  XXV_NRSITZ_00107  -  XXV  -  107
48  -  XXV_NRSITZ_00069  -  XXV  -  69
47  -  XXIII_NRSITZ_00016  -  XXIII  -  16
46  -  XXIV_NRSITZ_00189  -  XXIV  -  189
45  -  XXIV_NRSITZ_00111  -  XXIV  -  111
44  -  XXIV_NRSITZ_00184  -  XXIV  -  184
43  -  XXV_NRSITZ_00084  -  XXV  -  84
42  -  XXIV_NRSITZ_00219  -  XXIV  -  219
41  -  XXIV_NRSITZ_00217  -  XXIV  -  217
40  -  XXV_NRSITZ_00078  -  XXV  -  78
39  -  XXIII_NRSITZ_00003  -  XXIII  -  3
38  -  XXIV_NRSITZ_00220  -  XXIV  -  220
37  -  XXV_NRSITZ_00105  -  XXV  -  105
36  -  XXIII_NRSITZ_00041  -  XXIII  -  41
35  -  XXV_NRSITZ_00054  -  XXV  -  54
34  -  XXV_NRSITZ_00050  -  XXV  -  50
33  -  XXIV_NRSITZ_00122  -  XXIV  -  122
32  -  XXV_NRSITZ_00060  -  XXV  -  60
31  -  XXIII_NRSITZ_00007  -  XXIII  -  7
30  -  XXV_NRSITZ_00046  -  XXV  -  46
29  -  XXV_NRSITZ_00095  -  XXV  -  95
28  -  XXIV_NRSITZ_00074  -  XXIV  -  74
27  -  XXIII_NRSITZ_00070  -  XXIII  -  70
26  -  XXIV_NRSITZ_00187  -  XXIV  -  187
25  -  XXIV_NRSITZ_00073  -  XXIV  -  73
24  -  XXIV_NRSITZ_00171  -  XXIV  -  171
23  -  XXIV_NRSITZ_00081  -  XXIV  -  81
22  -  XXV_NRSITZ_00076  -  XXV  -  76
21  -  XXIV_NRSITZ_00076  -  XXIV  -  76
20  -  XXIV_NRSITZ_00110  -  XXIV  -  110
19  -  XXIV_NRSITZ_00083  -  XXIV  -  83
18  -  XXIV_NRSITZ_00058  -  XXIV  -  58
17  -  XXIV_NRSITZ_00190  -  XXIV  -  190
16  -  XXIV_NRSITZ_00011  -  XXIV  -  11
15  -  XXIV_NRSITZ_00036  -  XXIV  -  36
14  -  XXIV_NRSITZ_00018  -  XXIV  -  18
13  -  XXIV_NRSITZ_00038  -  XXIV  -  38
12  -  XXIV_NRSITZ_00039  -  XXIV  -  39
11  -  XXIV_NRSITZ_00204  -  XXIV  -  204
10  -  XXIII_NRSITZ_00069  -  XXIII  -  69
9  -  XXV_NRSITZ_00052  -  XXV  -  52
8  -  XXIV_NRSITZ_00201  -  XXIV  -  201
7  -  XXIII_NRSITZ_00032  -  XXIII  -  32
6  -  XXV_NRSITZ_00106  -  XXV  -  106
5  -  XXV_NRSITZ_00112  -  XXV  -  112
4  -  XXIII_NRSITZ_00015  -  XXIII  -  15
3  -  XXV_NRSITZ_00110  -  XXV  -  110
2  -  XXIII_NRSITZ_00048  -  XXIII  -  48
1  -  XXIV_NRSITZ_00107  -  XXIV  -  107
0  -  XXIV_NRSITZ_00175  -  XXIV  -  175
Wall time: 5min 5s

Die Sitzung XXIII_NRSITZ_00076 oben ist keine echte Sitzung, deshalb oben keine richtige Nummer. Spielt haber keine Rolle, da diese Sitzung eh keine Rede enthält.

Die folgenden drei Zellen wurden verwendet, um während der Entwicklung des Codes für einzelne Protokolle das entsprechende HTML File zu schreiben. Dieses konnte dann im Browser analysiert werden.

In [7]:
#print(key)
In [8]:
#with open('temp.html', 'w', encoding='utf8') as fp:
#    fp.write(protokolle_txt[key])
In [9]:
#with open('temp.html', 'w', encoding='utf8') as fp:
#    fp.write(protokolle_txt['XXIII_NRSITZ_00021'])
In [10]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 30820 entries, 0 to 30819
Data columns (total 5 columns):
datum      30820 non-null object
key        30820 non-null object
periode    30820 non-null object
rede       30820 non-null object
sitzung    30820 non-null object
dtypes: object(5)
memory usage: 1.2+ MB

Wir haben also mehr als 30.000 Reden. So sehen die einzelnen Zeilen aus, pro Zeile gibt es eine Rede.

In [11]:
df[:3]
Out[11]:
datum key periode rede sitzung
0 Donnerstag, 5. Juli 2007 XXIII_NRSITZ_00028 XXIII. Gesetzgebungsperiode Abgeordneter\r\nDr. Peter Pilz (Grüne): Frau\r... 28. Sitzung des\r\nNationalrates der Republik ...
1 Donnerstag, 5. Juli 2007 XXIII_NRSITZ_00028 XXIII. Gesetzgebungsperiode Abgeordneter\r\nDr. Günther Kräuter (SPÖ):\r\n... 28. Sitzung des\r\nNationalrates der Republik ...
2 Donnerstag, 5. Juli 2007 XXIII_NRSITZ_00028 XXIII. Gesetzgebungsperiode Abgeordneter\r\nHeinz-Christian Strache (FPÖ):... 28. Sitzung des\r\nNationalrates der Republik ...
In [12]:
# Die Spalte periode wird verkürzt, nur der Wert, nicht noch der Text 'Gesetzgebungsperiode'
df.periode = df.periode.str.split('.',expand=True)[0]

Datum

Nun wollen wir das Datum bereinigen. Leider gibt es hier ein paar Sonderfälle, nämlich Sitzungen über mehrere Tage. Wir nehmen hier einfach den letzten Tag. Im Beispiel also den 29. Mai 2009.

In [13]:
df.loc[df.key=='XXIV_NRSITZ_00023', 'datum'].iloc[0]
Out[13]:
'Mittwoch, 20., Dienstag, 26., Mittwoch, 27.,\r\nDonnerstag, 28., und Freitag, 29. Mai 2009'
In [14]:
df.datum = df.datum.str.rsplit(',', expand=True, n=1)[1].str.strip()
In [15]:
df.loc[df.key=='XXIV_NRSITZ_00023', 'datum'].iloc[0]
Out[15]:
'29. Mai 2009'

Nun wird das Textformat, z.B. '29. Mai 2009' in ein Python-Datumsformat umgewandelt.

In [16]:
# Interessanterweise gibt es sowohl Februar als auch Feber
map_monat = {'Jänner':1, 'Feber':2, 'Februar':2, 'März':3, 'April':4, 'Mai':5, 'Juni':6, 'Juli':7, 'August':8,
            'September':9, 'Oktober':10, 'November':11, 'Dezember':12}
def datum_umwandeln(txt):
    txt = txt.replace('\r\n',' ').replace('\xa0', ' ')
    tag, monat, jahr = txt.split(' ',maxsplit=2)
    datum = tag + str(map_monat[monat]) + '.' + str(jahr)
    return pd.to_datetime(datum, format='%d.%m.%Y')
In [17]:
df.datum = df.datum.apply(datum_umwandeln)
In [18]:
df.groupby(df.datum.dt.year)[['key']].count()
Out[18]:
key
datum
2006 357
2007 3158
2008 2763
2009 4319
2010 3417
2011 3629
2012 3273
2013 2888
2014 3506
2015 3139
2016 371
In [19]:
sns.set(context='talk', style='whitegrid')
sns.countplot(y=df.datum.dt.year, data=df)
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden pro Jahr')
sns.set(font_scale=1)
sns.axlabel('Reden', 'Jahr')
sns.plt.savefig('reden_pro_jahr.svg')
In [20]:
sns.set(context='talk', style='whitegrid')
sns.countplot(y=df.datum.dt.month, data=df)
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden pro Monat')
sns.set(font_scale=1)
sns.axlabel('Reden', 'Monat')
sns.plt.savefig('reden_pro_monat.svg')
In [21]:
sns.set(context='talk', style='whitegrid')
ax = sns.countplot(y=df.datum.dt.dayofweek, data=df)
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden pro Wochentag')
sns.set(font_scale=1)
sns.axlabel('Reden', 'Wochentag')
#labels = [item.get_text() for item in ax.get_yticklabels()]
labels = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
ax.set_yticklabels(labels)
sns.plt.savefig('reden_pro_wochentag.svg')
In [22]:
df_temp = (df.groupby([df.datum.dt.year, df.datum.dt.month])['key'].count()).unstack()
mask = df_temp.isnull()
df_temp = df_temp.fillna(value=0)
df_temp = df_temp.astype('int')
df_temp[:3]
Out[22]:
datum 1 2 3 4 5 6 7 8 9 10 11 12
datum
2006 0 0 0 0 0 0 0 0 0 43 159 155
2007 224 0 411 106 457 306 418 0 173 242 254 567
2008 407 0 325 266 297 402 476 0 238 67 33 252
In [23]:
sns.set(context='talk', style='whitegrid')
sns.heatmap(df_temp, annot=True, linewidths=.5, fmt='d', mask=mask)
#sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden (in 100 Reden)')
sns.set(font_scale=1)
sns.axlabel('Monat', 'Jahr')
sns.plt.savefig('reden_pro_jahr_monat.svg')
In [24]:
df_temp = (df.groupby([df.datum.dt.dayofweek, df.datum.dt.year])['key'].count()).unstack()
df_temp.index = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
mask = df_temp.isnull()
df_temp = df_temp.fillna(value=0)
df_temp = df_temp.astype('int')
df_temp[:3]
Out[24]:
datum 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
Mo 43 0 70 0 0 0 21 29 196 49 0
Di 0 381 387 585 219 725 470 289 733 241 0
Mi 133 936 731 981 1746 981 1102 810 1275 1647 170
In [25]:
sns.set(context='talk', style='whitegrid')
sns.heatmap(df_temp, annot=True, linewidths=.5, fmt='d', mask=mask)
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden (in 100 Reden)')
sns.set(font_scale=1)
sns.axlabel('Jahr', 'Wochentag')
sns.plt.savefig('reden_pro_wochentag_jahr.svg')
In [26]:
df_temp = (df.groupby([df.datum.dt.dayofweek, df.datum.dt.month])['key'].count()).unstack()
df_temp.index = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']
mask = df_temp.isnull()
df_temp = df_temp.fillna(value=0)
df_temp = df_temp.astype('int')
df_temp[:3]
Out[26]:
datum 1 2 3 4 5 6 7 8 9 10 11 12
Mo 0 196 39 29 21 28 0 0 0 95 0 0
Di 97 223 434 463 560 306 466 0 261 292 492 436
Mi 922 653 885 756 910 974 1200 36 788 839 941 1608
In [27]:
sns.set(context='talk', style='whitegrid')
sns.heatmap(df_temp, annot=True, linewidths=.5, fmt='d', mask=mask)
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden (in 100 Reden)')
sns.set(font_scale=1)
sns.axlabel('Monat', 'Wochentag')
sns.plt.savefig('reden_pro_wochentag_monat.svg')

Redner auslesen

Der Redner ist der erste Teil des Felds rede. Dieser soll herausgelöst und extra gespeichert werden, inklusive der Partei.

In [28]:
#Sonderzeichen entfernen
df.rede = df.rede.str.replace('\xa0',' ')
df.rede = df.rede.str.replace('\r\n',' ')
df.rede = df.rede.str.replace('\xad','')
df.rede = df.rede.str.replace('†', '')
df.rede = df.rede.str.replace('|', '')

Manuelle Korrektur von Sonderfällen

Sonderfall, erster Abschnitt der Rede fehlt, falsch in HTML abgebildet. Manuelle Korrektur.

In [29]:
mask = df.rede.str.startswith('Im Sicherheitsbereich bin ich zuständig')
rede_temp = df[mask].iloc[0].rede
rede_fehlend = '''Bundesministerin für Inneres Mag. Dr. Maria Theresia Fekter: Herr Präsident! Hohes Haus! 
Werte Damen und Herren auf der Besuchergalerie und vor den Bildschirmen! Liebe Kollegin Moser, Sie haben zwar 
die Ministerin Bures angesprochen, aber da ich das Wohnpaket verhandelt habe, weise ich darauf hin, dass Ihrer 
Aufmerksamkeit entgangen sein dürfte, dass wir für die thermische Sanierung im Wohnbau sehr viel Geld in die Hand 
nehmen (Abg. Dr. Moser: Leider nur 100! Wir brauchen 300!) und dass die Infrastrukturprojekte ja dazu gedacht sind, 
Arbeitsplätze zu schaffen, damit es nicht zu Arbeitslosigkeit kommt. Und das nützt den Menschen, Frau Kollegin Moser! 
(Beifall bei der ÖVP.) '''
df.loc[mask, 'rede'] = rede_fehlend + rede_temp

Sonderfall, bei dem die Rede zu früh beginnt und das Ende fehlt.

In [30]:
mask = df.rede.str.startswith('Abgeordneter Dr. Michael Spindelegger: Ich bedanke mich für das Vertrauen und nehme die Wahl sehr gerne an. (Allgemeiner Beifall.)')
rede_temp = df[mask].iloc[0].rede
rede_fehlend = '''umzugehen. Wir sind dazu bereit und laden Sie alle hier noch einmal ein, bei dieser Aufklärungsarbeit mitzuwirken. 
(Beifall bei der SPÖ und bei Abgeordneten der Grünen. – Abg. Grillitsch: Sie haben keine Verantwortung!)'''
start_pos = rede_temp.find('Abgeordneter Dr. Josef Cap (SPÖ)')
df.loc[mask, 'rede'] = rede_temp[start_pos:] + rede_fehlend

Bei Rednern aus der Regierung, wird deren Titel mitangeführt. Dieser muss gesondert behandelt werden. Übrigens beeindruckend, wie viele unterschiedliche Minister/Ministerinnen es gab - es sind 62. Zusätzlich gibt es noch andere "Titel", die nicht zur Regierung gehören, z.B. Volksanwalt, Schriftführerin. Diese werden auch gesondert erfasst.

In [31]:
TITEL_REGIERUNG = (
'Bundeskanzler', 
'Bundesminister für Arbeit, Soziales und Konsumentenschutz',
'Bundesminister für Europa, Integration und Äußeres',
'Bundesminister für Finanzen',
'Bundesminister für Finanzen Vizekanzler',
'Bundesminister für Gesundheit',
'Bundesminister für Gesundheit, Familie und Jugend',
'Bundesminister für Inneres',
'Bundesminister für Justiz',
'Bundesminister für Kunst und Kultur, Verfassung und Medien',
'Bundesminister für Kunst und Kultur, Verfassung und öffentlichen Dienst',
'Bundesminister für Land- und Forstwirtschaft, Umwelt und Wasserwirtschaft',
'Bundesminister für Landesverteidigung',
'Bundesminister für Landesverteidigung und Sport',
'Bundesminister für Soziales und Konsumentenschutz',
'Bundesminister für Verkehr, Innovation und Technologie',
'Bundesminister für Wirtschaft und Arbeit',
'Bundesminister für Wirtschaft, Familie und Jugend',
'Bundesminister für Wissenschaft und Forschung',
'Bundesminister für Wissenschaft, Forschung und Wirtschaft',
'Bundesminister für Wissenschaft, Forschung und Wirtschaft Vizekanzler',
'Bundesminister für europäische und internationale Angelegenheiten',
'Bundesminister für europäische und internationale Angelegenheiten Vizekanzler',
'Bundesminister für soziale Sicherheit, Generationen und Konsumentenschutz',
'Bundesminister für soziale Sicherheit, Generationen und Konsumentenschutz Vizekanzler',
'Bundesminister im Bundeskanzleramt',
'Bundesminister ohne Portefeuille',
'Bundesministerin  für Unterricht, Kunst und Kultur',
'Bundesministerin für Bildung und Frauen',
'Bundesministerin für Bildung, Wissenschaft und Kultur',
'Bundesministerin für Familien und Jugend',
'Bundesministerin für Finanzen',
'Bundesministerin für Frauen und öffentlichen Dienst',
'Bundesministerin für Frauen, Medien und Regionalpolitik',
'Bundesministerin für Frauen, Medien und öffentlichen Dienst',
'Bundesministerin für Gesundheit',
'Bundesministerin für Gesundheit und Frauen',
'Bundesministerin für Gesundheit, Familie und Jugend',
'Bundesministerin für Inneres',
'Bundesministerin für Justiz',
'Bundesministerin für Unterricht, Kunst und Kultur',
'Bundesministerin für Verkehr, Innovation und Technologie',
'Bundesministerin für Wissenschaft und Forschung',
'Bundesministerin für auswärtige Angelegenheiten',
'Bundesministerin für europäische und internationale Angelegenheiten',
'Bundesministerin für soziale Sicherheit, Generationen und Konsumentenschutz',
'Bundesministerin ohne Portefeuille',
'Staatssekretär im Bundesministerium für Finanzen',
'Staatssekretär im Bundesministerium für Verkehr, Innovation und Technologie',
'Staatssekretärin im Bundesministerium für soziale Sicherheit, Generationen und Konsumentenschutz',
'Staatssekretär im Bundeskanzleramt',
'Staatssekretärin im Bundesministerium für Wirtschaft, Familie und Jugend',
'Staatssekretär im Bundesministerium für europäische und internationale Angelegenheiten',
'Staatssekretärin im Bundesministerium für Verkehr, Innovation und Technologie',
'Staatssekretärin im Bundesministerium für Wirtschaft und Arbeit',
'Staatssekretär im Bundesministerium für Gesundheit und Frauen',
'Staatssekretär im Bundesministerium für soziale Sicherheit, Generationen und Konsumentenschutz',
'Staatssekretär im Bundesministerium für auswärtige Angelegenheiten',
'Staatssekretär im Bundesministerium für Inneres',
'Staatssekretärin im Bundeskanzleram',
'Staatssekretär im Bundesministerium für Wissenschaft, Forschung und Wirtschaft',
'Staatssekretärin im Bundesministerium für Finanzen'
)
TITEL_ANDERE = (
'Präsident des Rechnungshofes',
'Volksanwältin',
'Volksanwalt',
'Schriftführerin',
'Schriftführer',
'Präsident',
'Präsidentin',
'Berichterstatter',
'Berichterstatterin',
'Mitglied des Europäischen Parlaments'
)
# Stellt sicher, dass z.B. 'Bundesminister für europäische und internationale Angelegenheiten Vizekanzler' vor
# 'Bundesminister für europäische und internationale Angelegenheiten Vizekanzler'
TITEL_REGIERUNG = sorted(TITEL_REGIERUNG, reverse=True, key=len)
TITEL_ANDERE = sorted(TITEL_ANDERE, reverse=True, key=len)
In [32]:
len(TITEL_REGIERUNG)
Out[32]:
62
In [33]:
# Funktion um aus der Rede den Redner (Anrede, Name, Partei) auszulesen
def formatieren(rede):
    redner, rede_txt = rede.split(':', maxsplit=1)
    redner = redner.replace('\n',' ').strip()
    rede_txt = rede_txt.replace('\n',' ').strip()
    for titel in TITEL_REGIERUNG:
        if redner.startswith(titel):
            redner_anrede = titel
            redner_name = redner.replace(titel,'').strip()
            # In Ausnahmefall noch Anmerkung bei Namen , z.B. (mit Beifall von ... begrüßt)
            redner_name = re.sub(r'\([^)]*\)', '', redner_name)
            redner_partei = 'Regierung'
            rede_txt = re.sub(r'\([^)]*\)', '', rede_txt)
            return pd.Series({'redner_anrede': redner_anrede, 'redner_name': redner_name, 
                              'redner_partei': redner_partei, 'rede_txt': rede_txt})

    for titel in TITEL_ANDERE:
        if redner.startswith(titel):
            redner_anrede = titel
            redner_name = redner.replace(titel,'').strip()
            # In Ausnahmefall noch Anmerkung bei Namen , z.B. (mit Beifall von ... begrüßt)
            redner_name = re.sub(r'\([^)]*\)', '', redner_name)
            redner_partei = 'Andere'
            rede_txt = re.sub(r'\([^)]*\)', '', rede_txt)
            return pd.Series({'redner_anrede': redner_anrede, 'redner_name': redner_name, 
                              'redner_partei': redner_partei, 'rede_txt': rede_txt})

    redner_anrede, redner_name = redner.split(' ', maxsplit=1)
    redner_partei = redner_name[redner_name.rfind('(')+1:redner_name.rfind(')')].strip()
    if redner_partei in ('zur Geschäftsbehandlung', 
                        'in Übersetzung durch eine Gebärdensprachdolmetscherin',
                        'in Übersetzung durch einen Gebärdensprachdolmetscher',
                        'in Übersetzung durch die Gebärdensprachdolmetscherin',
                        'in Richtung SPÖ, die noch immer Beifall spendet',
                        'sich zunächst an Dr. Jarolim wendend',
                        'mit Beifall begrüßt', 
                        'die Höhe des Rednerpultes einstellend',
                        'fortsetzend'):
        redner_name = redner_name[:redner_name.rfind('(')]
        if redner_name.count('(') > 0:
            redner_partei = redner_name[redner_name.rfind('(')+1:redner_name.rfind(')')].strip()
        else:
            redner_partei = ''
    redner_name = redner_name[:redner_name.rfind('(')].strip()
    # Enfernen von Zwischenrufen, die sind immer in Klammern
    rede_txt = re.sub(r'\([^)]*\)', '', rede_txt)
    return pd.Series({'redner_anrede': redner_anrede, 'redner_name': redner_name, 
                      'redner_partei': redner_partei, 'rede_txt': rede_txt})
In [34]:
%%time
df = pd.concat([df, df.rede.apply(formatieren)], axis=1)
Wall time: 8.38 s
In [35]:
df[:3]
Out[35]:
datum key periode rede sitzung rede_txt redner_anrede redner_name redner_partei
0 2007-07-05 XXIII_NRSITZ_00028 XXIII Abgeordneter Dr. Peter Pilz (Grüne): Frau Präs... 28. Sitzung des\r\nNationalrates der Republik ... Frau Präsidentin! Meine sehr verehrten Damen u... Abgeordneter Dr. Peter Pilz Grüne
1 2007-07-05 XXIII_NRSITZ_00028 XXIII Abgeordneter Dr. Günther Kräuter (SPÖ): Sehr g... 28. Sitzung des\r\nNationalrates der Republik ... Sehr geehrte Frau Präsidentin! Herr Bundesmini... Abgeordneter Dr. Günther Kräuter SPÖ
2 2007-07-05 XXIII_NRSITZ_00028 XXIII Abgeordneter Heinz-Christian Strache (FPÖ): Se... 28. Sitzung des\r\nNationalrates der Republik ... Sehr geehrte Frau Präsidentin! Sehr geehrter H... Abgeordneter Heinz-Christian Strache FPÖ

Abgeordnete (männlich und weiblich) haben am meisten Reden gehalten. Verhältnis weiblich zu männlich ist 1 zu 2,6. Der Bundesminister für Arbeit, Soziales und Konsumentenschutz war dann der fleißigste mit 161 Reden.

In [36]:
df.redner_anrede.value_counts()
Out[36]:
Abgeordneter                                                                                      20803
Abgeordnete                                                                                        7968
Bundesminister für Arbeit, Soziales und Konsumentenschutz                                           161
Bundesminister für Land- und Forstwirtschaft, Umwelt und Wasserwirtschaft                           154
Bundeskanzler                                                                                       125
Bundesministerin für Justiz                                                                         104
Staatssekretär im Bundesministerium für Finanzen                                                    104
Bundesministerin für Inneres                                                                         92
Bundesminister für Wirtschaft, Familie und Jugend                                                    90
Bundesministerin für Unterricht, Kunst und Kultur                                                    88
Bundesministerin für Verkehr, Innovation und Technologie                                             78
Bundesminister für Finanzen Vizekanzler                                                              74
Bundesminister für Gesundheit                                                                        70
Präsident des Rechnungshofes                                                                         66
Bundesministerin für Finanzen                                                                        63
Staatssekretär im Bundeskanzleramt                                                                   49
Bundesminister für Wissenschaft und Forschung                                                        47
Bundesminister für Landesverteidigung und Sport                                                      42
Bundesminister für Wirtschaft und Arbeit                                                             38
Bundesminister für Soziales und Konsumentenschutz                                                    34
Bundesminister für Justiz                                                                            33
Bundesminister für Verkehr, Innovation und Technologie                                               29
Bundesminister für Finanzen                                                                          29
Staatssekretär im Bundesministerium für europäische und internationale Angelegenheiten               29
Bundesministerin für Gesundheit, Familie und Jugend                                                  27
Bundesministerin für Frauen und öffentlichen Dienst                                                  26
Staatssekretärin im Bundesministerium für Wirtschaft, Familie und Jugend                             25
Bundesminister für Wissenschaft, Forschung und Wirtschaft Vizekanzler                                24
Bundesminister für europäische und internationale Angelegenheiten Vizekanzler                        22
Bundesministerin für Bildung und Frauen                                                              22
                                                                                                  ...  
Bundesministerin für Gesundheit                                                                      14
Bundesminister für Landesverteidigung                                                                13
Bundesminister für Europa, Integration und Äußeres                                                   12
Bundesministerin für Frauen, Medien und öffentlichen Dienst                                          12
Volksanwalt                                                                                          12
Staatssekretärin im Bundesministerium für Verkehr, Innovation und Technologie                        12
Staatssekretärin im Bundesministerium für Wirtschaft und Arbeit                                      11
Staatssekretär im Bundesministerium für Wissenschaft, Forschung und Wirtschaft                        9
Bundesministerin für Familien und Jugend                                                              8
Bundesministerin für europäische und internationale Angelegenheiten                                   8
Bundesminister für Wissenschaft, Forschung und Wirtschaft                                             8
Präsident                                                                                             8
Berichterstatterin                                                                                    7
Bundesministerin für soziale Sicherheit, Generationen und Konsumentenschutz                           6
Staatssekretär im Bundesministerium für Inneres                                                       6
Bundesministerin ohne Portefeuille                                                                    4
Bundesminister im Bundeskanzleramt                                                                    4
Bundesministerin für Wissenschaft und Forschung                                                       4
Bundesminister für Kunst und Kultur, Verfassung und öffentlichen Dienst                               3
Staatssekretärin im Bundesministerium für Finanzen                                                    3
Bundesminister für soziale Sicherheit, Generationen und Konsumentenschutz                             3
Bundesministerin für Frauen, Medien und Regionalpolitik                                               2
Staatssekretär im Bundesministerium für soziale Sicherheit, Generationen und Konsumentenschutz        2
Bundesministerin für Bildung, Wissenschaft und Kultur                                                 1
Bundesministerin  für Unterricht, Kunst und Kultur                                                    1
Bundesministerin für Gesundheit und Frauen                                                            1
Staatssekretär im Bundesministerium für Verkehr, Innovation und Technologie                           1
Bundesminister für Gesundheit, Familie und Jugend                                                     1
Bundesministerin für auswärtige Angelegenheiten                                                       1
Bundesminister ohne Portefeuille                                                                      1
Name: redner_anrede, dtype: int64
In [37]:
len(df[df.redner_anrede == 'Abgeordneter']) / len(df[df.redner_anrede == 'Abgeordnete'])
Out[37]:
2.6108182730923697
In [38]:
len(df[df.redner_anrede == 'Abgeordnete']) / len(df[df.redner_anrede == 'Abgeordneter'])
Out[38]:
0.3830216795654473

Insgesamt gibt es 68 "Berufsbezeichnungen".

In [39]:
len(df.redner_anrede.unique())
Out[39]:
68

Das Thema 'Kultur' wurde in den unterschiedlichsten Ministerien behandelt.

In [40]:
df.loc[df.redner_anrede.str.contains('Kultur'), 'redner_anrede'].value_counts()
Out[40]:
Bundesministerin für Unterricht, Kunst und Kultur                          88
Bundesminister für Kunst und Kultur, Verfassung und Medien                 16
Bundesminister für Kunst und Kultur, Verfassung und öffentlichen Dienst     3
Bundesministerin  für Unterricht, Kunst und Kultur                          1
Bundesministerin für Bildung, Wissenschaft und Kultur                       1
Name: redner_anrede, dtype: int64
In [41]:
df_temp = df.loc[df.redner_anrede.str.contains('Kultur'), ['redner_anrede', 'datum']]
df_temp.groupby([df.datum.dt.year, df.redner_anrede])['redner_anrede'].count()
Out[41]:
datum  redner_anrede                                                          
2007   Bundesministerin für Bildung, Wissenschaft und Kultur                       1
       Bundesministerin für Unterricht, Kunst und Kultur                           8
2008   Bundesministerin für Unterricht, Kunst und Kultur                           7
2009   Bundesministerin für Unterricht, Kunst und Kultur                          15
2010   Bundesministerin für Unterricht, Kunst und Kultur                           7
2011   Bundesministerin für Unterricht, Kunst und Kultur                          12
2012   Bundesministerin für Unterricht, Kunst und Kultur                          20
2013   Bundesministerin  für Unterricht, Kunst und Kultur                          1
       Bundesministerin für Unterricht, Kunst und Kultur                          18
2014   Bundesminister für Kunst und Kultur, Verfassung und Medien                  7
       Bundesminister für Kunst und Kultur, Verfassung und öffentlichen Dienst     3
       Bundesministerin für Unterricht, Kunst und Kultur                           1
2015   Bundesminister für Kunst und Kultur, Verfassung und Medien                  9
Name: redner_anrede, dtype: int64

Wie hat sich das Verhätnis von Reden weiblicher Abgeordneter zu denen von männlichen über die Zeit entwickelt?

In [42]:
df_ratio = df.groupby(df.datum.dt.year)[['redner_anrede']].agg(lambda x: 
                                                               sum(x == 'Abgeordnete') / sum(x == 'Abgeordneter') * 100)
df_ratio.columns = ['Frau pro Mann']
df_ratio[:3]
Out[42]:
Frau pro Mann
datum
2006 41.525424
2007 43.976494
2008 40.273224
In [43]:
sns.set(context='talk', style='whitegrid')
sns.barplot(x='datum', y='Frau pro Mann', data=df_ratio.reset_index())
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Reden von weiblichen Abgeordneten pro 100 Reden von männlichen')
sns.set(font_scale=1)
sns.axlabel('Jahr', 'Anzahl')
sns.plt.savefig('geschlecht.svg')

Parteien

Nun schauen wir uns die Parteien an. Hier sieht man 2 Einträge, ohne Partei. Das wird manuell korregiert.

Im Folgenden behandle ich Reden von Regierungsmitgliedern unter der virtuellen Partei 'Regierung'.

In [44]:
df.redner_partei.value_counts()
Out[44]:
SPÖ                       7288
ÖVP                       6670
FPÖ                       5145
Grüne                     4398
BZÖ                       3301
Regierung                 1894
STRONACH                  1003
NEOS                       687
ohne Klubzugehörigkeit     227
Andere                     155
NEOS-LIF                    50
                             2
Name: redner_partei, dtype: int64

Manuelle Korrektur für 2 Fälle, in denen die Partei fehlt.

In [45]:
df.loc[df.redner_partei == '', ['redner_name', 'redner_partei']]
Out[45]:
redner_name redner_partei
2249 Wolfgang Zanger
20333 Peter Wurm
In [46]:
df.loc[(df.redner_partei == '') & (df.redner_name == 'Peter Wurm'), 'redner_partei'] = 'FPÖ'
df.loc[(df.redner_partei == '') & (df.redner_name == 'Wolfgang Zanger'), 'redner_partei'] = 'FPÖ'
In [47]:
df.loc[df.redner_partei == 'Andere', 'redner_anrede'].value_counts()
Out[47]:
Präsident des Rechnungshofes            66
Volksanwältin                           19
Mitglied des Europäischen Parlaments    15
Berichterstatter                        14
Präsidentin                             14
Volksanwalt                             12
Präsident                                8
Berichterstatterin                       7
Name: redner_anrede, dtype: int64
In [48]:
# NEOS und NEOS-LIF im folgenden als eine Partei betrachtet 
df.loc[df.redner_partei == 'NEOS-LIF', 'redner_partei'] = 'NEOS'
# Behandle 'ohne Klubzugehörigkeit' und 'Andere' gemeinsam als 'Andere'. 
# Das ist eine Vereinfachung, da Fokus auf den Parteien und Regierung liegt.
df.loc[df.redner_partei == 'ohne Klubzugehörigkeit', 'redner_partei'] = 'Andere'
In [49]:
df.redner_partei.value_counts()
Out[49]:
SPÖ          7288
ÖVP          6670
FPÖ          5147
Grüne        4398
BZÖ          3301
Regierung    1894
STRONACH     1003
NEOS          737
Andere        382
Name: redner_partei, dtype: int64

Definieren Reihenfolge der Parteien und zugehörige Farben.

In [50]:
parteien_ordnung = ['Regierung', 
                    'SPÖ', 
                    'ÖVP', 
                    'FPÖ', 
                    'Grüne', 
                    'BZÖ',
                    'STRONACH',
                    'NEOS',
                    'Andere']

# Farbennamen von http://www.luminoso.com/colors/
parteien_farben = ['grey', #Regierung 
                   'red', 
                   'black', 
                   'blue', 
                   'green', 
                   'orange',
                   'yellow',
                   'pink', 
                   'light grey'] #Andere
sns.palplot(sns.xkcd_palette(parteien_farben))
In [51]:
df.groupby(['redner_partei', 'periode'])[['key']].count().unstack()
Out[51]:
key
periode XXIII XXIV XXV
redner_partei
Andere 30.0 215.0 137.0
BZÖ 653.0 2648.0 NaN
FPÖ 890.0 3071.0 1186.0
Grüne 880.0 2517.0 1001.0
NEOS NaN NaN 737.0
Regierung 390.0 1076.0 428.0
SPÖ 1546.0 4130.0 1612.0
STRONACH NaN 287.0 716.0
ÖVP 1568.0 3623.0 1479.0
In [52]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben))
sns.countplot(y='redner_partei', data=df, order=parteien_ordnung)
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Reden pro Partei')
sns.set(font_scale=1)
sns.axlabel('Reden', '')
sns.plt.savefig('reden_pro_partei.svg')

Wie hat sich die relative Anzahl an Reden pro Partei über die Zeit verändert?

In [53]:
df_temp = df.groupby([df.datum.dt.year, 'redner_partei'])[['key']].count().unstack()
df_temp[:3]
Out[53]:
key
redner_partei Andere BZÖ FPÖ Grüne NEOS Regierung SPÖ STRONACH ÖVP
datum
2006 3.0 45.0 65.0 61.0 NaN 20.0 84.0 NaN 79.0
2007 13.0 337.0 447.0 466.0 NaN 205.0 856.0 NaN 834.0
2008 14.0 328.0 446.0 407.0 NaN 188.0 666.0 NaN 714.0
In [54]:
df_temp = df_temp.apply(lambda x: x / np.sum(x), axis=1) * 100
df_temp[:3]
Out[54]:
key
redner_partei Andere BZÖ FPÖ Grüne NEOS Regierung SPÖ STRONACH ÖVP
datum
2006 0.840336 12.605042 18.207283 17.086835 NaN 5.602241 23.529412 NaN 22.128852
2007 0.411653 10.671311 14.154528 14.756175 NaN 6.491450 27.105763 NaN 26.409120
2008 0.506696 11.871155 16.141875 14.730366 NaN 6.804198 24.104235 NaN 25.841477
In [55]:
df_temp = df_temp.stack().reset_index()
df_temp = df_temp.rename(columns={'redner_partei': 'Partei'})
df_temp[:3]
Out[55]:
datum Partei key
0 2006 Andere 0.840336
1 2006 BZÖ 12.605042
2 2006 FPÖ 18.207283
In [56]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben))
ax = sns.factorplot(x = 'datum', y = 'key', hue = 'Partei' , data = df_temp, size=8, hue_order=parteien_ordnung)
ax.set(ylim=(0, 30))
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('% Reden pro Partei')
sns.set(font_scale=1)
sns.axlabel('Jahr', 'Prozent')
sns.plt.savefig('reden_pro_partei_pro_jahr.svg')

Eine andere Darstellung der Verteilung der Reden pro Partei.

In [57]:
df_temp = (df.groupby([df.datum.dt.year, df.redner_partei])['key'].count()).unstack()
df_temp = df_temp[parteien_ordnung]
df_temp = df_temp.transpose()
mask = df_temp.isnull()
df_temp = df_temp.fillna(value=0)
df_temp = df_temp.astype('int')
df_temp[:13]
Out[57]:
datum 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
redner_partei
Regierung 20 205 188 237 181 255 208 190 203 185 22
SPÖ 84 856 666 1062 811 848 777 622 786 699 77
ÖVP 79 834 714 891 717 757 679 568 716 639 76
FPÖ 65 447 446 769 619 653 564 447 592 494 51
Grüne 61 466 407 632 496 517 460 403 489 418 49
BZÖ 45 337 328 699 511 558 466 357 0 0 0
STRONACH 0 0 0 0 0 0 68 256 349 295 35
NEOS 0 0 0 0 0 0 0 31 349 322 35
Andere 3 13 14 29 82 41 51 14 22 87 26
In [58]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben))
sns.heatmap(df_temp, annot=True, linewidths=.5, fmt='d', mask=mask)
#sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Anzahl Reden (in 100 Reden)')
sns.set(font_scale=1)
sns.axlabel('Partei', 'Jahr')
#sns.plt.savefig('geschlecht.svg')

Wie hat sich das Verhältnis der Reden von weiblichen Abgeordneten zu denen von männlichen pro Partei (exkl. Regierung) über die Zeit entwickelt?

In [59]:
df_ratio = df.groupby([df.datum.dt.year, 'redner_partei'])[['redner_anrede']].agg(lambda x: 
                                                               sum(x == 'Abgeordnete') / sum(x == 'Abgeordneter')*100)
df_ratio = df_ratio.reset_index()
df_ratio = df_ratio.rename(columns={'redner_partei': 'Partei', 'redner_anrede': 'Rednerin pro Redner'})

df_ratio = df_ratio.loc[df_ratio.Partei != 'Regierung']
df_ratio[:3]
Out[59]:
datum Partei Rednerin pro Redner
0 2006 Andere NaN
1 2006 BZÖ 15.384615
2 2006 FPÖ 12.068966
In [60]:
parteien_ordnung_ohne_regierung = parteien_ordnung.copy()
parteien_ordnung_ohne_regierung.remove('Regierung')
parteien_farben_ohne_regierung = parteien_farben.copy()
parteien_farben_ohne_regierung.remove('grey')
In [62]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben_ohne_regierung))

ax = sns.factorplot(x = 'datum', y = 'Rednerin pro Redner', hue = 'Partei' , data = df_ratio, size=8, 
                    hue_order=parteien_ordnung_ohne_regierung)
ax.set(ylim=(0, 110))
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Reden weiblicher Abgeordneter je 100 Reden von männlichen Abgeordneten')
sns.set(font_scale=1)
sns.axlabel('Jahr', 'Reden')
sns.plt.savefig('geschlecht_je_partei.svg')

Wie ist z.B. der Rückgang bei den NEOS zu erklären?

In [63]:
df[df.redner_partei == 'NEOS'].groupby([df.datum.dt.year, 'redner_anrede', 'redner_name'])[['key']].count().unstack('datum')
Out[63]:
key
datum 2013 2014 2015 2016
redner_anrede redner_name
Abgeordnete Claudia Angela Gamon, MSc (WU) NaN NaN 6.0 3.0
Mag. Beate Meinl-Reisinger, MES 4.0 50.0 33.0 NaN
Mag. Dr. Angelika Rosa Mlinar 3.0 13.0 NaN NaN
Abgeordneter Dr. Nikolaus Scherak 1.0 32.0 57.0 6.0
Dr. Rainer Hable 8.0 55.0 19.0 1.0
Josef Schellhorn NaN 21.0 34.0 4.0
Mag. Christoph Vavrik 1.0 13.0 15.0 NaN
Mag. Dr. Matthias Strolz 6.0 53.0 41.0 6.0
Mag. Gerald Loacker 4.0 55.0 54.0 9.0
Mag. Nikolaus Alm 1.0 24.0 26.0 4.0
Mag. Nikolaus Scherak 2.0 NaN NaN NaN
Michael Pock 1.0 33.0 37.0 2.0
In [65]:
df[df.redner_partei == 'STRONACH'].groupby([df.datum.dt.year, 'redner_anrede', 'redner_name'])[['key']].count().unstack('datum')
Out[65]:
key
datum 2012 2013 2014 2015 2016
redner_anrede redner_name
Abgeordnete Dr. Jessi Lintl NaN NaN 13.0 11.0 NaN
Dr. Kathrin Nachbaur NaN 7.0 51.0 18.0 NaN
Elisabeth Kaufmann-Bruckberger 16.0 16.0 NaN NaN NaN
Ing. Waltraud Dietrich NaN 5.0 36.0 41.0 4.0
Martina Schenk NaN 37.0 22.0 29.0 6.0
Ulrike Weigerstorfer NaN NaN 14.0 16.0 3.0
Abgeordneter Christoph Hagen 21.0 65.0 35.0 47.0 7.0
Dr. Georg Vetter NaN 9.0 42.0 12.0 NaN
Dr. Marcus Franz NaN 3.0 34.0 10.0 NaN
Erich Tadler 3.0 18.0 NaN NaN NaN
Frank Stronach NaN 2.0 1.0 NaN NaN
Ing. Robert Lugar 17.0 51.0 38.0 48.0 9.0
Leopold Steinbichler NaN 5.0 41.0 46.0 6.0
Rouven Ertlschweiger, MSc NaN NaN 22.0 17.0 NaN
Stefan Markowitz 11.0 38.0 NaN NaN NaN

Länge der Reden

In [66]:
# from http://stackoverflow.com/questions/17507876/trying-to-count-words-in-a-string
def anzahl_woerter(txt):
    return len(re.findall(r'\b\w+\b', txt))
In [67]:
anzahl_woerter('Johnny.Appleseed!is:a*good&farmer')
Out[67]:
6
In [68]:
%%time
df['rede_anzahl_woerter'] = df.rede_txt.apply(anzahl_woerter)
Wall time: 8.65 s
In [69]:
df[:3]
Out[69]:
datum key periode rede sitzung rede_txt redner_anrede redner_name redner_partei rede_anzahl_woerter
0 2007-07-05 XXIII_NRSITZ_00028 XXIII Abgeordneter Dr. Peter Pilz (Grüne): Frau Präs... 28. Sitzung des\r\nNationalrates der Republik ... Frau Präsidentin! Meine sehr verehrten Damen u... Abgeordneter Dr. Peter Pilz Grüne 1743
1 2007-07-05 XXIII_NRSITZ_00028 XXIII Abgeordneter Dr. Günther Kräuter (SPÖ): Sehr g... 28. Sitzung des\r\nNationalrates der Republik ... Sehr geehrte Frau Präsidentin! Herr Bundesmini... Abgeordneter Dr. Günther Kräuter SPÖ 1051
2 2007-07-05 XXIII_NRSITZ_00028 XXIII Abgeordneter Heinz-Christian Strache (FPÖ): Se... 28. Sitzung des\r\nNationalrates der Republik ... Sehr geehrte Frau Präsidentin! Sehr geehrter H... Abgeordneter Heinz-Christian Strache FPÖ 2044
In [70]:
df_temp = (df.groupby(df.datum.dt.year)
            .agg({'rede_anzahl_woerter': 'sum', 'key': 'count'})
            .rename(columns={'rede_anzahl_woerter': 'woerter', 'key': 'reden'}))
df_temp['woerter_pro_rede'] = df_temp['woerter'] / df_temp['reden']
df_temp[:3]
Out[70]:
woerter reden woerter_pro_rede
datum
2006 228325 357 639.565826
2007 1914967 3158 606.386004
2008 1531344 2763 554.232356
In [71]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben))
sns.barplot(x='datum', y='woerter_pro_rede', data=df_temp.reset_index())
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Durchschnittliche Anzahl an Worten pro Rede je Jahr')
sns.set(font_scale=1)
sns.axlabel('Jahr', 'Wörter pro Rede')
sns.plt.savefig('woerter_pro_rede_je_jahr.svg')
In [72]:
df_temp = (df.groupby('redner_partei')
            .agg({'rede_anzahl_woerter': 'sum', 'key': 'count'})
            .rename(columns={'rede_anzahl_woerter': 'woerter', 'key': 'reden'}))
df_temp['woerter_pro_rede'] = df_temp['woerter'] / df_temp['reden']
df_temp[:3]
Out[72]:
woerter reden woerter_pro_rede
redner_partei
Andere 264319 382 691.934555
BZÖ 2214068 3301 670.726447
FPÖ 3264382 5147 634.230037
In [73]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben))
sns.barplot(x='redner_partei', y='woerter_pro_rede', data=df_temp.reset_index(), order=parteien_ordnung)
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Durchschnittliche Anzahl an Worten pro Rede je Partei')
sns.set(font_scale=1)
sns.axlabel('Partei', 'Wörter pro Rede')
sns.plt.savefig('woerter_pro_rede_je_partei.svg')
In [74]:
df_temp = (df.groupby([df.datum.dt.year, 'redner_partei'])
            .agg({'rede_anzahl_woerter': 'sum', 'key': 'count'})
            .rename(columns={'rede_anzahl_woerter': 'woerter', 'key': 'reden'}))
df_temp['woerter_pro_rede'] = df_temp['woerter'] / df_temp['reden']
df_temp = df_temp.woerter_pro_rede
df_temp = df_temp.unstack()
df_temp = df_temp / 100
df_temp = df_temp[parteien_ordnung]
df_temp = df_temp.transpose()
df_temp[:3]
Out[74]:
datum 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016
redner_partei
Regierung 9.391000 11.010146 8.871117 9.293797 9.777956 8.756353 9.385240 8.544526 10.221330 9.871946 7.312727
SPÖ 5.639405 4.340479 4.421877 4.239689 4.433662 4.677441 4.614363 4.818955 4.607774 4.690973 4.569870
ÖVP 5.384051 4.149580 4.015714 4.644961 4.617238 4.767503 5.004050 4.872518 4.784330 4.864679 4.488947
In [75]:
sns.set(context='talk', style='whitegrid', palette=sns.xkcd_palette(parteien_farben))
sns.heatmap(df_temp, annot=True, linewidths=.5)
sns.despine()
sns.set(font_scale=1.5)
sns.plt.title('Durchschnittliche Wörter pro Rede (in 100 Wörtern)')
sns.set(font_scale=1)
sns.axlabel('Jahr', 'Partei')
#sns.plt.savefig('geschlecht.svg')