Skip to content

Instantly share code, notes, and snippets.

@dhernandez
Last active June 1, 2023 11:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhernandez/10ac224ed51d449bcc8c2884d2dd577a to your computer and use it in GitHub Desktop.
Save dhernandez/10ac224ed51d449bcc8c2884d2dd577a to your computer and use it in GitHub Desktop.
Calculo del reparto de diputados en la Diputación Provincial de Soria para el año 2023
#!/bin/bash
import math
import numpy as np
import pandas as pd
import requests
from fake_useragent import UserAgent
from requests.adapters import HTTPAdapter, Retry
from municipalities import municipalities
URL_FORMAT = "https://resultados.locales2023.es/_next/data/bKpDDhunNBOUCBsOTpr5x/es/resultados/0/{id}/90.json?elec=0" \
"&id={id}&mapLevel=90"
ROUND_METHOD = np.trunc # or np.round
TYPE_OF_DATA = 'def' # or pro
def dhondt(parties, seats):
results = {}
parties = [party | {'updated_votes': party['votes']} for party in parties]
while seats > 0:
parties = sorted(parties, key=lambda x: x['updated_votes'], reverse=True)
results[parties[0]['name']] = results.get(parties[0]['name'], 0) + 1
next = '{} votes needed -> {} would gain a seat from {}'.format(
results[parties[0]['name']] * math.ceil(parties[0]['updated_votes'] - parties[1]['updated_votes']), parties[1]['name'],
parties[0]['name'])
seats -= 1
parties[0]['updated_votes'] = parties[0]['votes'] / (results[parties[0]['name']] + 1)
return {'SEATS': results, 'next': next}
def get_votes(information, siglas):
try:
return next(
x['votos'][TYPE_OF_DATA] for x in
information['pageProps']['ScopeResponse']['scope']['escrutinio']['partidos'] if
x['siglas'] == siglas)
except:
return 0
def get_real_votes(information, siglas):
votes = 0
try:
party = get_party(information, siglas)
if len(party['candidatos']) > 0:
for candidate in party['candidatos']:
votes += candidate['votos'][TYPE_OF_DATA]
finally:
return votes
def get_candidates(information, siglas):
try:
party = get_party(information, siglas)
return len(party['candidatos'])
except:
return 0
def get_party(information, siglas):
return next(x for x in information['pageProps']['ScopeResponse']['scope']['escrutinio']['partidos'] if
x['siglas'] == siglas)
def get_data(read_local_file=False):
if read_local_file:
return pd.read_csv('data.csv')
data = []
s = requests.Session()
retries = Retry(total=5, backoff_factor=1, status_forcelist=[502, 503, 504, 403])
s.mount('http://', HTTPAdapter(max_retries=retries))
ua = UserAgent()
headers = {'User-Agent': ua.firefox}
for municipality in municipalities:
response = s.get(URL_FORMAT.format(id=municipality), headers=headers)
information = response.json()
data.append({
'municipality': information['pageProps']['ScopeResponse']['scope']['nombre'],
'partidoJudicial': information['pageProps']['ScopeResponse']['scope']['padres'][1]['nombre'],
'padron': information['pageProps']['ScopeResponse']['scope']['padron'],
'censoINE': information['pageProps']['ScopeResponse']['scope']['censoINE'],
'censoNoche': information['pageProps']['ScopeResponse']['scope']['censoNoche'],
'tipo': information['pageProps']['ScopeResponse']['scope']['padres'][5]['nombre'],
'tipo_id': information['pageProps']['ScopeResponse']['scope']['padres'][5]['codigo'],
'cargos': information['pageProps']['ScopeResponse']['scope']['escrutinio']['cargos'],
'votesPSOE': get_votes(information, 'PSOE'),
'votesPP': get_votes(information, 'PP'),
'votesVOX': get_votes(information, 'VOX'),
'realPSOE': get_real_votes(information, 'PSOE'),
'realPP': get_real_votes(information, 'PP'),
'realVOX': get_real_votes(information, 'VOX'),
'candidatesPSOE': get_candidates(information, 'PSOE'),
'candidatesPP': get_candidates(information, 'PP'),
'candidatesVOX': get_candidates(information, 'VOX'),
})
print('Fetching results for {}'.format(information['pageProps']['ScopeResponse']['scope']['nombre']))
df = pd.DataFrame.from_records(data)
df.to_csv('data.csv')
return df
def balance(df):
df = df.drop(columns=(
'balancedPSOE_1', 'balancedPP_1', 'balancedVOX_1', 'balancedPSOE_2', 'balancedPP_2', 'balancedVOX_2'), errors='ignore')
df['balancedPSOE_1'] = np.where(df['tipo_id'] != '01', df['votesPSOE'],
ROUND_METHOD(df['realPSOE'] / np.minimum(df['candidatesPSOE'], 4)))
df['balancedPP_1'] = np.where(df['tipo_id'] != '01', df['votesPP'],
ROUND_METHOD(df['realPP'] / np.minimum(df['candidatesPP'], 4)))
df['balancedVOX_1'] = np.where(df['tipo_id'] != '01', df['votesVOX'],
ROUND_METHOD(df['realVOX'] / np.minimum(df['candidatesVOX'], 4)))
df['balancedPSOE_2'] = np.where(df['tipo_id'] != '01', df['votesPSOE'], ROUND_METHOD(
df['realPSOE'] / np.minimum(df['candidatesPSOE'], np.where((df['cargos'] > 1) & (df['padron'] <= 101), 2, 4))))
df['balancedPP_2'] = np.where(df['tipo_id'] != '01', df['votesPP'], ROUND_METHOD(
df['realPP'] / np.minimum(df['candidatesPP'], np.where((df['cargos'] > 1) & (df['padron'] < 101), 2, 4))))
df['balancedVOX_2'] = np.where(df['tipo_id'] != '01', df['votesVOX'], ROUND_METHOD(
df['realVOX'] / np.minimum(df['candidatesVOX'], np.where((df['cargos'] > 1) & (df['padron'] < 101), 2, 4))))
df = df.fillna(0).astype(
{'balancedPSOE_1': 'int32', 'balancedPP_1': 'int32', 'balancedVOX_1': 'int32', 'balancedPSOE_2': 'int32',
'balancedPP_2': 'int32', 'balancedVOX_2': 'int32'})
print(df.head())
df.to_csv('data.csv')
return df
def apply_dhondt(df):
partiesBalancedSoria_1 = [
{'name': 'PSOE', 'votes': df.query("partidoJudicial == 'Soria'")['balancedPSOE_1'].sum()},
{'name': 'PP', 'votes': df.query("partidoJudicial == 'Soria'")['balancedPP_1'].sum()},
{'name': 'VOX', 'votes': df.query("partidoJudicial == 'Soria'")['balancedVOX_1'].sum()},
]
partiesBalancedSoria_2 = [
{'name': 'PSOE', 'votes': df.query("partidoJudicial == 'Soria'")['balancedPSOE_2'].sum()},
{'name': 'PP', 'votes': df.query("partidoJudicial == 'Soria'")['balancedPP_2'].sum()},
{'name': 'VOX', 'votes': df.query("partidoJudicial == 'Soria'")['balancedVOX_2'].sum()},
]
partiesBalancedAlmazan_1 = [
{'name': 'PSOE', 'votes': df.query("partidoJudicial == 'Almazán'")['balancedPSOE_1'].sum()},
{'name': 'PP', 'votes': df.query("partidoJudicial == 'Almazán'")['balancedPP_1'].sum()},
{'name': 'VOX', 'votes': df.query("partidoJudicial == 'Almazán'")['balancedVOX_1'].sum()},
]
partiesBalancedAlmazan_2 = [
{'name': 'PSOE', 'votes': df.query("partidoJudicial == 'Almazán'")['balancedPSOE_2'].sum()},
{'name': 'PP', 'votes': df.query("partidoJudicial == 'Almazán'")['balancedPP_2'].sum()},
{'name': 'VOX', 'votes': df.query("partidoJudicial == 'Almazán'")['balancedVOX_2'].sum()},
]
partiesBalancedOsma_1 = [
{'name': 'PSOE',
'votes': df.query("partidoJudicial == 'Burgo de Osma-Ciudad de Osma'")['balancedPSOE_1'].sum()},
{'name': 'PP', 'votes': df.query("partidoJudicial == 'Burgo de Osma-Ciudad de Osma'")['balancedPP_1'].sum()},
{'name': 'VOX', 'votes': df.query("partidoJudicial == 'Burgo de Osma-Ciudad de Osma'")['balancedVOX_1'].sum()},
]
partiesBalancedOsma_2 = [
{'name': 'PSOE',
'votes': df.query("partidoJudicial == 'Burgo de Osma-Ciudad de Osma'")['balancedPSOE_2'].sum()},
{'name': 'PP', 'votes': df.query("partidoJudicial == 'Burgo de Osma-Ciudad de Osma'")['balancedPP_2'].sum()},
{'name': 'VOX', 'votes': df.query("partidoJudicial == 'Burgo de Osma-Ciudad de Osma'")['balancedVOX_2'].sum()},
]
print('Ponderación con máximo 4 en todos los municipios < 251 habitantes')
print('-------------')
print('Soria')
print(partiesBalancedSoria_1)
print(dhondt(partiesBalancedSoria_1, 15))
print('Almazán')
print(partiesBalancedAlmazan_1)
print(dhondt(partiesBalancedAlmazan_1, 5))
print('Burgo de Osma-Ciudad de Osma')
print(partiesBalancedOsma_1)
print(dhondt(partiesBalancedOsma_1, 5))
print()
print()
print('Ponderación con máximo 2 en municipios con padrón < 101 habitantes')
print('-------------')
print('Soria')
print(partiesBalancedSoria_2)
print(dhondt(partiesBalancedSoria_2, 15))
print('Almazán')
print(partiesBalancedAlmazan_2)
print(dhondt(partiesBalancedAlmazan_2, 5))
print('Burgo de Osma-Ciudad de Osma')
print(partiesBalancedOsma_2)
print(dhondt(partiesBalancedOsma_2, 5))
if __name__ == '__main__':
df = get_data()
balanced = balance(df)
apply_dhondt(balanced)
municipalities = [
122,
162,
351,
364,
365,
390,
395,
396,
397,
423,
433,
507,
526,
527,
533,
537,
538,
545,
594,
692,
728,
744,
845,
877,
974,
991,
999,
1035,
1036,
1201,
1223,
1286,
1296,
1342,
1345,
1391,
1409,
1423,
1511,
1512,
1544,
1570,
1660,
1709,
1712,
1766,
1768,
1793,
1917,
1926,
1937,
2011,
2058,
2061,
2147,
2153,
2155,
2176,
2183,
2319,
2328,
2353,
2365,
2372,
2443,
2477,
2480,
2628,
2733,
2755,
2757,
2789,
2919,
2935,
2996,
2998,
2999,
3011,
3013,
3020,
3036,
3049,
3126,
3189,
3205,
3301,
3340,
3371,
3748,
3821,
3828,
3933,
3960,
4070,
4190,
4198,
4275,
4291,
4425,
4427,
4468,
4575,
4576,
4602,
4612,
4669,
4683,
4691,
4780,
4822,
4823,
4866,
4873,
4906,
4966,
4991,
5000,
5111,
5459,
5552,
5573,
5743,
5751,
5772,
5797,
5802,
5817,
5825,
5836,
5848,
5905,
5957,
6055,
6133,
6141,
6163,
6227,
6414,
6459,
6463,
6631,
6694,
6716,
6733,
6753,
6779,
6780,
6794,
6812,
6817,
6834,
6919,
6962,
7061,
7120,
7156,
7213,
7235,
7250,
7260,
7269,
7281,
7295,
7303,
7419,
7472,
7480,
7481,
7510,
7619,
7772,
7851,
7854,
7862,
7917,
7921,
8009,
8024,
8030,
8053,
8061,
8152,
8166
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment