"""E8 · referencia de carburantes (sin red), implementación 1. - Geoportal MITECO (precio por estación y día; descargado un lunes por semana 2015-2026 + el día del sondeo): para cada día y provincia, media de las estaciones con precio de gasóleo A, gasóleo B y gasolina 95 E5 (con impuestos). Provincia-día con < 5 estaciones → media nacional del día (Península y Baleares). Referencia mensual = media de los días muestreados del mes. - Gasóleo C (calefacción): no está en el geoportal → Weekly Oil Bulletin (Comisión; datos que remite el MITECO), España, «heating oil» con impuestos, media de las semanas del mes. Nacional. - Todo ÷ 1,21 (IVA) → € por litro SIN IVA, comparable con imp_adj_eur_comparable. Salidas: salidas/ref_carb_dia.csv (día × provincia × producto), salidas/ref_carb_mensual.csv (mes × provincia × producto; provincia '00' = nacional), salidas/ref_carb_wob_mensual.csv (WOB nacional: diésel, euro-super 95, calefacción).""" import json, gzip, glob, csv, collections, statistics as st, datetime as dt, zipfile, re, os import xml.etree.ElementTree as ET G = '/ref/carburantes/geoportal'; W = '/ref/carburantes/wob/wob_history.xlsx' FUERA = {'35', '38', '51', '52'} CAMPO = {'gasoleo_a': 'Precio Gasoleo A', 'gasoleo_b': 'Precio Gasoleo B', 'gasolina95': 'Precio Gasolina 95 E5'} num = lambda s: float(s.replace(',', '.')) if s and s.strip() else None dia_rows = []; mens = collections.defaultdict(list) ficheros = sorted(glob.glob(f'{G}/geo_hist_*.json.gz')) for f in ficheros: j = json.loads(gzip.open(f).read()) d = dt.datetime.strptime(j['Fecha'].split()[0], '%d/%m/%Y').date() acc = collections.defaultdict(lambda: collections.defaultdict(list)) for e in j['ListaEESSPrecio']: pid = e.get('IDProvincia') for p, c in CAMPO.items(): v = num(e.get(c, '')) if v and v > 0: acc[pid][p].append(v) pen = [pid for pid in acc if pid not in FUERA] for p in CAMPO: vals = [v for pid in pen for v in acc[pid][p]] nac = st.mean(vals) dia_rows.append((d.isoformat(), '00', p, len(vals), round(nac, 5), 'nacional')) mens[(f'{d:%Y-%m}', '00', p)].append(nac) for pid in sorted(pen): vs = acc[pid][p] if len(vs) >= 5: v, fuente = st.mean(vs), 'provincia' else: v, fuente = nac, 'nacional_por_pocas_estaciones' dia_rows.append((d.isoformat(), pid, p, len(vs), round(v, 5), fuente)) mens[(f'{d:%Y-%m}', pid, p)].append(v) with open('/p/salidas/ref_carb_dia.csv', 'w', newline='') as fh: w = csv.writer(fh); w.writerow(['dia', 'prov_ine', 'producto', 'estaciones', 'media_con_iva_eur_l', 'fuente']); w.writerows(dia_rows) # ---- WOB ns = {'m': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'} z = zipfile.ZipFile(W) ss = [''.join(t.itertext()) for t in ET.fromstring(z.read('xl/sharedStrings.xml')).findall('m:si', ns)] def hoja(path): head = {}; out = [] for ev, el in ET.iterparse(z.open(path)): if not el.tag.endswith('}row'): continue r = {} for c in el.findall('m:c', ns): v = c.find('m:v', ns); x = v.text if v is not None else None if c.get('t') == 's' and x is not None: x = ss[int(x)] r[''.join(ch for ch in c.get('r') if ch.isalpha())] = x if int(el.get('r')) == 1: head = {v: k for k, v in r.items() if v} elif int(el.get('r')) >= 4: out.append(r) el.clear() return head, out head, rws = hoja('xl/worksheets/sheet1.xml') # «with taxes» colw = {p: head.get(f'ES_price_with_tax_{p}') for p in ('euro95', 'diesel', 'heating_oil')} assert all(colw.values()), colw wob = collections.defaultdict(lambda: collections.defaultdict(list)) for r in rws: try: d = dt.date(1899, 12, 30) + dt.timedelta(days=int(float(r['A']))) except Exception: continue for p, c in colw.items(): v = r.get(c) if v not in (None, ''): wob[f'{d:%Y-%m}'][p].append(float(v) / 1000) wob_rows = [] for mes in sorted(wob): if '2014-12' < mes < '2026-11': wob_rows.append([mes] + [st.mean(wob[mes][p]) if wob[mes][p] else '' for p in ('diesel', 'euro95', 'heating_oil')] + [len(wob[mes]['diesel'])]) with open('/p/salidas/ref_carb_wob_mensual.csv', 'w', newline='') as fh: w = csv.writer(fh); w.writerow(['mes', 'wob_diesel_con_iva', 'wob_euro95_con_iva', 'wob_calefaccion_con_iva', 'semanas']); w.writerows(wob_rows) # ---- mensual filas = [] for (mes, pid, p), vs in sorted(mens.items()): filas.append((mes, pid, p, len(vs), st.mean(vs), st.mean(vs) / 1.21, 'geoportal')) # sin redondear (la 2.ª implementación compara a 1e-6) for r in wob_rows: if r[3] != '': filas.append((r[0], '00', 'gasoleo_c', r[4], r[3], r[3] / 1.21, 'wob')) with open('/p/salidas/ref_carb_mensual.csv', 'w', newline='') as fh: w = csv.writer(fh); w.writerow(['mes', 'prov_ine', 'producto', 'dias_o_semanas', 'ref_con_iva_eur_l', 'ref_sin_iva_eur_l', 'fuente']) w.writerows(filas) meses = sorted({x[0] for x in filas if x[6] == 'geoportal'}) print('ficheros', len(ficheros), 'días', len({x[0] for x in dia_rows}), 'meses geoportal', len(meses), meses[0], meses[-1], 'filas mensuales', len(filas), 'pocas estaciones', sum(1 for x in dia_rows if x[5].startswith('nacional_por')))