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, PageRank
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('national_assembly')
Downloading national_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)
len(opposition)
[13]:
577

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_transform(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])
['Émilie Chalas' 'Didier Paris' 'Richard Ferrand'
 'Élise Fajgeles|||Benjamin Griveaux' 'Guillaume Vuilletet'
 'Yaël Braun-Pivet' 'Marie Guévenoux' 'Jean Terlier' 'Sacha Houlié'
 'Thomas Rudigoz']
[31]:
# bottom-10 deputies for majority
bottom = index[top_k(-values[index], 10)]
print(names[bottom])
['Frédérique Dumas' 'Maud Petit' 'Richard Ramos' 'Agnès Thill'
 'Brahim Hammouche' 'Sébastien Nadot' '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])
['Ugo Bernalicis' 'Adrien Quatennens' 'Alain Bruneel' 'Clémentine Autain'
 'Nicolas Dupont-Aignan' 'Alexis Corbière' 'Jean-Luc Mélenchon'
 'Sébastien Jumel' 'Jean-Hugues Ratenon' 'Pierre Dharréville']
[33]:
# bottom-10 deputies for opposition
bottom = index[top_k(values[index], 10)]
print(names[bottom])
['Jean-Luc Warsmann' 'Olivier Dassault' 'Napole Polutele|||Sylvain Brial'
 'Stéphane Demilly' 'Michèle Tabarot' 'Bernard Deflesselles'
 'Franck Riester|||Patricia Lemoine' 'Thierry Robert|||Jean-Luc Poudroux'
 '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, damping_factor=1.0)
[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'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).

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'article 13 du projet de loi de programmation 2018-2022 et de réforme pour la justice (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° 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° 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° 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).

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

[39]:
# top-5 controversial
for i in top_k(-np.abs(values_bill-0.5), 5):
    print(bills[i] + '\n')
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° 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'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