Politics

We show how to use scikit-network to analyse the way deputies vote and their proximity to the majority. We here consider the French National Assembly (XVth legislature, from 2017 to 2020). The considered graph is the bipartite graph between deputies and bills.

[1]:
from IPython.display import SVG
[2]:
import numpy as np
[3]:
from sknetwork.data import load_netset
from sknetwork.clustering import Louvain
from sknetwork.regression import Diffusion
from sknetwork.ranking import top_k
from sknetwork.embedding import Spectral
from sknetwork.visualization import svg_graph, svg_dendrogram

Data

The dataset is part of the NetSet collection.

[4]:
graph = load_netset('fr-assembly')
Downloading fr-assembly from NetSet...
Unpacking archive...
Parsing files...
Done.
[5]:
biadjacency = graph.biadjacency
position = graph.position
names = graph.names_row
bills = graph.names_col
labels = graph.labels
label_colors = graph.label_colors
names_labels = graph.names_labels
[6]:
n_deputy, n_bill = biadjacency.shape
[7]:
print(names_labels)
['Non inscrit' 'Les Républicains'
 'Les Constructifs : républicains, UDI, indépendants'
 'Libertés et Territoires' 'UDI et Indépendants'
 'UDI, Agir et Indépendants' 'Mouvement Démocrate et apparentés'
 'La République en Marche' 'Socialistes et apparentés' 'Nouvelle Gauche'
 'Gauche démocrate et républicaine' 'La France insoumise']
[8]:
# parameters for visualization
node_size = 4
width = 480
height = 300
[9]:
image = svg_graph(position=position, labels=labels, node_size=node_size, width=width, height=height,
                  label_colors=label_colors)
SVG(image)
[9]:
../_images/use_cases_votes_11_0.svg
[10]:
labels_majority = [6,7]
[11]:
print(names_labels[labels_majority])
['Mouvement Démocrate et apparentés' 'La République en Marche']
[12]:
# majority
majority = np.isin(labels, labels_majority)
np.sum(majority)
[12]:
356
[13]:
# opposition
opposition = ~np.isin(labels, labels_majority)
np.sum(opposition)
[13]:
221

Votes

[14]:
biadjacency_for = biadjacency > 0
biadjacency_against = (-biadjacency > 0)
[15]:
biadjacency_for
[15]:
<577x2807 sparse matrix of type '<class 'numpy.bool_'>'
        with 119901 stored elements in Compressed Sparse Row format>
[16]:
bills_for = biadjacency_for.T.dot(np.ones(n_deputy))
bills_against = biadjacency_against.T.dot(np.ones(n_deputy))
bills_total = bills_for + bills_against
[17]:
print('Average participation = ', np.round(np.sum(bills_total) / n_bill / n_deputy, 2))
Average participation =  0.17

Clustering

[18]:
louvain = Louvain()
[19]:
labels_pred = louvain.fit_transform(biadjacency_for)
[20]:
np.unique(labels_pred, return_counts=True)
[20]:
(array([0, 1, 2]), array([213, 363,   1]))
[21]:
labels_pred_majority, counts_majority = np.unique(labels_pred[majority], return_counts=True)
[22]:
label_pred_majority = labels_pred_majority[np.argmax(counts_majority)]
[23]:
image = svg_graph(position=position, labels=labels_pred, node_size=node_size, width=width, height=height, label_colors=['red','blue', 'lightgrey'])
SVG(image)
[23]:
../_images/use_cases_votes_27_0.svg
[24]:
neutral = np.argwhere(labels_pred==2).ravel()
[25]:
print(names[neutral])
['Laure de La Raudière']
[26]:
# dissident
print(names[majority * (labels_pred!=label_pred_majority)])
['Frédérique Dumas' 'Maud Petit' 'Sonia Krimi' 'Richard Ramos'
 'Sébastien Nadot']

Diffusion

[27]:
diffusion = Diffusion(n_iter=4)
[28]:
values = diffusion.fit_predict(biadjacency_for, seeds_row=majority)
[29]:
image = svg_graph(position=position, scores=values, node_size=node_size,
                  width=width, height=height)
SVG(image)
[29]:
../_images/use_cases_votes_34_0.svg
[30]:
# top-10 deputies for majority
index = np.argwhere(majority).ravel()
top = index[top_k(values[index], 10)]
print(names[top])
['Didier Paris' 'Yaël Braun-Pivet' 'Jean Terlier' 'Guillaume Vuilletet'
 'Marie Guévenoux' 'Richard Ferrand' 'Élise Fajgeles|||Benjamin Griveaux'
 'Émilie Chalas' 'Sacha Houlié' 'Thomas Rudigoz']
