Vi har for vane at opdele politik i højre- og venstrefløj. Den historiske årsag er oplysende: det var sådan medlemmerne af den franske nationalforsamling placerede sig efter den franske revolution i 1789. Dem til højre i salen var loyale over for konge og kirke, dem til venstre støttede revolutionen. På den måde undgik man de værste albuehug og slåskampe mens man skændtes om Frankrings fremtid.

Hvordan burde de danske medlemmer af folketinget sidde i dag, hvis vi ville minimere risikoen for den slags håndgemæng? Spurgt på en anden måde: hvordan placerer man danske politiker i et lokale så deres politiske uenighed afspejles bedst muligt af deres indbyrdes afstande på en to-dimensionel flade?

Der findes faktisk en simpel matematisk metode til at finde ud af det på. Den kaldes en 'principal component analyse' (pca), og bliver brugt flittigt i maskinlæring og til at lave undersøgende dataanalyse. Gevinsten ved at bruge en pca på folketingets afstemninger er, at vi kan se om de politiske partier vitterlig stemmer i forhold til det vil forestiller os som en højre- og venstrefløj i dansk politik. Vi vil med andre ord kunne svare på om det virkelig er rigtig, at Enhedslisten og Dansk Folkeparti er længst fra hinanden. Vi vil også kunne finde ud af, hvordan partierne grupperer sig i forhold til andre akser, hvor afstandene måske er lige så store. Da der jo er lige så mange politiske synspunkter i folketinget som der er folketingsmedlemmer, er pca'en en rigtig god måde til at reducere de mange uenigheder ned til de to eller tre mest betydningsfulde uenigheds-typer der kendetegner dansk politik. En analyse på tværs af årene vil desuden kunne vise, hvordan partierne har bevæget sig i forhold til hinanden i løbet af årene.

Vi starter med at se på det danske folketing anno 2020:

  Enhedslisten: 13 mandater
  Socialistisk Folkeparti: 15 mandater
  Siumut : 1 mandat
  Socialdemokratiet: 48 mandater
  Alternativet: 1 mandat
  Uden for folketingsgrupperne: 4 mandater
  Radikale Venstre: 16 mandater
  Sambandsflokkurin: 1 mandat
  Javnaðarflokkurin: 1 mandat
  Inuit Ataqatigiit: 1 mandat
  Venstre: 42 mandater
  Det Konservative Folkeparti: 13 mandater
  Liberal Alliance: 3 mandater
  Dansk Folkeparti: 16 mandater
  Nye Borgerlige: 4 mandater
</td>

Der er ti danske partier og fire oversøiske. Og så er der fire (pr. 4. dec 5) mandater uden for folketingsgrupperne som udgør løsgængerne fra de Frie Grønne samt Simon Emil Ammitzbøll-Bille. Alle data finder vi via den frit tilgængelige database på oda.ft.dk.

import sys
import pyodbc 
import pandas as pd
import numpy as np 
import matplotlib.pyplot as plt
import xml.etree.ElementTree as ET
from collections import Counter
from sklearn.decomposition import PCA
from adjustText import adjust_text
import seaborn as sns
sns.set_style('white')
sns.set_context('notebook')
plt.rcParams["font.family"] = "sans-serif"
PLOTS_DIR = 'images'
%matplotlib inline

Vi starter med at kalde SQL serveren og skrive en søgning til oda-databasen, hvorefter resultatet importeres til en pandas dataframe. Den periode vi er interesseret i er perioden 2019-2020 som har id = 151.

periodid = 151
conn = pyodbc.connect('Driver={SQL Server};'
                      'Server=HUM1006903\SQLEXPRESS;'
                      'Database=oda_20201103;'
                      #'Database=oda;'
                      'Trusted_Connection=yes;')
cursor = conn.cursor()

sql_query = pd.read_sql_query('SELECT \
        oda.dbo.Afstemning.id AS afstemning, \
        oda.dbo.Sag.periodeid, \
        oda.dbo.Periode.titel AS periode, \
        oda.dbo.Sag.titel AS titel, \
        oda.dbo.Sag.resume, \
        oda.dbo.Afstemning.konklusion, \
        oda.dbo.Sagstrin.dato, \
        oda.dbo.Stemme.typeid AS stemme, \
        oda.dbo.Aktør.fornavn, \
        oda.dbo.Aktør.efternavn, \
        oda.dbo.Aktør.biografi \
    FROM oda.dbo.Afstemning \
    JOIN oda.dbo.Stemme ON oda.dbo.Afstemning.id = oda.dbo.Stemme.afstemningid \
    JOIN oda.dbo.Aktør ON oda.dbo.Aktør.id = oda.dbo.Stemme.aktørid \
    JOIN oda.dbo.Sagstrin ON oda.dbo.Sagstrin.id = oda.dbo.Afstemning.sagstrinid \
    JOIN oda.dbo.Sag ON oda.dbo.Sag.id = oda.dbo.Sagstrin.sagid \
    JOIN oda.dbo.Periode ON oda.dbo.Sag.periodeid = oda.dbo.Periode.id \
    WHERE oda.dbo.Sag.periodeid='+str(periodid)+';', conn)

