Apportionment of seats in the European Parliament

Evolution of EU countries relative apportionment of seats at the European Parliament, colored by year of accession. Although the areaY mark implicitly stacks values, we chose to make the stack transform explicit, in order to minimize the difference between the two marks (areaY and textY). Original D3 visualization created by Luc Guillemot. Data: Wikipedia.

Plot.plot({
  width,
  height: 600,
  y: {percent: true},
  color: {type: "ordinal", scheme: "set2", legend: true},
  marks: [
    Plot.areaY(
      members,
      Plot.stackY({
        x: "date",
        y: "seats",
        z: "state",
        fill: "joined",
        order: "joined",
        title: (d) => `${d.state}\nJoined ${d.joined}`,
        stroke: "#fff",
        strokeOpacity: 0.3,
        offset: "expand",
        curve: "monotone-x"
      })
    ),
    Plot.text(
      members,
      Plot.selectLast(
        Plot.stackY({
          filter: (d) => d.date < Date.UTC(2020, 0, 1),
          x: "date",
          y: "seats",
          z: "state",
          fill: "joined",
          order: "joined",
          text: "state",
          textAnchor: "start",
          dx: -17,
          fill: "#000",
          offset: "expand"
        })
      )
    )
  ]
})

The original dataset (eu) is a wide array, with one member state per row, and as many columns as there are dates. We make it tidy with arquero’s fold operator, so that each row of the members array is an observation: state, date, and number of seats. The joined column is used to color the state’s ribbon.

const eu = await FileAttachment("data/european-parliament.csv").csv().then(display);
const dates = eu.columns.slice(4);
const dateParse = d3.utcParse("%b %Y");
const members = Array.from(
  aq.from(eu)
    .fold(dates)
    .rename({key: "accession", value: "seats", Joined: "joined", State: "state"}),
  (d) => ({state: d.state, seats: d.seats, joined: d.joined, date: dateParse(d.accession)})
);
✎ Suggest changes to this page