Diagrama entidad-relación

El esquema PostgreSQL de eleccionesdb sigue un modelo estrella con 6 tablas de dimensiones y 4 tablas de hechos (2 principales + 2 CERA).

erDiagram tipos_eleccion { VARCHAR codigo PK VARCHAR descripcion } elecciones { INT id PK VARCHAR tipo_eleccion FK CHAR year CHAR mes CHAR dia DATE fecha CHAR codigo_ccaa SMALLINT numero_vuelta VARCHAR descripcion VARCHAR ambito VARCHAR slug } elecciones_fuentes { INT eleccion_id PK VARCHAR fuente VARCHAR url_fuente TEXT observaciones } territorios { INT id PK VARCHAR tipo CHAR codigo_ccaa CHAR codigo_provincia CHAR codigo_municipio CHAR codigo_distrito CHAR codigo_seccion CHAR codigo_circunscripcion VARCHAR nombre VARCHAR codigo_completo INT parent_id FK } partidos_recode { INT id PK VARCHAR partido_recode VARCHAR agrupacion VARCHAR color } partidos { INT id PK INT partido_recode_id FK VARCHAR siglas VARCHAR denominacion } resumen_territorial { INT id PK INT eleccion_id FK INT territorio_id FK INT censo_ine INT participacion_1 INT participacion_2 INT participacion_3 INT votos_validos INT abstenciones INT votos_blancos INT votos_nulos INT nrepresentantes } votos_territoriales { INT id PK INT eleccion_id FK INT territorio_id FK INT partido_id FK INT votos INT representantes } resumen_cera { INT id PK INT eleccion_id FK INT territorio_id FK INT censo_ine INT votos_validos INT abstenciones INT votos_blancos INT votos_nulos } votos_cera { INT id PK INT eleccion_id FK INT territorio_id FK INT partido_id FK INT votos } tipos_eleccion ||--o{ elecciones : tipo_eleccion elecciones ||--o| elecciones_fuentes : eleccion_id territorios ||--o{ territorios : parent_id partidos_recode ||--o{ partidos : partido_recode_id elecciones ||--o{ resumen_territorial : eleccion_id territorios ||--o{ resumen_territorial : territorio_id elecciones ||--o{ votos_territoriales : eleccion_id territorios ||--o{ votos_territoriales : territorio_id partidos ||--o{ votos_territoriales : partido_id elecciones ||--o{ resumen_cera : eleccion_id territorios ||--o{ resumen_cera : territorio_id elecciones ||--o{ votos_cera : eleccion_id territorios ||--o{ votos_cera : territorio_id partidos ||--o{ votos_cera : partido_id

Tablas de dimensiones

elecciones_fuentes

Documenta la fuente oficial de los datos para cada elección. Una fila por eleccion_id.

ColumnaTipoDescripción
eleccion_idINT PK, FKReferencia a elecciones.id
fuenteVARCHAR(255)Nombre de la fuente (ej: Ministerio del Interior)
url_fuenteVARCHAR(500)URL a la fuente oficial
observacionesTEXTNotas u observaciones adicionales (opcional)

tipos_eleccion

Catálogo de tipos de elección con un código de un carácter.

ColumnaTipoDescripción
codigoVARCHAR(1) PKCódigo del tipo: G (Generales), A (Autonómicas), L (Locales), E (Europeas), S (Senado)
descripcionVARCHAR(100)Nombre descriptivo del tipo de elección

elecciones

Cada fila identifica una convocatoria electoral concreta.

ColumnaTipoDescripción
idINT PKIdentificador autogenerado
tipo_eleccionVARCHAR(1) FKReferencia a tipos_eleccion.codigo
yearCHAR(4)Año de la elección
mesCHAR(2)Mes (con cero a la izquierda)
diaCHAR(2)Día (con cero a la izquierda)
fechaDATEFecha completa
codigo_ccaaCHAR(2)Código de comunidad autónoma (NULL para generales)
numero_vueltaSMALLINTNúmero de vuelta (default 1)
descripcionVARCHAR(255)Texto descriptivo legible (ej: “Generales junio 2016”)
ambitoVARCHAR(50)Ámbito: “nacional”, “autonómico”, etc.
slugVARCHAR(50)Identificador legible para URLs

Restricción UNIQUE: (tipo_eleccion, year, mes, codigo_ccaa, numero_vuelta)

territorios

Jerarquía territorial con auto-referencia vía parent_id. Cada registro representa un nivel territorial único.

ColumnaTipoDescripción
idINT PKIdentificador autogenerado
tipoterritorio_tipo ENUMNivel: ccaa, provincia, circunscripcion, municipio, distrito, seccion, cera
codigo_ccaaCHAR(2)Código INE de comunidad autónoma
codigo_provinciaCHAR(2)Código INE de provincia
codigo_municipioCHAR(3)Código INE de municipio
codigo_distritoCHAR(2)Código de distrito censal
codigo_seccionCHAR(4)Código de sección censal
codigo_circunscripcionCHAR(3)Código de circunscripción (sub-provincial en Canarias, Asturias, Baleares)
nombreVARCHAR(255)Nombre del territorio
codigo_completoVARCHAR(13)Código concatenado completo
parent_idINT FKReferencia al territorio padre en la jerarquía

Restricción UNIQUE: (tipo, codigo_ccaa, codigo_provincia, codigo_municipio, codigo_distrito, codigo_seccion, codigo_circunscripcion)

partidos_recode

Agrupaciones y recodificaciones de partidos. Permite agrupar siglas históricas o regionales bajo una misma etiqueta analítica.

ColumnaTipoDescripción
idINT PKIdentificador autogenerado
partido_recodeVARCHAR(50) UNIQUENombre de la agrupación (ej: “PSOE”, “PP”, “Otros”)
agrupacionVARCHAR(50)Agrupación de nivel superior
colorVARCHAR(7)Color hexadecimal para visualización

partidos

Cada par único (siglas, denominacion) tal como aparece en los datos oficiales de cada elección.

ColumnaTipoDescripción
idINT PKIdentificador autogenerado
partido_recode_idINT FKReferencia a partidos_recode.id
siglasVARCHAR(255)Siglas del partido
denominacionVARCHAR(255)Denominación completa del partido

Restricción UNIQUE: (siglas, denominacion)

Tablas de hechos

resumen_territorial

Resumen de censo, participación y votos agregados por elección y territorio. Una fila por cada combinación (eleccion_id, territorio_id).

ColumnaTipoDescripción
eleccion_idINT FKReferencia a elecciones.id
territorio_idINT FKReferencia a territorios.id
censo_ineINTCenso electoral (electores con derecho a voto)
participacion_1INTPrimer avance de participación
participacion_2INTSegundo avance de participación
participacion_3INTParticipación final
votos_validosINTTotal de votos válidos
abstencionesINTAbstenciones
votos_blancosINTVotos en blanco
votos_nulosINTVotos nulos
nrepresentantesINTNúmero de escaños asignados a ese territorio

votos_territoriales

Votos por partido, elección y territorio. Una fila por cada combinación (eleccion_id, territorio_id, partido_id).

ColumnaTipoDescripción
eleccion_idINT FKReferencia a elecciones.id
territorio_idINT FKReferencia a territorios.id
partido_idINT FKReferencia a partidos.id
votosINTNúmero de votos recibidos
representantesINTRepresentantes electos (si aplica)

resumen_cera y votos_cera

Tablas análogas a resumen_territorial y votos_territoriales para el voto CERA (Censo de Españoles Residentes Ausentes). Estructura similar pero sin avances de participación ni nrepresentantes.

Jerarquía territorial

El campo parent_id de la tabla territorios codifica la siguiente jerarquía:

flowchart TD CCAA["🏛️ Comunidad Autónoma\n(tipo = ccaa)"] PROV["🗺️ Provincia\n(tipo = provincia)"] CIRC["📍 Circunscripción\n(tipo = circunscripcion)"] MUNI["🏘️ Municipio\n(tipo = municipio)"] DIST["📊 Distrito\n(tipo = distrito)"] SECC["📋 Sección censal\n(tipo = seccion)"] CCAA --> PROV PROV --> CIRC CIRC --> MUNI PROV --> MUNI MUNI --> DIST DIST --> SECC
  • Cada CCAA es padre de sus provincias.
  • Cada provincia es padre de sus municipios (o de circunscripciones sub-provinciales en Canarias, Asturias y Baleares).
  • Las circunscripciones sub-provinciales (islas en Canarias y Baleares; Occidente/Centro/Oriente en Asturias) son padres de sus municipios.
  • Cada municipio es padre de sus distritos.
  • Cada distrito es padre de sus secciones censales.

Este diseño permite navegar la jerarquía de forma recursiva: dado cualquier territorio, se puede subir o bajar en la jerarquía siguiendo parent_id.

Sistema de partidos

El modelo utiliza dos tablas separadas para gestionar la complejidad del panorama partidista español:

Dos niveles de detalle

  1. partidos: Recoge cada par (siglas, denominacion) tal como aparece en los datos oficiales. Un mismo partido puede aparecer con distintas denominaciones a lo largo de los años o en distintas comunidades.

  2. partidos_recode: Agrupa las distintas apariciones bajo una etiqueta analítica común. Por ejemplo, todas las variantes de “Partido Socialista Obrero Español”, “PSOE”, “PSC-PSOE” pueden agruparse bajo el recode “PSOE”. Incluye además un campo agrupacion para agrupaciones de nivel superior y un color para visualización.

Flujo de asignación

  1. Los scripts de generación de datos producen votos con (siglas, denominacion) tal cual.
  2. new-partidos.R detecta combinaciones nuevas sin recodificación asignada.
  3. Se revisan manualmente y se añaden a partidos_recodes.xlsx.
  4. sync-partidos.R sincroniza las dimensiones partidos y partidos_recode.
  5. 02-bind-hechos.R asigna partido_id por matching case-insensitive — falla si quedan votos sin match.