[31]:
# bottom-10 deputies for majority
bottom = index[top_k(-values[index], 10)]
print(names[bottom])
['Agnès Thill' 'Frédérique Dumas' 'Maud Petit' 'Sébastien Nadot'
 'Richard Ramos' 'Brahim Hammouche' 'Jimmy Pahun' 'Josy Poueyto'
 'Sonia Krimi' 'Max Mathiasin']
[32]:
# top-10 deputies for opposition
index = np.argwhere(opposition).ravel()
top = index[top_k(-values[index], 10)]
print(names[top])
['Clémentine Autain' 'Nicolas Dupont-Aignan' 'Adrien Quatennens'
 'Jean-Hugues Ratenon' 'Pierre Dharréville' 'Alexis Corbière'
 'Alain Bruneel' 'Jean-Luc Mélenchon' 'Sébastien Jumel' 'Ugo Bernalicis']
[33]:
# bottom-10 deputies for opposition
bottom = index[top_k(values[index], 10)]
print(names[bottom])
['Olivier Dassault' 'Jean-Luc Warsmann' 'Michèle Tabarot'
 'Napole Polutele|||Sylvain Brial' 'Bernard Deflesselles'
 'Thierry Robert|||Jean-Luc Poudroux' 'Franck Riester|||Patricia Lemoine'
 'Stéphane Demilly' 'Laure de La Raudière' 'Philippe Gomès']

Bills

[34]:
# labels are on deputies so you need an odd number of iterations
diffusion = Diffusion(n_iter=5)
[35]:
diffusion.fit(biadjacency_for, seeds_row=majority)
[35]:
Diffusion(n_iter=5)
[36]:
values_bill = diffusion.values_col_
[37]:
# top-5 bills for majority
for i in top_k(values_bill, 5):
    print(bills[i] + '\n')
l'article 27 du projet de loi de programmation 2018-2022 et de réforme pour la justice (première lecture).

l'article 13 du projet de loi de programmation 2018-2022 et de réforme pour la justice (première lecture).

l'amendement n° 2362  de Mme Marsaud après l'article 60 du projet de loi portant évolution du logement, de l'aménagement et du numérique (première lecture)

l'amendement de suppression n° 72 du Gouvernement à l'article 9 de la proposition de loi d'orientation et de programmation relative à la sécurité intérieure (première lecture).

l'amendement de suppression n° 71 du Gouvernement à l'article 3 de la proposition de loi d'orientation et de programmation relative à la sécurité intérieure (première lecture).

[38]:
# top-5 bills for opposition
for i in top_k(-values_bill, 5):
    print(bills[i] + '\n')
l'amendement n° 602 de M. Bernalicis à l'article 32 du projet de loi de programmation 2018-2022 et de réforme pour la justice (première lecture).

l'amendement n° 153 de Mme Pires Beaune à l'article 13 du projet de loi relatif à la lutte contre la fraude (première lecture).

l'amendement n° 205 de Mme Obono après l'article 30 du projet de loi de programmation 2018-2022 et de réforme pour la justice (première lecture).

l'amendement n° 208 de Mme Obono après l'article 31 bis du projet de loi de programmation 2018-2022 et de réforme pour la justice (première lecture).

l'amendement n° 11 de M. Bernalicis à l'article unique de la proposition de loi renforçant la lutte contre les rodéos motorisés (première lecture).

[39]:
# top-5 controversial
for i in top_k(-np.abs(values_bill-0.5), 5):
    print(bills[i] + '\n')
l'amendement n° 66 de Mme Auconie et les amendements identiques suivants à l'article premier du projet de loi renforçant la lutte contre les violences sexuelles et sexistes (première lecture).

l'amendement de suppression n° 168 de Mme Obono et les amendements identiques suivants à l'article 9 ter du projet de loi pour une immigration maîtrisée, un droit d'asile effectif et une intégration réussie (nouvelle lecture).

l'article unique de la proposition de loi organique visant à permettre l'inscription d'office sur la liste électorale spéciale à la consultation sur l'accession à la pleine souveraineté de la Nouvelle-Calédonie (première lecture).

l'amendement n° 962 de la commission du développement durable et l'amendement identique suivant après l'article 14 septies du projet de loi pour l'équilibre des relations commerciales dans le secteur agricole et alimentaire et une alimentation saine et durable (première lecture).

l'amendement n° 1118 de M. Clément à l'article premier du projet de loi pour une immigration maîtrisée, un droit d'asile effectif et une intégration réussie (première lecture).

Embedding

[40]:
spectral = Spectral(2, normalized=False)
[41]:
embedding = spectral.fit_transform(biadjacency_for)
[42]:
image = svg_graph(position=embedding, names=names, labels=labels, node_size=5, width=400, height=1000,
                  label_colors=label_colors)
SVG(image)

[42]:
../_images/use_cases_votes_49_0.svg