d3 = require("d3@7")
seats = await d3.csv("https://d1bjgq97if6urz.cloudfront.net/Public/Peilingwijzer/PW2023/Last/coa_seats.csv")
seats_av = await d3.csv("https://d1bjgq97if6urz.cloudfront.net/Public/Peilingwijzer/PW2023/Last/coa_seats_av.csv")
// Load data using FileAttachment
// seats = await FileAttachment("resources/coa_seats.csv").csv({typed: true})
// seats_av = await FileAttachment("resources/coa_seats_av.csv").csv({typed: true})
// Get party names from coa_seats_av.csv
partyNames = seats_av.map(d => d.party)
// Controls: party selection
viewof selectedParties = Inputs.checkbox(partyNames, {label: "Kies partijen voor coalitie:", value: ["VVD", "BBB"]})
// Compute coalition seats for each simulation
coalitionSeats = selectedParties.length > 0
? seats.map(row => d3.sum(selectedParties.map(party => row[party])))
: []
// Filter average seats for selected parties
averageSeats = seats_av.filter(d => selectedParties.includes(d.party))
// Load party colors
partycolours = await FileAttachment("resources/partycolours.csv").csv({typed: true})
// Create color domain and range from the CSV
partyColorDomain = partycolours.map(d => d.party)
partyColorRange = partycolours.map(d => d.colour)
// Only show legend for selected parties
selectedPartyColors = partycolours.filter(d => selectedParties.includes(d.party))
selectedPartyColorDomain = selectedPartyColors.map(d => d.party)
selectedPartyColorRange = selectedPartyColors.map(d => d.colour)
ciLower = d3.quantile(coalitionSeats, 0.025)
ciUpper = d3.quantile(coalitionSeats, 0.975)
meanAndCI = [{
mean: averageSeats,
ciLower: ciLower,
ciUpper: ciUpper
}]
Gemiddeld aantal zetels voor deze coalitie
// Top stacked horizontal bar: average seats per selected party, all in one bar
selectedParties.length > 0 && seatCounts.length > 0
? Plot.plot({
marginLeft: 10,
color: {
legend: true,
label: "Partij",
domain: selectedPartyColorDomain,
range: selectedPartyColorRange
},
x: {label: "Gemiddeld aantal zetels", domain: [0, 150], grid: true, ticks: d3.range(0, 151, 15)},
y: {axis: null},
height: 70,
width: 800,
marks: [
Plot.barX(
averageSeats,
Plot.groupZ(
{x: "sum"}, // sum the zetels for each partij
{y: "", x: "zetels", fill: "party"}
)
),
Plot.ruleY(meanAndCI, {
y: "",
x1: "ciLower",
x2: "ciUpper",
stroke: "lightgrey",
marker: "none"
}),
Plot.ruleX([75], {stroke: "grey", strokeDasharray: "4,4", strokeWidth: 1})
]
})
: md`**Selecteer ten minste één partij om de grafiek te tonen.**`
selectedParties.length > 0
? md`Deze coalitie haalt nu gemiddeld **${d3.sum(averageSeats, d => d.zetels).toFixed(0)}** zetels in de Peilingwijzer, maar we hebben te maken met een onzekerheidsmarge, dus het kunnen het er ook ${ciLower.toFixed(0)} of ${ciUpper.toFixed(0)} zijn (95% credible interval). Let op: dit is de situatie op het moment dat de laatste peiling werd uitgevoerd, **geen voorspelling** van de verkiezingsuitslag.`
: md``
Aantal zetels voor deze coalitie per simulatie
seatCounts = Array.from(
d3.rollup(
coalitionSeats,
v => v.length,
d => d
),
([seats, count]) => ({
seats,
count,
percentage: 100 * count / coalitionSeats.length
})
).sort((a, b) => a.seats - b.seats)
// Barplot: percentage simulaties per exact zetelaantal
selectedParties.length > 0 && seatCounts.length > 0
? Plot.plot({
marks: [
Plot.barY(seatCounts, {
x: "seats",
y: "percentage",
title: d => `${d.seats} zetels: ${d.percentage.toFixed(1)}% van de simulaties`
})
],
x: {
label: "Aantal zetels voor coalitie",
ticks: seatCounts.length,
tickFormat: d => Number.isInteger(d) ? d : ""
},
y: {label: "Percentage simulaties"},
height: 200
})
: md`**Selecteer ten minste één partij om de grafiek te tonen.**`
percentage76plus = coalitionSeats.length > 0
? 100 * coalitionSeats.filter(d => d >= 76).length / coalitionSeats.length
: 0
// Zoek de modale categorie (de waarde met de hoogste count)
modaal = seatCounts.length > 0
? seatCounts.reduce((a, b) => (a.count > b.count ? a : b))
: {seats: null, percentage: null}
// Inline input for threshold
viewof seatThreshold = Inputs.range([0, 150], {step: 1, value: 76, label: html`<span style = "width:60px">Gewenst zetelaantal</span>`})
percentageThresholdPlus = coalitionSeats.length > 0
? 100 * coalitionSeats.filter(d => d >= seatThreshold).length / coalitionSeats.length
: 0
md`Percentage van simulaties met ${seatThreshold} of meer zetels: **${percentageThresholdPlus.toFixed(1).toLocaleString("nl")}%**`
Elke Peilingwijzer bestaat uit een grote hoeveelheid simulaties. De steun voor een partij en dus ook voor een coalitie kan per simulatie iets verschillen.
In bovenstaande grafiek is te zien hoeveel zetels de coalitie haalt in alle simulaties. Zo haalt bovenstaande coalitie zetels in % van de simulaties.
Op basis daaran kun je ook uitrekenen in welk percentage van de gevallen de coalitie een bepaald minimumaantal zetels haalt (bijvoorbeeld 76). Let op: dit geldt voor de huidige situatie en is geen voorspelling van de verkiezingsuitslag.