sql_query.head()
afstemning periodeid periode titel resume konklusion dato stemme fornavn efternavn biografi
0 7165 151 2019-20 Forslag til folketingsbeslutning om danske mil... Med folketingsbeslutningen får regeringen Folk... Forslaget blev vedtaget. For stemte 106 (S, V,... 2019-10-24 1 Erling Bonnesen <member><url>/medlemmer/mf/e/erling-bonnesen</...
1 7165 151 2019-20 Forslag til folketingsbeslutning om danske mil... Med folketingsbeslutningen får regeringen Folk... Forslaget blev vedtaget. For stemte 106 (S, V,... 2019-10-24 1 Morten Bødskov <member><url>/medlemmer/mf/m/morten-boedskov</...
2 7165 151 2019-20 Forslag til folketingsbeslutning om danske mil... Med folketingsbeslutningen får regeringen Folk... Forslaget blev vedtaget. For stemte 106 (S, V,... 2019-10-24 1 Bent Bøgsted <member><url>/medlemmer/mf/b/bent-boegsted</ur...
3 7165 151 2019-20 Forslag til folketingsbeslutning om danske mil... Med folketingsbeslutningen får regeringen Folk... Forslaget blev vedtaget. For stemte 106 (S, V,... 2019-10-24 1 Lennart Damsbo-Andersen <member><url>/medlemmer/mf/l/lennart-damsbo-an...
4 7165 151 2019-20 Forslag til folketingsbeslutning om danske mil... Med folketingsbeslutningen får regeringen Folk... Forslaget blev vedtaget. For stemte 106 (S, V,... 2019-10-24 1 Louise Schack Elholm <member><url>/medlemmer/mf/l/louise-schack-elh...
print(len(sql_query.afstemning.unique()))
print(len(sql_query))
323
57817

Som man kan se, er der kun 323 afstemninger og knap 58.000 rækker i dataframen. Coronakrisen i 2020 betød at afstemninger mellem 28. maj 2020 og slut oktober blev foretaget ved håndsoprækkelse og derfor ikke er inkluderet i databasen.

I det følgende skal data renses og formatteres. Vi kan se, at tabellen ikke indeholder nogen kolonne, der angiver hvilket parti folketingsmedlemmerne tilhører. Det haves ikke i databasen. Eneste sted jeg kan se, at man kan finde et folketingsmedlems partitilknytning i data er i "biografi"-kolonnen, som består af en masse XML tags. For at ekstrahere partinavnet, der er placeret mellem tagsene "party", bruger jeg bibliotekten xml.etree:

party = []
for bio_string in sql_query['biografi'].values:
    try:
        root = ET.fromstring(bio_string)
        for child in root.findall("./party"):
            party.append(child.text)
    except Exception as e:
        party.append(None)
        continue
sql_query['party'] = party
#sql_query.party.unique()

Næste skridt i rensningen af data består i at omkode de enkelte stemmer så deres numeriske værdi er normaliseret og kan bruges i analysen. Folketingets database har kodet dem sådan at et 1-tal betyder en stemme FOR, et 2-tal betyder IMOD, et 3-tal betyder FRAVÆR, og et 4-tal betyder "Hverken for eller imod". I stedet koder jeg dem sådan at 1 betyder FOR, -1 betyder "IMOD", og 0 betyder "hverken for eller imod. Desuden samler jeg for- og efternavn og beholder kun de kolonner, vi har brug for:

sql_query['navn'] = sql_query[['fornavn', 'efternavn']].agg(' '.join, axis=1)

df = sql_query[['afstemning', 'titel', 'resume', 'konklusion', 'navn', 'party', 'stemme']]
df['stemme'].replace(to_replace=2, value=-1, inplace=True)
df['stemme'].replace(to_replace=4, value=0, inplace=True)
df.tail()
afstemning titel resume konklusion navn party stemme
57812 7490 Folketinget konstaterer, at det aftalte loft o... Forslaget blev vedtaget. For stemte 54 (S, RV,... Birgitte Bergman Det Konservative Folkeparti 0
57813 7490 Folketinget konstaterer, at det aftalte loft o... Forslaget blev vedtaget. For stemte 54 (S, RV,... Birgitte Vind Socialdemokratiet 0
57814 7490 Folketinget konstaterer, at det aftalte loft o... Forslaget blev vedtaget. For stemte 54 (S, RV,... Pernille Vermund Nye Borgerlige 0
57815 7490 Folketinget konstaterer, at det aftalte loft o... Forslaget blev vedtaget. For stemte 54 (S, RV,... Theresa Berg Andersen Socialistisk Folkeparti 0
57816 7490 Folketinget konstaterer, at det aftalte loft o... Forslaget blev vedtaget. For stemte 54 (S, RV,... Ruben Kidde Radikale Venstre 0

Lad os lige se hvor mange stemmer der er i hver kategori:

Counter(df.stemme)
Counter({1: 22567, -1: 11244, 3: 20853, 0: 3153})

Desværre viser det sig at der er rigtig mange fravær i folketinget (mange 3-taller), og manglende stemmer gør det vanskeligt at foretage en ordentlig PCA, fordi de gængs PCA-algoritmer ikke kan klare NaNs. Da der er mødepligt i folketinget, og alle folketingsmedlemmer SKAL stemme ved alle afstemninger, er det jo en mærkelig sag. Men det viser sig, at folketingets partier benytter sig af såkaldte 'clearingsaftaler', som er private aftaler mellem de forskellige folketingsgrupper. Aftalerne sikrer, at et antal folketingsmedlemmer fra hver partigruppe kan få 'fri' fra afstemningerne i folketingssalen, uden at der derved rokkes ved, hvilke partier som har flertal i Folketinget, eller ved, at der skal være mindst 90 medlemmer til stede, for at Folketinget er beslutningsdygtigt. Clearingaftalerne giver dermed mulighed for, at de politiske aktiviteter ikke går i stå, selv om der er møde og afstemninger i salen. Aftalerne giver plads til, at medlemmerne kan deltage i f.eks. politiske møder eller deltage i andre aktiviteter uden for Christiansborg, uden at afstemningerne i folketingssalen af den grund får et utilsigtet udfald.

I praksis indgås aftalerne typisk ved, at partigrupperne parvist aftaler for en hel folketingssamling, hvor mange medlemmer hver partigruppe kan 'cleare' hos hinanden - dvs. give lov til at blive væk fra afstemningerne, fordi den politiske modpart også beder et antal medlemmer blive væk. Når et medlem er clearet, stemmer medlemmet ikke i salen den pågældende dag. Hvordan clearingerne fordeles på medlemmer i partigrupperne kan veksle fra dag til dag, alt efter hvem der har behov for at være fri for at deltage i afstemningerne i salen. Det er typisk gruppesekretæren i den enkelte folketingsgruppe, som koordinerer fordelingen af clearingerne og sikrer, at man kan stille med det aftalte antal medlemmer ved eventuelle afstemninger. Det er også typisk gruppesekretæren, som tager kontakt til sin modpart i de andre partigrupper, hvis man pludselig har mandefald på grund af f.eks. sygdom eller lignende, og aftaler de nødvendige yderligere clearinger for en relevant periode. Det typiske mønster for clearingaftalerne er, at regeringspartierne clearer medlemmer med deres umiddelbare modpart. Dermed sikrer man lettest, at den politiske balance er bevaret på trods af clearingerne.

Efter en samtale med partisekretær Annette Lind (S), som i nuværende folketingssamling en den der sammen med Erling Bonnesen (V) koordinerer clearningsaftalerne for alle partierne, forstå jeg at hvis en person bliver clearet, så vil den person ALTID stemme ihht. partilinjen. Det vil sige at jeg kan skrive en funktion, der erstatter alle fravær med typetallet ("mode") for partiet for den givne afstemning. I tilfælde af at fravær er den hyppigste adfærd, vælger jeg i stedet den anden mest hyppige stemmetype. Og hvis alle medlemmer af et parti har været fraværende ved en afstemning (hvilket hyppigt sker for de grønlandske og færøske stemmer), sætter jeg dem til at være hverken for eller imod, dvs. til 0. Hvis et folketingsmedlem er "Uden for folketingsgrupperne", ændrer jeg FRAVÆR til "Hverken for eller imod" da jeg ikke kunne få bekræftet om løsgængerne også benytter sig af clearningsaftaler.

def get_most_frequent_vote(afstemning, party):
    df_ap = df[(df.afstemning == afstemning) & (df.party == party)]
    party_votes = df_ap.stemme.values

    cnt = Counter(party_votes)
    mostfrequent_vote = cnt.most_common()[0][0]
    if mostfrequent_vote == 3:
        try:
            mostfrequent_vote = cnt.most_common()[1][0] # set the second most frequent vote as the most frequent one.
        except: # if all members of the party have been absent
            mostfrequent_vote = 0 # set their most frequent vote to be "abstain"  

    return mostfrequent_vote

for i, row in df.iterrows():
    if row['stemme'] == 3:
        if row['party'] == 'Uden for folketingsgrupperne':
            df.at[i,'stemme'] = 0
        else:
            ifor_val = get_most_frequent_vote(row['afstemning'], row['party'])
            df.at[i,'stemme'] = ifor_val
Counter(df.stemme)
Counter({1: 35397, -1: 17254, 0: 5166})

For at få data i det rigtige format, bliver vi dernæst nød til at reorganisere tabellen således at rækkerne viser de enkelte folketingsmedlemmer, søjlerne de enkelte afstemninger, og selve cellerne indeholder så stemmerne. Vi kan bruge pivot-funtionen i python:

dp = df.pivot_table(index = ['navn', 'party'], columns=['afstemning'], values=['stemme'])
dp.dropna(inplace=True)
dp.columns = [col[1] for col in dp.columns] # get rid of the extra multicolumn "vote"
dp = dp.reset_index(level=['navn', 'party']) # make the multiindex [name, party] into two columns
dp.head()
navn party 7165 7166 7167 7168 7169 7170 7171 7172 ... 7481 7482 7483 7484 7485 7486 7487 7488 7489 7490
0 Aaja Chemnitz Larsen Inuit Ataqatigiit 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
1 Aki-Matilda Høegh-Dam Siumut 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
2 Alex Vanopslagh Liberal Alliance 1.0 1.0 1.0 -1.0 -1.0 1.0 1.0 -1.0 ... -1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.0
3 Anders Kronborg Socialdemokratiet 1.0 1.0 1.0 -1.0 -1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
4 Andreas Steenberg Radikale Venstre 1.0 1.0 1.0 -1.0 -1.0 1.0 -1.0 1.0 ... -1.0 -1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 0.0

5 rows × 325 columns

Dernæst giver vi partierne en farve, så vi kan kende forskel på dem på det resulterende plot:

# compute the color that each MP should be, based on their party. color codes are taken from https://www.dr.dk/om-dr/designmanager/temapakker/folketingsvalg-2019
color_dict = {
        'Enhedslisten' : '#E6801A',
        'Socialistisk Folkeparti'  : '#E07EA8',
        'Sambandsflokkurin' : '#41b6c4',
        'Javnaðarflokkurin' : '#67001f',
        'Socialdemokratiet' : '#A82721',
        'Siumut' : '#ef3b2c',
        'Radikale Venstre' : '#733280',
        'Inuit Ataqatigiit' : '#980043',
        'Det Konservative Folkeparti' : '#96B226',
        'Liberal Alliance'  : '#3FB2BE',
        'Venstre' : '#254264',
        'Dansk Folkeparti': '#EAC73E',
        'Uden for folketingsgrupperne' : '#737373',
        'nan' : 'black',
        'Alternativet' : '#2B8738',
        'Nye Borgerlige' : '#127B7F',
        'Kristendemokraterne' : '#8B8474',
        'Klaus Riskær Pedersen' : '#6C8BB8',
        'Stram Kurs' : '#998F4D', 
        'Nunatta Qitornai' : '#c51b8a',
        'Tjóðveldi' : '#a6d96a'
}
def party_color(x):
    return color_dict.get(str(x),'black')

colors = [party_color(x) for x in dp['party']]

De originale data har lige så mange dimensioner som folketingsmedlemmer, men pca'en reducerer dem ned til tre eller endda kun to dimensioner. De to/tre dimensioner er så til gengæld dem, der viser størst varians i folketingsmedlemmernes stemmeadfærd, og kan derfor bruges som indikatorer for hvor meget folketingsmedlemmerne er uenige med hinanden. Dimensionerne er desuden ortogonale på hinanden, hvilket betyder at de er uafhængige af hinanden. Enhver korrelation mellem afstemningerne transformeres således til linæere ukorrelerede variable der kaldes 'komponenter'. PCA er altså en "usuperviseret" metode, der beregnet afstanden mellem partierne som en kompleks blanding af, hvordan der blev stemt i de 323 afstemninger der blev registreret i folketingssamlingen 2019-2020.

For at dimensions-reduktionen kan give et nogenlunde retvisende billede af forskellene er det dog vigtigt at de 2-3 principale komponenter fanger størstedelen af variansen i data. Forneden vælger vi antallet af komponenter til at være 3, og kalder den første komponent for xvector, den anden komponent for yvector, og den tredje zvector.

num_folketingsmedlemmer = len(dp)
num_bills = len(dp.columns)-2
bills = dp.columns[2:num_bills+2]
dat = dp.iloc[:,2:num_bills+2]

pca = PCA(n_components=3)
pca.fit(dat)
xvector = pca.components_[0] 
yvector = pca.components_[1]
zvector = pca.components_[2]

xs = pca.transform(dat)[:,0] 
ys = pca.transform(dat)[:,1]
zs = pca.transform(dat)[:,2]
pca.explained_variance_, pca.explained_variance_ratio_
(array([59.93278144, 25.24262653, 16.02101912]),
 array([0.44414989, 0.18706807, 0.11872858]))

Som det kan ses fanger den første komponent 44,4 procent af variansen i data. Det er ikke så meget som håbet (i de tidligere folketingsår kommer den typisk op på 60-70%), men skyldes nok det specielle Corona-år vi har haft. Vi må leve med det, og kan nu plotte resultatet for de to første komponenter:

fig, ax = plt.subplots()

ix_high = np.argsort(xvector)[-5:] # returns an array of sorted indexes of the components
ix_low = np.argsort(xvector)[:5] 
iy_high = np.argsort(yvector)[-5:] # returns an array of sorted indexes of the components
iy_low = np.argsort(yvector)[:5] 
ix_highest_and_lowest_comps = np.append(ix_high, ix_low)
iy_highest_and_lowest_comps = np.append(iy_high, iy_low)

def get_arr_index_colors(color):
    # returns an array of indexes in the colors array corresponding to a certain party with color "color"
    col_mask = np.where(np.array(colors) == color,True,False)
    col_index = np.arange(0, len(colors))[col_mask]
    return col_index

for color in np.unique(colors):
    ix_color = get_arr_index_colors(color)
    ax.scatter(xs[ix_color], ys[ix_color], c = color, label = list(color_dict.keys())[list(color_dict.values()).index(color)])
    for i in ix_color:
        ax.annotate(dp.iloc[i]['navn'], (xs[i], ys[i]), fontsize=2)

for i in ix_highest_and_lowest_comps:
# arrows project features as vectors onto PC axes
    plt.arrow(0, 0, xvector[i]*max(xs)*2, yvector[i]*max(ys)*2,
              color='grey', width=0.0005, head_width=0.005)
    texts = [plt.text(xvector[i]*max(xs)*2.2, yvector[i]*max(ys)*2.2,
             list(dat.columns.values)[i], color='black', fontsize=3)]

for i in iy_highest_and_lowest_comps:
# arrows project features as vectors onto PC axes
    plt.arrow(0, 0, xvector[i]*max(xs)*2, yvector[i]*max(ys)*2,
              color='grey', width=0.0005, head_width=0.005)
    plt.text(xvector[i]*max(xs)*2.2, yvector[i]*max(ys)*2.2,
             list(dat.columns.values)[i], color='black', fontsize=3)

plt.scatter(0,0, color='white', s=4, zorder=20)

adjust_text(texts)

lgd = ax.legend(title=str(num_bills)+' afstemninger', prop={'size': 10}, bbox_to_anchor=(1.05, 1))
ax.set_title('Folketingsperiode ' + period_txt, fontsize=14)

# invert the x-axis so that the "left wing" goes to the left and the "right wing" to the right. First grab a reference to the current axes and then set the xlimits to be the reverse of the current xlimits
ax = plt.gca()
ax.set_xlim(ax.get_xlim()[::-1]) # vi vender akserne om, så det matcher med det visuelle udtryk om at venstrefløjen er på venstre side og højrefløjen på højre side.
ax.set_ylim(ax.get_ylim()[::-1])
#ax.set_xlim([25,-15])
#ax.set_ylim([12.5,-12.5])

#plt.tight_layout()


# Remember: save as pdf and transparent=True for Adobe Illustrator
if not os.path.exists(PLOTS_DIR):
    os.makedirs(PLOTS_DIR)

plt.savefig(os.path.join(PLOTS_DIR, 'ft'+str(periodid)+'.png'), bbox_extra_artists=(lgd,), bbox_inches='tight', transparent=True, dpi=800)
plt.savefig(os.path.join(PLOTS_DIR, 'ft'+str(periodid)+'.pdf'), bbox_extra_artists=(lgd,), bbox_inches='tight', transparent=True, dpi=800)
plt.close()

pca-resultat for folketinget 2019-2020

x-aksen giver et ganske godt billede af hvad vi normalt forestiller os som den ideologiske højre-venstre akse i dansk politik. Enhedslisten ligger yderst til venstre, og Dansk Folkeparti ligger yderst til højre (i hvert fald i årene før folketingsvalget i efteråret 2018), og i midten øverst ligger Socialdemokratiet. Der er dog et par overraskelser. De Radikale venstre ligger klart til venstre for Socialdemokratiet, ja faktisk tættere på SF end på S. De mest 'højreorienterede' partier i dansk politik er hverken Dansk Folkeparti eller de Nye Borgerlige. Det er Venstre og Det Konservative Folkeparti, to partier der i øvrigt er stort set uskelnelige i deres stemmerafgivelser. De oversøiske mandater roder rundt i midten, hvilket primært er en konsekvens af at de ofte afholder sig fra at stemme eller stemmer 'hverken for eller imod'.

Pilene, som udspringer fra (0,0) er de fem største eigenvektorer for covariansmatricen for hver komponent, og repræsenterer kernen i en pca: de viser retningen på de afstemninger som er væsentligst for de to første komponenter. F.eks. peger en vektor i retning af kl. 7:30, og den fortæller os, at afstemning 7393 (Forslag til lov om dyrevelfærd) var een af de afstemninger, der adskildte de venstreorienterede og "bund"-partierne mest fra resten (forslaget blev forkastet. For stemte 41 - DF, RV, SF, EL og ALT, og imod stemte 62 - S, V, KF, NB og LA).

Lad os prøve at se lidt nærmere på denne første komponent i PCA-analysen, som altså projicerer ned på en akse de største forskelle mellem de danske politikere, og som står for circa 45 % af variansen i data. Vi starter med at plotte et heat map for at se, hvilke afstemninger der har haft størst og mindst betydning for denne spredning.

# da der er 323 afstemninger tilføjer vil et ekstra element så vi har 12x27 elementer, 
# som vi så kan plotte ved at bruge matshow. Jeg har brugt en farvekode som angiver de 
# afstemninger der er vigtige for højrefløjen med blå, og dem der er vigtige for 
# venstrefløjen med rød.

b = np.append(bills, [0])
b = b.reshape(12,27)
w = np.append(xvector, [0])
w = w.reshape(12,27)

fig, ax = plt.subplots(figsize=(12,8))
mesh = ax.matshow(w, cmap='seismic')
for (i, j), z in np.ndenumerate(b):
    ax.text(j, i, '{}'.format(z), ha='center', va='center', fontsize=4, bbox=dict(boxstyle='round', facecolor='white', edgecolor='0.3'))
plt.colorbar(mesh, ax=ax, fraction=0.02, pad=0.04) # arguments shrink the colorbar 

# Remember: save as pdf and transparent=True for Adobe Illustrator
if not os.path.exists(PLOTS_DIR):
    os.makedirs(PLOTS_DIR)

plt.savefig(os.path.join(PLOTS_DIR, 'ft'+str(periodid)+'_heatmap_1.png'), transparent=True, dpi=300)
# plt.savefig(os.path.join(PLOTS_DIR, 'ft'+str(periodid)+'_heatmap_1.pdf'), transparent=True, dpi=800)
plt.close()

heat map for første komponent

Plottet viser i rødt de afstemninger som (hvis vedtaget) har rykket Danmark mod venstre, og i blåt de afstemninger som (hvis vedtaget) har rykket Danmark mod højre. Det er tydeligt at der er stor forskel på, hvor meget en bestemt afstemning betyder for denne første komponent. Vi kan prøve at printe resumeet for de fem vigtigste afstemninger i hver retning (højre og venstre), og se om de også repræsenterer politiske temaer, som vi typisk forbinder med en højre- og venstrefløj:

print('Her de mest polariserende afstemninger som de røde (dvs. venstrefløjen + S) stemte for og vandt:\n')
for lov in bills[ix_high]:
    print('afstemnings-id', lov)
    pprint.pprint(df[df.afstemning == lov].titel.unique()[0])
    pprint.pprint(df[df.afstemning == lov].konklusion.unique()[0])
    pprint.pprint(df[df.afstemning == lov].resume.unique()[0])
    print('\n\n')
Her de mest polariserende afstemninger som de røde (dvs. venstrefløjen + S) stemte for og vandt:

afstemnings-id 7399
('Forslag til lov om ændring af færdselsloven. (Forhøjelse af bødeniveauet for '
 'ulovlig afholdelse af hvil i køretøjet m.v.).')
('Forslaget blev vedtaget. For stemte 60 (S, RV, SF, EL og ALT), imod stemte '
 '49 (V, DF, KF, NB og LA), hverken for eller imod stemte 0.')
('Formålet med forslaget er at skærpe bødestraffen for ulovlig afholdelse af '
 'det regulære hvil i køretøjet. Forslaget indeholder derfor retningslinjer, '
 'hvorefter bødeniveauet forhøjes til en bøde på 10.000 kr. til chaufføren og '
 'en bøde på 20.000 kr. til vognmanden. \n'
 '\n'
 'Forslaget indeholder desuden en præcisering af, at retten til adgang uden '
 'retskendelse til brug for den administrative kontrol med virksomheders '
 'overholdelse af køre-hvile-tids-reglerne og kontrol med installatører og '
 'værksteder, som kan udføre installering, eftersyn, kontrol og reparation af '
 'takografer, er overgået til Transport- og Boligministeriet.\n'
 '\n'
 'Loven træder i kraft den 1. marts 2020.')



afstemnings-id 7386
('Forslag til lov om ændring af aktieavancebeskatningsloven og '
 'dødsboskatteloven. (Ophævelse af hovedaktionærnedslaget).')
('Forslaget blev vedtaget. For stemte 56 (S, RV, SF, EL og ALT), imod stemte '
 '47 (V, DF, KF, NB og LA), hverken for eller imod stemte 0.')
('Med lovforslaget foreslås det, at ophæve det særlige nedslag i den '
 'skattepligtige fortjeneste, som kan opnås ved afståelse af '
 'hovedaktionæraktier, der er erhvervet før den 19. maj 1993.')



afstemnings-id 7351
'Forslag til lov om et midlertidigt børnetilskud til visse forsørgere.'
('Forslaget blev vedtaget. For stemte 56 (S, RV, SF, EL og ALT), imod stemte '
 '47 (V, DF, KF, NB og LA), hverken for eller imod stemte 0.')
('Lovforslaget udmønter aftalen om et nyt midlertidigt børnetilskud, som '
 'udspringer af det såkaldte forståelsespapir "Retfærdig retning for Danmark" '
 'mellem regeringen (Socialdemokratiet), Radikale Venstre, Socialistisk '
 'Folkeparti og Enhedslisten.\n'
 '\n'
 'Med lovforslaget foreslås det at indføre et nyt midlertidigt skattefrit '
 'børnetilskud til børnefamilier med børn under 15 år, hvor en eller begge '
 'forældre er berørt af kontanthjælpsloftet og/eller integrationsydelsen.\n'
 '\n'
 'Med lovforslaget foreslås det, at det nye midlertidige børnetilskud udgør '
 '700 kr. pr. barn pr. måned for familier på integrationsydelse og '
 'uddannelsesparate uddannelseshjælpsmodtagere. Tilskuddet udgør 600 kr. pr. '
 'barn pr. måned til enlige forældre og 550 kr. pr. barn pr. måned til øvrige '
 'familier. Enlige forsørgere får derudover et tilskud på 650 kr. pr. måned '
 'uafhængigt af antallet af børn. Børnefamilierne kan maksimalt få det i '
 'børnetilskud, som de har mistet som følge af indførelsen af '
 'kontanthjælpsloftet og integrationsydelsen.\n'
 '\n'
 'Det foreslås, at lovforslaget træder i kraft 1. januar 2020 med virkning fra '
 '1. august 2019.')



afstemnings-id 7376
('Forslag til lov om ændring af lov om arbejdsløshedsforsikring m.v. '
 '(Afskaffelse af opholdskrav for ret til dagpenge m.v.).')
('Forslaget blev vedtaget. For stemte 59 (S, RV, SF, EL og ALT), imod stemte '
 '50 (V, DF, KF, NB og LA), hverken for eller imod stemte 0.')
('Med lovforslaget foreslås det at afskaffe opholdskravet for ret til '
 'dagpenge, der kræver ophold i riget, et EU/EØS-land eller Schweiz i 5 ud af '
 '12 år i 2019, 6 ud af 12 år i 2020 og 7 ud af 12 år fra 2021 og fremefter.\n'
 '\n'
 'Med lovforslaget foreslås det desuden, at der etableres en overgangsordning, '
 'der giver mulighed for at genoprette medlemsskabet af en a-kasse fra '
 'udmeldelsestidspunktet for personer, der har meldt sig ud, fordi de har '
 'været ramt af opholdskravet. Ordningen målrettes personer, som ikke opfyldte '
 'betingelserne i opholdskravet i 2019 og frem til afskaffelsen af '
 'opholdskravet den 1. februar 2020, og som meldte sig ud af en a-kasse i '
 'samme periode. \n'
 '\n'
 'Det foreslås, at lovforslaget træder i kraft den 1. februar 2020.')



afstemnings-id 7456
('Forslag til lov om ændring af revisorloven og lov om formidling af fast '
 'ejendom m.v. (Gebyrfinansiering af hvidvasktilsyn).')
('Forslaget blev vedtaget. For stemte 54 (S, RV, SF, EL og UFG), imod stemte '
 '41 (V, DF, KF, NB og LA), hverken for eller imod stemte 0.')
('Lovforslaget skaber hjemmel til at gebyrfinansiere de udgifter, som er '
 'forbundet med hvidvasktilsynet med godkendte revisorer og ejendomsmæglere.\n'
 '\n'
 'Loven skal træde i kraft den 1. januar 2021.\n')



print('Her de mest polariserende afstemninger som de blå (dvs. højrefløjen) stemte for og tabte:\n')
for lov in bills[ix_low]:
    print('afstemnings-id', lov)
    pprint.pprint(df[df.afstemning == lov].titel.unique()[0])
    pprint.pprint(df[df.afstemning == lov].konklusion.unique()[0])
    pprint.pprint(df[df.afstemning == lov].resume.unique()[0])
    print('\n\n')
Her de mest polariserende afstemninger som de blå (dvs. højrefløjen) stemte for og tabte:

afstemnings-id 7414
('Forslag til lov om ændring af lov om afgift af tinglysning af ejer- og '
 'panterettigheder m.v. (tinglysningsafgiftsloven), emballageafgiftsloven, lov '
 'om afgift af bekæmpelsesmidler og forskellige andre love. (Indeksering af de '
 'faste tinglysningsafgifter og en række miljøafgifter og genindførelse af '
 'registreringsafgiften på luftfartøjer m.v.).')
('Forslaget blev forkastet. For stemte 50 (V, DF, KF, NB og LA), imod stemte '
 '59 (S, RV, SF, EL og ALT), hverken for eller imod stemte 0.')
('Med lovforslaget foreslås det at indeksere afgifterne på tinglysning, '
 'råstoffer, emballager, bekæmpelsesmidler og spildevand frem til 2025.\n'
 '\n'
 'Indekseringen foreslås indført ved to satsforhøjelser i perioden 2020-2025. '
 'Afgifterne på råstoffer og tinglysning forhøjes i 2020 og 2023, og '
 'afgifterne på emballager, bekæmpelsesmidler og spildevand forhøjes i 2021 og '
 '2024. Afgifterne foreslås forhøjet med 5,5 pct. pr. gang.\n'
 '\n'
 'Desuden foreslås en registreringsafgift på luftfartøjer fra og med den 1. '
 'januar 2021, således at der indføres en afgift for registrering af '
 'ejerrettigheder over fly på 0,1 pct. af flyets værdi og en afgift for '
 'registrering af pantrettigheder i fly på 0,1 pct. af pantets værdi. For '
 'registrering af pant i fly, der vejer under 5.700 kg, eller som er '
 'registreret godkendt til højst 10 passagerer, er afgiften på 1,5 pct. af '
 'pantets værdi.\n'
 '\n'
 'Lovforslaget udmønter dele af aftale om finansloven for 2020 indgået den 2. '
 'december 2019 mellem regeringen (Socialdemokratiet), Radikal Venstre, '
 'Socialistisk Folkeparti, Enhedslisten og Alternativet.\n')



afstemnings-id 7248
('Forslag til lov om ændring af lov om Arbejdsgivernes Uddannelsesbidrag. '
 '(Modelparametre for erhvervsuddannelser til brug for beregning af '
 'praktikpladsafhængigt arbejdsgiverbidrag for 2020 og justering af det '
 'aktivitetsafhængige VEU-bidrag for 2020 m.v.).')
('Forslaget blev forkastet. For stemte 51 (V, DF, KF, NB og LA), imod stemte '
 '61 (S, RV, SF, EL og ALT), hverken for eller imod stemte 0.')
('Forslaget vedrører det praktikpladsafhængige AUB-bidrag, der blev aftalt ved '
 '”Trepartsaftale om tilstrækkelig og kvalificeret arbejdskraft i hele Danmark '
 'og praktikpladser” fra 2016, og det aktivitetsafhængige VEU-bidrag, der blev '
 'aftalt ved ”Trepartsaftalen om styrket og mere fleksibel voksen-, efter- og '
 'videreuddannelse (2018-2021)” fra 2017. \n'
 '\n'
 'I lov om Arbejdsgivernes Uddannelsesbidrag er det forudsat, at der årligt '
 'ved lov skal ske en fastsættelse af modelparametrene i det '
 'praktikpladsafhængige AUB-bidrag og af bidragssatsen i det '
 'aktivitetsafhængige VEU-bidrag. \n'
 '\n'
 'Forslaget har derfor til formål at fastsætte de årlige modelparametre i det '
 'praktikpladsafhængige AUB-bidrag for de enkelte erhvervsuddannelser for 2020 '
 'i bilag 1 til lov om Arbejdsgivernes Uddannelsesbidrag og at indføre den '
 'årlige tilpasning af det aktivitetsafhængige VEU-bidrag for 2020.')



afstemnings-id 7385
('Forslag til lov om ændring af aktieavancebeskatningsloven og '
 'dødsboskatteloven. (Ophævelse af hovedaktionærnedslaget).')
('Forslaget blev forkastet. For stemte 47 (V, DF, KF, NB og LA), imod stemte '
 '57 (S, RV, SF, EL og ALT), hverken for eller imod stemte 0.')
('Med lovforslaget foreslås det, at ophæve det særlige nedslag i den '
 'skattepligtige fortjeneste, som kan opnås ved afståelse af '
 'hovedaktionæraktier, der er erhvervet før den 19. maj 1993.')



afstemnings-id 7237
('Forslag til lov om ændring af pensionsbeskatningsloven, '
 'pensionsafkastbeskatningsloven, selskabsskatteloven og forskellige andre '
 'love. (Videregivelse af oplysninger om diskvalificerende '
 'pensionsudbetalinger, smidiggørelse af regler for flytning af '
 'pensionsindbetalinger, justering af reglerne om omdannelse af pensionskasser '
 'til livsforsikringsselskaber og goodwillbeskatning m.v.).')
('Forslaget blev forkastet. For stemte 50 (V, DF, KF, NB og LA), imod stemte '
 '62 (S, RV, SF, EL og ALT), hverken for eller imod stemte 0.')
('Formålet med forslaget er at foretage en række tekniske justeringer på '
 'pensionsbeskatningsområdet. \n'
 '\n'
 'Forslaget indeholder bl.a. adgang til oplysninger om diskvalificerende '
 'udbetalinger for pensionsinstitutter, smidigere regler for flytning af '
 'pensionsindbetalinger mellem forskellige pensionsordninger og justeringer af '
 'reglerne for skattefri omstruktureringer af pensionsinstitutter. ')



afstemnings-id 7278
'Forslag til finanslov for finansåret 2020.'
('Forslaget blev forkastet. For stemte 50 (V, DF, KF, NB og LA), imod stemte '
 '61 (S, RV, SF, EL, ALT og SIU), hverken for eller imod stemte 0.')
('Finanslovforslaget fastlægger størrelsen og fordelingen af de samlede '
 'statslige udgifter og indtægter for finansåret 2020. Lovforslaget indeholder '
 'desuden overslag over statens udgifter for de efterfølgende 3 år.')



Så hvad viser dette? Højre-venstrefløjs-aksen handler om emner som ordentlige arbejdsforhold hos chauffører og vognmænd, om børnebidrag til enlige forsørgere, om afskaffelse af opholdskrav for ret til dagpenge, samt finanslovsting. Altså ting man kunne forvente at der var uenighed om mellem de to fløje, og som venstrefløjen + S fik igennem fordi de har flertal.

Hvad så med y-aksen, altså bunden vs. toppen i figuren? Her skal vi kigge på den anden komponent, og vi starter igen med at tegne et heatmap:

# nu det samme for anden component:
w = np.append(yvector, [0])
w = w.reshape(12,27)

fig, ax = plt.subplots(figsize=(12,8))
mesh = ax.matshow(w, cmap='PiYG')
for (i, j), z in np.ndenumerate(b):
    ax.text(j, i, '{}'.format(z), ha='center', va='center', fontsize=4, bbox=dict(boxstyle='round', facecolor='white', edgecolor='0.3'))
plt.colorbar(mesh, ax=ax, fraction=0.02, pad=0.04)

# Remember: save as pdf and transparent=True for Adobe Illustrator
if not os.path.exists(PLOTS_DIR):
    os.makedirs(PLOTS_DIR)

plt.savefig(os.path.join(PLOTS_DIR, 'ft'+str(periodid)+'_heatmap_2.png'), transparent=True, dpi=300)
# plt.savefig(os.path.join(PLOTS_DIR, 'ft'+str(periodid)+'_heatmap_2.pdf'), transparent=True, dpi=800)
plt.close()

heat map for første komponent

Vi printer igen de fem vigtigste afsteninger i hver sin retning (altså op og ned):

iyhigh = np.argsort(yvector)[-5:] # returns an array of sorted indexes of the components

print('Her de mest polariserende afstemninger som de grønne (dvs. bunden) stemte for og tabte:\n')
for lov in bills[iyhigh]:
    print('afstemnings-id', lov)
    pprint.pprint(df[df.afstemning == lov].titel.unique()[0])
    pprint.pprint(df[df.afstemning == lov].konklusion.unique()[0])
    pprint.pprint(df[df.afstemning == lov].resume.unique()[0])
    print('\n\n')
Her de mest polariserende afstemninger som de grønne (dvs. bunden) stemte for og tabte:

afstemnings-id 7393
'Forslag til lov om dyrevelfærd (dyrevelfærdsloven).'
('Forslaget blev forkastet. For stemte 41 (DF, RV, SF, EL og ALT), imod stemte '
 '62 (S, V, KF, NB og LA), hverken for eller imod stemte 0.')
('Forslaget gennemfører en forenkling af lovgivningsstrukturen på '
 'dyrevelfærdsområdet og gør lovgivningen mere overskuelig og brugervenlig i '
 'praksis ved at ophæve en række dyreartsspecifikke love. Forslaget er en ny '
 'hovedlov og er rammelov for størstedelen af dyrevelfærdslovgivningen med det '
 'formål at fremme god dyrevelfærd og respekt for dyr som levende væsener. '
 'Loven varetager dyrevelfærdsmæssige og dyreetiske hensyn. Forslaget '
 'viderefører som udgangspunkt gældende ret og indeholder hovedsagelig regler '
 'fra den gældende dyreværnslov, men det indeholder også enkelte nye regler '
 'som f.eks. forbud mod dyrekampe.')



afstemnings-id 7422
('Forslag til folketingsbeslutning om ens krav til kvalifikationer hos læger i '
 'Danmark, der er uddannet i henholdsvis EU-/EØS-lande og tredjelande.')
('Forslaget blev forkastet. For stemte 21 (DF, EL og NB), imod stemte 88 (S, '
 'V, RV, SF, Christian Juhl (EL), KF, LA og UFG), hverken for eller imod '
 'stemte 0.')
('For at sikre patientsikkerheden pålægger beslutningsforslaget regeringen at '
 'sørge for, at personer, der er uddannet i et EU-/EØS-land - bortset fra '
 'Danmark og de øvrige nordiske lande - og som søger om dansk lægeautorisation '
 'eller anerkendelse som speciallæge i Danmark, bliver mødt med de samme krav '
 'til og test af kvalifikationer som ansøgere, der er statsborgere og/eller '
 'uddannet i lande uden for EU/EØS. ')



afstemnings-id 7361
('Forslag til folketingsbeslutning om folketingsmedlemmers omkostningstillæg, '
 'eftervederlag, sygeorlov og afholdelse af barselsorlov.')
('Forslaget blev forkastet. For stemte 22 (DF, EL og NB), imod stemte 80 (S, '
 'V, RV, SF, KF, ALT og LA), hverken for eller imod stemte 0.')
('Beslutningsforslaget går ud på, at der skal gennemføres visse ændringer af '
 'folketingsmedlemmernes vilkår med hensyn til vederlag og orlov. Konkret '
 'indebærer forslaget, at medlemmer af Folketinget ikke skal kunne modtage '
 'omkostningstillæg, mens de er på barselsorlov, optjene eftervederlag, mens '
 'de bestrider et andet job, højst skal kunne modtage eftervederlag i 6 '
 'måneder, skal fremvise en lægeerklæring i forbindelse med sygeorlov, og at '
 'de skal omfattes af reglerne i barselsloven for afholdelse af '
 'barselsorlov. \n'
 '\n'
 'Beslutningsforslaget er en delvis genfremsættelse af beslutningsforslag nr. '
 'B 152, folketingsåret 2018-19, 1. samling.')



afstemnings-id 7362
('Forslag til folketingsbeslutning om indførelse af årlige '
 '2-procentsbesparelser på gruppestøttemidlerne til Folketingets partier.')
('Forslaget blev forkastet. For stemte 25 (Jens Joel (S), DF, EL, NB og LA), '
 'imod stemte 77 (S, V, RV, SF, KF og ALT), hverken for eller imod stemte 0.')
('Beslutningsforslaget indebærer, at der skal indføres årlige besparelser på 2 '
 'pct. på gruppestøttemidlerne til Folketingets partier svarende til det '
 'såkaldte omprioriteringsbidrag, som en række offentlige områder og '
 'institutioner er pålagt.')



afstemnings-id 7477
'Forslag til folketingsbeslutning om ændring af ferieloven (borgerforslag).'
('Forslaget blev forkastet. For stemte 20 (DF, EL, NB, LA og UFG), imod stemte '
 '65 (S, V, RV, KF og ALT), hverken for eller imod stemte 9 (SF).')
('Ifølge forslaget skal de såkaldte indefrosne feriepenge udbetales senest den '
 '1. september 2020.\n'
 '\n'
 'Der er tale om et borgerforslag, som inden for den fastsatte frist har '
 'opnået det antal støttetilkendegivelser fra borgere, som kræves for at få '
 'forslaget fremsat og behandlet som beslutningsforslag i Folketinget. \n'
 '\n'
 'Fremsættelsen er alene udtryk for, at forslagsstillerne på vegne af de '
 'partier, som støtter borgerforslagsordningen, påtager sig at opfylde en '
 'nødvendig betingelse for, at borgerforslaget kan blive behandlet i '
 'Folketinget i overensstemmelse med intentionerne bag ordningen. ')



iylow = np.argsort(yvector)[:5] 
print('Her de mest polariserende afstemninger som de lilla (dvs. toppen) stemte for og vandt:\n')
for lov in bills[iylow]:
    print('afstemnings-id', lov)
    pprint.pprint(df[df.afstemning == lov].titel.unique()[0])
    pprint.pprint(df[df.afstemning == lov].konklusion.unique()[0])
    pprint.pprint(df[df.afstemning == lov].resume.unique()[0])
    print('\n\n')
Her de mest polariserende afstemninger som de lilla (dvs. toppen) stemte for og vandt:

afstemnings-id 7407
('Forslag til lov om ændring af lov om social service. (Ro og stabilitet for '
 'udsatte børn og unge og fuldbyrdelse af tvangsmæssige afgørelser om ændret '
 'anbringelsessted uden samtykke).')
('Forslaget blev vedtaget. For stemte 73 (S, V, RV og KF), imod stemte 35 (DF, '
 'SF, EL, ALT, NB og LA), hverken for eller imod stemte 0.')
('Med lovforslaget ændres lov om social service. Det foreslås blandt andet, at '
 'kommunerne får adgang til at fuldbyrde afgørelser om ændret anbringelsessted '
 'uden samtykke. Hvis denne fuldbyrdelse sker med bistand fra politiet, skal '
 'den registreres og indberettes af kommunen til Ankestyrelsen. Det forslås '
 'videre, at fuldbyrdelse af afgørelser skal ske med henblik på at sikre '
 'barnets eller den unges bedste.')



afstemnings-id 7363
('Folketinget noterer sig, at der har været politiske drøftelser i '
 'forligskredsen med henblik på at ændre offentlighedsloven. Folketinget '
 'noterer sig endvidere, at det i forligskredsen ikke har været muligt at nå '
 'til enighed om en ændring af offentlighedsloven. Endelig noterer Folketinget '
 'sig, at justitsministeren ikke finder behov for at genåbne drøftelserne om '
 'at ændre offentlighedsloven.')
('Forslaget blev vedtaget. For stemte 66 (S, V, Liselott Blixt (DF) og KF), '
 'imod stemte 44 (DF, RV, SF, EL, ALT, NB og LA), hverken for eller imod '
 'stemte 0.')
''



afstemnings-id 7326
('Forslag til lov om ændring af lov om offentlighed i forvaltningen. '
 '(Ophævelse af revisionsbestemmelse).')
('Forslaget blev vedtaget. For stemte 68 (S, V, KF og LA), imod stemte 46 (DF, '
 'RV, SF, EL, ALT, NB og UFG), hverken for eller imod stemte 0.')
('Det følger af offentlighedslovens § 44, at justitsministeren i '
 'folketingsåret 2018-19 skulle fremsætte lovforslag om revision af '
 'offentlighedslovens § 16 om postlister. Ved en postliste forstås en '
 'fortegnelse over dokumenter, der den pågældende dag er modtaget i eller '
 'afsendt af myndigheden. \n'
 '\n'
 'Som opfølgning på den generelle evaluering af offentlighedsloven har der '
 'været forligskredsdrøftelser om en ny aftale om offentlighedsloven, og bl.a. '
 'af den grund er revisionen af postlistebestemmelsen blevet udskudt i 2017 og '
 'i 2018. \n'
 '\n'
 'Da det ikke har været muligt at opnå enighed i forligskredsen om at ændre '
 'offentlighedsloven, herunder om at indføre en postlisteordning, foreslås '
 'det, at revisionsbestemmelsen i offentlighedslovens § 44 ophæves. \n'
 '\n'
 'Lovforslaget er en genfremsættelse af L 176 (2018-19, 1. samling).\n'
 '\n'
 'Det foreslås, at loven træder i kraft den 1. januar 2020.\n')



afstemnings-id 7341
('Forslag til lov om ændring af skatteforvaltningsloven. (Afledt skattemæssig '
 'virkning ved ekstraordinær genoptagelse af ejendomsvurderinger).')
('Forslaget blev vedtaget. For stemte 78 (S, V, RV, SF, KF, ALT og LA), imod '
 'stemte 22 (DF, EL og NB), hverken for eller imod stemte 0.')
('Lovforslaget handler om de dele af lovforslag nr. L 71, der omhandler afledt '
 'skattemæssig virkning ved ekstraordinær genoptagelse af ejendomsvurderinger.')



afstemnings-id 7485
('Forslag til lov om ændring af aktiesparekontoloven. (Forhøjelse af loftet '
 'for indskud på aktiesparekontoen).')
('Forslaget blev vedtaget. For stemte 71 (S, V, RV, KF, NB og LA), imod stemte '
 '25 (DF, SF, EL, ALT og UFG), hverken for eller imod stemte 0.')
('Det foreslås med lovforslaget at forhøje loftet for indskud på '
 'aktiesparekontoen fra 50.000 kr. i 2019 til 100.000 kr. i 2020.')



Hvad viser dette? De mest polariserende afstemninger mellem top og bund handler om ting som offentlighedloven (bunden vil have den ændret, toppen ikke vil), mere dyrevelfærd (toppen vil ikke), afskaffelse af EU-privilegier (toppen vil ikke), ligeberettigelse til social pension (toppen vil ikke), fjernelse af politikeres frynsegoder (toppen vil ikke), og om imødekommelse af borgerforslag (toppen vil ikke). Blandt de emner som blev gennemført, men bunden stemte imod er nye regler for tvangsanbringelser, indvandrere, og diverse økonomiske regler (DF hhv EL er dog typisk langt fra hinanden i disse sager), og igen en modstand mod at udskyde ændring af offentlighedloven.

Alt i alt kan man måske sige at bund versus top i Dansk politik handler om dem der vil fjerne privilegier fra et bestemt 'establishment', og dem der vil beholde privilegierne. Der er dog tale om forskellige typer af 'establishment'. Nogle gange er det den politiske elite vs. resten, nogle gange er det EU vs. resten af verden, nogle gange er det mennesker vs. dyr, og nogle gange er det de rige vs. de ikke-så-rige. Den analyse passer også godt overens med Aarhus Universitets magtudredning "De Folkevalgte" fra 2004, hvori der står at "i modsætning til alle de andre partier ser 'midterpartierne' (sic - her menes S + V + KF) ikke et behov for at ændre magtforholdene i samfundet." (s. 246).

Måske kan man også med Larry Summers ord sige, at den næst-vigtigste skillelinje i dansk politik, efter højre-venstre opdelingen, er den der adskiller 'insiders' fra 'outsiders'. Outsiders er de frie mennesker, der råber op og siger hvad de har lyst til, men ikke bestemmer noget som helst. Insiders er dem der kun siger det, der er accepteret at sige som insider, og de lytter ikke til outsiderne (som de kalder ekstremister). Til gengæld får insiderne lov til at tage alle de vigtige beslutninger.