Last active
June 1, 2023 11:03
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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