In [165]:
import numpy as np
import matplotlib.pyplot as pl
import openpyxl
import dateutil
import datetime
import pickle
import pystan
import seaborn as sns
import os
import csv
from hashlib import md5
import matplotlib
import bokeh.plotting as bp
from bokeh.models import HoverTool
from bokeh.embed import components
import bokeh
import dhondt
import copy
bp.output_notebook()
%matplotlib notebook
In [2]:
matplotlib.get_backend()
print(pystan.__version__)
print(bokeh.__version__)
Vamos a plantearnos como ejercicio analizar los sondeos electorales. Para ello cogemos como datos los sondeos recopilados en la página web de la Wikipedia para las elecciones de 2015 y para las de 2016. Consideramos sólo los partidos PP, PSOE, IU, Podemos y Ciudadanos. El resto de partidos se consideran metidos dentro de un mismo saco, que vienen siendo alrededor del 15% en cada sondeo.
Para modelar la evolución temporal de los sondeos para cada partido, usamos un modelado con variables latentes. La idea es que los resultados de los sondeos son sólo una estimación ruidosa y sesgada de la intención de voto real de los votantes. Por tanto, considamos que $\theta_i(t_j)$ es la subyacente intención de voto para el partido $i$ en el tiempo $t_j$. Esta variable se observa de forma ruidosa para cada empresa $k$ que realiza el sondeo, y además incluye un cierto sesgo. Por tanto, nuestro modelo de observación es:
$$ y_{ik}(t_j) \sim N(\theta_i(t_j) + b_{ik}, \sigma_{k}(t_j)^2) $$donde $b_{ik}$ es el bias que introduce la empresa de sondeos $k$ sobre el partido $i$ y $\sigma_{ik}^2$ es la varianza del sondeo realizado en el tiempo $t_j$ por la empresa $k$. Para los resultados de las elecciones, elegimos una varianza muy pequeña y no incluimos los sesgos, porque es el único momento en el que tenemos acceso directo a las intenciones subyacentes:
$$ y_{ik}(\mathrm{elecciones}) \sim N(\theta_i(t_j), \sigma_{k}(\mathrm{elecciones})^2). $$Es obvio que la suma de porcentajes de cada partido debe sumar 1 en cada sondeo, por lo cual debemos asegurarnos de que
$$ \sum_i \theta_i(t_j) = 1. $$Además, es lógico asumir que, dadas suficientes casas de sondeo, los biases sumen 0 para cada partido:
$$ \sum_k b_{ik} = 0. $$Por último, asumimos un proceso autoregresivo Markoviano para modelar la variación temporal de la intención de voto real para cada partido. Es decir, en cada sondeo, la intención de voto estará dada por una variable Gaussiana con media igual a la intención de voto anterior, y una varianza que debemos estimar para cada partido:
$$ \theta_i(t_j) \sim N(\theta_i(t_{j-1}), \sigma_i^2) $$Como es un parámetro de escala, definimos un prior poco informativo sobre $\sigma_i$:
$$ \sigma_i \sim IG(0.01,0.01). $$Por último, debemos definir un prior para $\theta_i(t_0)$, para el cual escogemos una distribución de Dirichlet con $\alpha=1$:
$$ \theta_i(t_0) \sim \mathrm{Dir}(\alpha). $$Definimos algunas funciones útiles para el parsing de la base de datos.
In [179]:
def stan_cache(model_code, model_name=None, **kwargs):
"""Use just as you would `stan`"""
code_hash = md5(model_code.encode('ascii')).hexdigest()
if model_name is None:
cache_fn = 'cached-model-{}.pkl'.format(code_hash)
else:
cache_fn = 'cached-{}-{}.pkl'.format(model_name, code_hash)
try:
sm = pickle.load(open(cache_fn, 'rb'))
except:
sm = pystan.StanModel(model_code=model_code)
with open(cache_fn, 'wb') as f:
pickle.dump(sm, f)
else:
print("Using cached StanModel")
return sm.sampling(**kwargs)
def toenglish(s):
spanish = ['ene', 'abr', 'ago', 'dic', 'de mayo de']
english = ['jan', 'apr', 'aug', 'dec', 'may']
for (j, month) in enumerate(spanish):
s = s.replace(month, english[j])
return s
def getPercentage(s):
if (s[0] not in ['0','1','2','3','4','5','6','7','8','9']):
return 0
else:
if (s.find('%') != -1):
return float(s.split('%')[0].replace(',','.')) / 100.0
else:
return float(s.split('\n')[0].replace(',','.')) / 100.0
def getSigma(s):
left = s.find('(')
right = s.find(')')
if (s[left+1:right] in ['?', '-']):
return 0.03
else:
return 1.0 / np.sqrt(float(s[left+1:right]))
def weeksDifference(d1, d2):
monday1 = (d1 - datetime.timedelta(days=d1.weekday()))
monday2 = (d2 - datetime.timedelta(days=d2.weekday()))
return int((monday2 - monday1).days / 7)
Leemos el fichero de datos obtenido de la Wikipedia y hacemos el parsing. Escogemos sólo aquellos sondeos en los que hay información para todos los partidos. Podríamos también escoger aquellos parciales porque el modelado Bayesiano los podría aceptar.
In [180]:
wb = openpyxl.load_workbook("data/new.xlsx")
ws = wb.active
empresaSondeoAll = []
sondeosAll = []
dateAll = []
sigmaAll = []
empresas = ['GAD3', 'Encuestamos', 'GESOP', 'Metroscopia', 'Celeste-Tel','Demoscopia Servicios', 'SimpleLogica', 'CIS',
'TNS', 'Invymark', 'NC', 'El Espanol', 'DYM', 'Sondaxe', 'SigmaDos', 'IMOP', 'Resultados de las elecciones']
loopSondeos = 0
for i in range(ws.max_row-1):
empresa = ws['A{0}'.format(i+2)].value
empresaSondeo = -1
for (loop, emp) in enumerate(empresas):
if (empresa.find(emp) != -1):
empresaSondeo = loop
if (empresaSondeo != -1):
PP = ws['D{0}'.format(i+2)].value
PSOE = ws['E{0}'.format(i+2)].value
UP = ws['F{0}'.format(i+2)].value
CS = ws['G{0}'.format(i+2)].value
otros = 100.0 - (PP+PSOE+UP+CS)
sondeo = [PP/100.0, PSOE/100.0, UP/100.0, CS/100.0, otros/100.0]
date = dateutil.parser.parse(ws['B{0}'.format(i+2)].value, dayfirst=True)
sigma = 1.0 / np.sqrt(ws['C{0}'.format(i+2)].value)
sondeosAll.append(sondeo)
sigmaAll.append(sigma)
dateAll.append(date)
empresaSondeoAll.append(empresaSondeo+1)
print ("{0} - {1} {7} - s={8:4.2f} : PP={2:4.2f} - PSOE={3:4.2f} - UP={4:4.2f} - CS={5:4.2f} - Resto={6:4.2f}".format(i,
empresas[empresaSondeo], PP, PSOE, UP, CS, otros, date, sigma*100))
loopSondeos += 1
sondeosAll = np.array(sondeosAll)
nSondeos, nPartidos = sondeosAll.shape
nEmpresas = len(empresas)
print ("Número de sondeos incluidos : {0}".format(loopSondeos))
# Compute week of every poll
weekAll = []
for i in range(nSondeos):
weekAll.append(weeksDifference(dateAll[nSondeos-1], dateAll[i]) + 1)
nDates = max(weekAll)
# Reverse all lists
sondeosAll = sondeosAll[::-1]
empresaSondeoAll.reverse()
weekAll.reverse()
sigmaAll.reverse()
dictionary = {'NPartidos': nPartidos, 'NSondeos': nSondeos, 'NEmpresas': nEmpresas-1, 'NDates': nDates, 'empresa': empresaSondeoAll, 'sondeos': sondeosAll,
'date': weekAll, 'sigmaSondeo': sigmaAll, 'alpha': np.ones(nPartidos)*1.0, 'eleccionesIndex': 17, 'NDatesPre': weekAll[sigmaAll.index(0.0001)]}
Leemos el modelo que hemos definido y hacemos el sampling.
In [5]:
f = open('model.stan', 'r')
model = f.read()
f.close()
In [6]:
out = stan_cache(model, model_name='elecciones', data=dictionary, chains=4)
In [11]:
thetaChain = out.extract('theta')['theta']
houseChain = out.extract('house')['house']
sigmaPreChain = out.extract('sigmaPre')['sigmaPre']
sigmaPostChain = out.extract('sigmaPost')['sigmaPost']
In [12]:
np.save('thetaChain.npy', thetaChain)
np.save('houseChain.npy', houseChain)
np.save('sigmaPreChain.npy', sigmaPreChain)
np.save('sigmaPostChain.npy', sigmaPostChain)
In [176]:
thetaChain = np.load('thetaChain.npy')
houseChain = np.load('houseChain.npy')
sigmaPreChain = np.load('sigmaPreChain.npy')
sigmaPostChain = np.load('sigmaPostChain.npy')
In [177]:
theta = np.percentile(thetaChain, [50.0, 50.0-68/2.0, 50.0+68/2,50.0-95/2.0, 50.0+95/2], axis=0)
house = np.percentile(houseChain, [50.0, 50.0-68/2.0, 50.0+68/2, 50.0-95/2.0, 50.0+95/2], axis=0)
sigmaPre = np.percentile(sigmaPreChain, [50.0, 50.0-68/2.0, 50.0+68/2, 50.0-95/2.0, 50.0+95/2], axis=0)
sigmaPost = np.percentile(sigmaPostChain, [50.0, 50.0-68/2.0, 50.0+68/2, 50.0-95/2.0, 50.0+95/2], axis=0)
En esta representación vemos la variación de las estimaciones de voto en puntos para cada una de las encuestas que hemos considerado y la estimación subyacente que hemos inferido. Como se ve, la varianza de la estimación subyacente cambia con el tiempo, siendo muy pequeña en la fecha de las elecciones, marcada con una línea vertical.
In [181]:
colors = ["blue", "red", "violet", "orange", "yellow"]
labelsPartidos = ['PP', 'PSOE', 'UP', 'Cs', 'Otros']
labelsEmpresas = ['GAD3', 'Encuestamos', 'GESOP', 'Metroscopia', 'Celeste-Tel','Demoscopia Servicios', 'SimpleLogica',
'CIS', 'TNS', 'Invymark', 'NC', 'El Espanol', 'DYM', 'Sondaxe', 'SigmaDos', 'IMOP']
f, ax = pl.subplots(figsize=(13,8))
for i in range(5):
ax.plot(weekAll, 100.0*sondeosAll[:,i], '.', color=sns.xkcd_rgb[colors[i]], linewidth=2)
ax.plot(np.arange(max(weekAll))+1, 100.0*theta[0,:,i], color=sns.xkcd_rgb[colors[i]], linewidth=2)
ax.fill_between(np.arange(max(weekAll))+1, 100.0*theta[1,:,i], 100.0*theta[2,:,i], color=sns.xkcd_rgb[colors[i]], alpha=0.3)
ax.set_xlabel('Semana')
ax.set_ylabel('Porcentaje de votos')
ax.axvline(54)
pl.savefig("sondeos.png")
In [262]:
labelsEmpresasAll = ['GAD3', 'Encuestamos', 'GESOP', 'Metroscopia', 'Celeste-Tel','Demoscopia Servicios', 'SimpleLogica',
'CIS', 'TNS', 'Invymark', 'NC', 'El Espanol', 'DYM', 'Sondaxe', 'SigmaDos', 'IMOP', 'Elecciones']
bokehPlot = bp.figure(plot_width=800, plot_height=600, background_fill_color="#EAEAF2", tools="pan,wheel_zoom,box_zoom,reset")
bokehPlot.grid.grid_line_color="white"
#generate the source data from the DataFrame
s = [None] * 5
for i in range(5):
bias = np.zeros(len(empresaSondeoAll))
for j in range(len(empresaSondeoAll)):
if (empresaSondeoAll[j]-1 != 16):
bias[j] = 100.*house[0,empresaSondeoAll[j]-1,i]
else:
bias[j] = 0.0
sourceData = bp.ColumnDataSource(
data = dict(
x = weekAll,
y = sondeosAll[:,i] * 100.0,
empresa = [labelsEmpresasAll[empresaSondeoAll[i]-1] for i in range(len(empresaSondeoAll))],
partido = [labelsPartidos[i] for j in range(len(empresaSondeoAll))],
estimacion = theta[0,np.array(weekAll)-1,i]*100.0,
house = bias
)
)
s[i] = bokehPlot.scatter(x='x', y='y', source=sourceData, color=sns.xkcd_rgb[colors[i]])
bokehPlot.add_tools(HoverTool(renderers=[s[i]], tooltips={"Semana":"$x", "Votos [%]":"$y", "Empresa": "@empresa",
"Partido": "@partido", "Estimación [%]": "@estimacion",
"Bias [%]": "@house"}))
bokehPlot.line(np.arange(max(weekAll))+1, theta[0,:,i]*100.0, color=sns.xkcd_rgb[colors[i]], line_width=2)
x_data = np.arange(max(weekAll))+1
band_x = np.append(x_data, x_data[::-1])
band_y = np.append(100.0*theta[2,:,i].flatten(), 100.0*theta[1,:,i].flatten()[::-1])
bokehPlot.patch(band_x, band_y, color=sns.xkcd_rgb[colors[i]], fill_alpha=0.3, line_color=None)
band_y = np.append(100.0*theta[4,:,i].flatten(), 100.0*theta[3,:,i].flatten()[::-1])
bokehPlot.patch(band_x, band_y, color=sns.xkcd_rgb[colors[i]], fill_alpha=0.2, line_color=None)
bokehPlot.xaxis.axis_label = 'Semana'
bokehPlot.xaxis.axis_label = 'Porcentaje de votos'
bp.show(bokehPlot)
Out[262]:
In [263]:
script, div = components(bokehPlot)
In [115]:
f, ax = pl.subplots()
sns.boxplot(data=thetaChain[:,-1,:], ax=ax)
ax.set_xticks(np.arange(5))
ax.set_xticklabels(labelsPartidos, rotation=90)
boxes = ax.artists
for (i, box) in enumerate(boxes):
box.set_facecolor(sns.xkcd_rgb[colors[i]])
pl.savefig("estimacionActual.png")
In [91]:
thetaChain[:,-1,:].shape
Out[91]:
In [116]:
f, ax = pl.subplots(figsize=(12,6))
for i in range(5):
ax.hist(100*thetaChain[:,-1,i], bins=40, color=sns.xkcd_rgb[colors[i]])
A continuación represento la evolución temporal del voto por cada partido político, incluyendo las incertidumbres estimadas para cada sondeo.
In [195]:
labelsPartidos = ['PP', 'PSOE', 'UP', 'Cs', 'Otros']
f, ax = pl.subplots(nrows=2, ncols=3, figsize=(10,8))
ax = ax.flatten()
for i in range(5):
ax[i].plot(weekAll, 100*sondeosAll[:,i], '.', color=sns.xkcd_rgb[colors[i]], linewidth=2)
ax[i].set_ylim([0,35])
ax[i].errorbar(weekAll, 100*sondeosAll[:,i], fmt='none', yerr=100*np.array(sigmaAll), color=sns.xkcd_rgb[colors[i]], linewidth=2, ecolor=sns.xkcd_rgb[colors[i]])
ax[i].plot(np.arange(max(weekAll))+1, 100*theta[0,:,i], color=sns.xkcd_rgb[colors[i]], linewidth=2)
ax[i].fill_between(np.arange(max(weekAll))+1, 100*theta[1,:,i], 100*theta[2,:,i], color=sns.xkcd_rgb[colors[i]], alpha=0.3)
ax[i].set_xlabel('Semana')
ax[i].set_ylabel('Porcentaje de votos')
ax[i].set_title(labelsPartidos[i])
pl.tight_layout()
pl.savefig("porPartido.png")
Represento a continuación las estimaciones de sesgo para cada una de las empresas encuestadoras, lo que muestra algún efecto curioso en Demoscopia Servicios que sería interesante analizar y entender por qué pasa. Nótese que un sesgo positivo implica que esa empresa subestima los votos y hace falta corregirlo por esa cantidad para obtener las observaciones.
In [118]:
f, ax = pl.subplots(ncols=3, nrows=2, figsize=(12,10))
ax = ax.flatten()
for i in range(5):
sns.boxplot(data=houseChain[:,:,i]*100., ax=ax[i])
ax[i].set_xticks(np.arange(16))
ax[i].set_xticklabels(labelsEmpresas, rotation=90)
ax[i].set_title(labelsPartidos[i])
ax[i].set_ylabel('Sesgo [%]')
pl.tight_layout()
pl.savefig("bias.png")
In [119]:
f, ax = pl.subplots(ncols=2, nrows=1, figsize=(12,6))
data = [sigmaPreChain, sigmaPostChain]
labels = ['Antes de elecciones','Después de elecciones']
for i in range(2):
sns.boxplot(data=data[i], ax=ax[i])
ax[i].set_xticks(np.arange(5))
ax[i].set_xticklabels(labelsPartidos, rotation=90)
ax[i].set_title(labels[i])
ax[i].set_ylim([0,0.014])
boxes = ax[i].artists
for (j, box) in enumerate(boxes):
box.set_facecolor(sns.xkcd_rgb[colors[j]])
pl.savefig("variabilidad.png", facecolor=f.get_facecolor(), edgecolor='none')
In [62]:
house.shape
Out[62]:
In [120]:
empresaSondeoAll = np.array(empresaSondeoAll)
weekAll = np.array(weekAll)
difference0 = []
difference1 = []
shift = 4
labelShift = ['Presente', '1 mes']
for i in range(np.max(empresaSondeoAll)-1):
ind = np.where((empresaSondeoAll == i+1) & (weekAll < 75 - shift))[0]
weeks = weekAll[ind] - 1
values = sondeosAll[ind,:] - theta[0,weeks+shift,:] - house[0,i,:]
difference1.append(values)
values = sondeosAll[ind,:] - theta[0,weeks,:] - house[0,i,:]
difference0.append(values)
f, ax = pl.subplots(ncols=2, nrows=len(difference0)-1, figsize=(12,50))
for i in range(len(difference0)-1):
#for i in range(2):
sns.boxplot(data=100*difference0[i], ax=ax[i,0])
sns.boxplot(data=100*difference1[i], ax=ax[i,1])
for j in range(2):
ax[i,j].set_xticks(np.arange(5))
ax[i,j].set_xticklabels(labelsPartidos, rotation=90)
ax[i,j].set_title("{0} - {1}".format(labelsEmpresas[i], labelShift[j]))
ax[i,j].set_ylim([-8,8])
ax[i,j].set_ylabel('Sondeo - subyacente - bias [%]')
pl.tight_layout()
pl.savefig("biasOneMonth.png")
In [235]:
pct20D = np.array([28.7,22.0,24.4,13.9])
avoid = ['Censo', 'Votantes', 'Nulos', 'Válidos', 'Blancos']
votos = np.empty(52, dtype='object')
pct = np.empty(52, dtype='object')
escanos = np.empty(52, dtype='object')
for i in range(52):
votos[i] = {}
pct[i] = {}
escanos[i] = {}
votantes = np.zeros(52)
with open('data/20D.csv', 'r') as csvfile:
reader = csv.reader(csvfile, delimiter=',')
for row in reader:
if (row[1] != '' and row[3] not in avoid):
prov = int(row[1])
partido = row[3]
votos[prov-1][partido] = int(row[4])
pct[prov-1][partido] = float(row[5])
escanos[prov-1][partido] = int(row[6])
escanosTotal = np.zeros(52, dtype='int32')
for i in range(52):
for d in escanos[i]:
escanosTotal[i] += int(escanos[i][d])
votantesTotal = np.sum(votantes)
In [236]:
for i in range(52):
key = [key for key, value in votos[i].items() if 'PODEMOS' in key]
for d in votos[i]:
if ('IU-UP' in d):
votos[i][key[0]] += votos[i][d]
del votos[i][d]
break
In [237]:
escanos20D = np.zeros((4))
votosOrig = votos.copy()
for i in range(52):
key = [key for key, value in escanos[i].items() if 'PP' in key]
if (len(key) != 0):
escanos20D[0] += escanos[i][key[0]]
key = [key for key, value in escanos[i].items() if 'PSOE' in key]
if (len(key) != 0):
escanos20D[1] += escanos[i][key[0]]
key = [key for key, value in escanos[i].items() if 'PODEMOS' in key]
if (len(key) != 0):
escanos20D[2] += escanos[i][key[0]]
key = [key for key, value in escanos[i].items() if "C's" in key]
if (len(key) != 0):
escanos20D[3] += escanos[i][key[0]]
print(escanos20D)
In [241]:
nSamples, _, _ = thetaChain.shape
escanos26J = np.zeros((nSamples,4))
for loop in range(nSamples):
ratio = thetaChain[loop,-1,0:4] / (pct20D*0.01)
votos = copy.deepcopy(votosOrig)
for i in range(52):
key = [key for key, value in votos[i].items() if 'PP' in key]
if (len(key) != 0):
votos[i][key[0]] *= ratio[0]
key = [key for key, value in votos[i].items() if 'PSOE' in key]
if (len(key) != 0):
votos[i][key[0]] *= ratio[1]
key = [key for key, value in votos[i].items() if 'PODEMOS' in key]
if (len(key) != 0):
votos[i][key[0]] *= ratio[2]
key = [key for key, value in votos[i].items() if "C's" in key]
if (len(key) != 0):
votos[i][key[0]] *= ratio[3]
dh = dhondt.dhondt(int(escanosTotal[i]), 3.0, votos[i])
newEscanos = dh.repre
key = [key for key, value in newEscanos.items() if 'PP' in key]
if (len(key) != 0):
escanos26J[loop,0] += newEscanos[key[0]]
key = [key for key, value in newEscanos.items() if 'PSOE' in key]
if (len(key) != 0):
escanos26J[loop,1] += newEscanos[key[0]]
key = [key for key, value in newEscanos.items() if 'PODEMOS' in key]
if (len(key) != 0):
escanos26J[loop,2] += newEscanos[key[0]]
key = [key for key, value in newEscanos.items() if "C's" in key]
if (len(key) != 0):
escanos26J[loop,3] += newEscanos[key[0]]
In [257]:
f, ax = pl.subplots(nrows=2, ncols=2, figsize=(12,8))
ax = ax.flatten()
binwidth = 1
for i in range(4):
for j in range(4):
if (i == j):
data = escanos26J[0:nSamples,i]
ax[i].hist(data, bins=np.arange(min(data), max(data) + binwidth, binwidth), color=sns.xkcd_rgb[colors[i]], normed=True)
ax[i].set_xlim([min(data)-10, max(data)+10])
ax[i].set_xlabel('Escaños')
ax[i].set_ylabel('Frecuencia')
ax[i].set_title(labelsPartidos[i])
ax[i].axvline(escanos20D[i], color=sns.xkcd_rgb[colors[i]])
else:
data = escanos26J[0:nSamples,j]
ax[i].hist(data, bins=np.arange(min(data), max(data) + binwidth, binwidth), color=sns.xkcd_rgb[colors[j]], normed=True, alpha=0.3)
pl.tight_layout()
pl.savefig("escanos.png")
In [258]:
f, ax = pl.subplots(nrows=4, ncols=4, figsize=(12,10))
binwidth = 1
for i in range(4):
for j in range(4):
if (i < j):
data = escanos26J[0:nSamples,i] + escanos26J[0:nSamples,j]
ax[i,j].hist(data, bins=np.arange(min(data), max(data) + binwidth, binwidth), normed=True)
ax[i,j].set_xlim([min(data)-20, max(data)+20])
ax[i,j].set_xlabel('Escaños')
ax[i,j].set_ylabel('Frecuencia')
ax[i,j].set_title('{0}+{1}'.format(labelsPartidos[i],labelsPartidos[j]))
ax[i,j].axvline(176)
if (i == j):
data = escanos26J[0:nSamples,j]
ax[i,j].hist(data, bins=np.arange(min(data), max(data) + binwidth, binwidth), normed=True, color=sns.xkcd_rgb[colors[j]])
pl.tight_layout()
pl.savefig("escanosCoaliciones.png")
In [259]:
f = open('elecciones2015/indexOriginal.html','r')
lines = f.readlines()
f.close()
In [260]:
fecha = datetime.datetime.now()
lines[32] = """<a id="welcome-to-github-pages" class="anchor" href="#welcome-to-github-pages" aria-hidden="true"><span aria-hidden="true" class="octicon octicon-link"></span></a>Resultado {0}/{1}/{2}</h3>\n""".format(fecha.day,fecha.month,fecha.year)
thetas = np.percentile(thetaChain, [50.0, 50.0-95/2.0, 50.0+95/2], axis=0)
lines[36] = """<td style='height: 50px; background: #02B3F4; color: white; font-size: 25px; border-radius: 1px 10px 0px 0px;'>{0:4.1f}-{1:4.1f}-{2:4.1f}</td>\n""".format(thetas[1,-1,0]*100,thetas[0,-1,0]*100,thetas[2,-1,0]*100)
lines[37] = """<td style='height: 50px; background: #8E1744; color: white; font-size: 25px; border-radius: 1px 10px 0px 0px;'>{0:4.1f}-{1:4.1f}-{2:4.1f}</td>\n""".format(thetas[1,-1,2]*100,thetas[0,-1,2]*100,thetas[2,-1,2]*100)
lines[38] = """<td style='height: 50px; background: #FF0202; color: white; font-size: 25px; border-radius: 1px 10px 0px 0px;'>{0:4.1f}-{1:4.1f}-{2:4.1f}</td>\n""".format(thetas[1,-1,1]*100,thetas[0,-1,1]*100,thetas[2,-1,1]*100)
lines[39] = """<td style='height: 50px; background: #FF800E; color: white; font-size: 25px; border-radius: 1px 10px 0px 0px;'>{0:4.1f}-{1:4.1f}-{2:4.1f}</td>\n""".format(thetas[1,-1,3]*100,thetas[0,-1,3]*100,thetas[2,-1,3]*100)
In [264]:
f = open('elecciones2015/index.html','w')
for j in range(21):
f.write(lines[j])
f.write(script)
for j in range(21,106):
f.write(lines[j])
f.write(div)
for j in range(106,len(lines)):
f.write(lines[j])
f.close()
In [265]:
os.system('./update.sh')
Out[265]:
In [ ]: