Public
Edited
Dec 26, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
downloadcountry = DOM.download(serialize(filtered_populism), null, "Download Country Data")
Insert cell
downloadfull = DOM.download(serialize(ojsdata), null, "Download Complete Data")
Insert cell
horizontal = "<p style='text-align: justify';> ― The horizontal line represents the quality of democracy with respect to your chosen democracy definition. The data is taken from the <a href='https://www.v-dem.net/data/the-v-dem-dataset/'>V-Dem Dataset</a>. Learn more about the measurement in their <a href='https://www.v-dem.net/documents/24/codebook_v13.pdf'>codebook </a>.</p>"
Insert cell
vertical = "<p style='text-align: justify';> | Verticel Lines represent constitutional changes according to the <a href='https://comparativeconstitutionsproject.org/'>Comparative Constitutions Project</a>. If the line is ┋ dashed the constitution was amended. If the line is | solid a new constitution was adopted.</p>"
Insert cell
Insert cell
ojsdata = d3.csv("https://raw.githubusercontent.com/jasminskoenig/blog_loop/main/data/ccpc_vdem_populism.csv")
Insert cell
Insert cell
// define parser
parseryear = d3.timeParse("%Y-%m-%d")
Insert cell
// Parse Data
parsedDate = ojsdata.map(d => ({
...d,
new_date: parseryear(d.date),
new_end: parseryear(d.year_end),
new_start: parseryear(d.year_start)
}));
Insert cell

parsedData = parsedDate.map(d => ({
...d,
manipulated_date: d.date == "2021-01-01" ? parseryear("2020-06-01") : d.new_date
}));
Insert cell
Insert cell
Insert cell
geo = {
if(continent === "Latin America"){
return(["17","18"])
}else{
return(["1","2","3","4"])
}
}
Insert cell
// Filter data to country
filteredcon = parsedData.filter(d => geo.includes(d.e_regiongeo))
Insert cell
// Filter data to country
filtered = filteredcon.filter(function(filteredcon) {
return filteredcon.country == source;
})
Insert cell
Insert cell
// Options for Democracy Score
democracyoptions = ({
"Liberal Democracy": "v2x_libdem",
"Participation": "v2x_cspart",
"Egalitarian Democracy": "v2x_egaldem",
"Electoral Democracy": "v2x_polyarchy",
"Deliberative Democracy": "v2x_delibdem"
})
Insert cell
// value for y-axis
democracydef = democracyoptions[Democracy]
Insert cell
Insert cell
// import data on which dataset there is per country
countriesds = d3.csv("https://raw.githubusercontent.com/jasminskoenig/blog_loop/main/data/overview.csv")
Insert cell
// Change the countriesds to an object with an array of datasets per country
countrydatasets = countriesds.reduce((acc, obj) => {
const { country, ...rest } = obj;
const arr = Object.entries(rest)
.filter(([key]) => key.includes("dataset"))
.map(([, value]) => value);
acc[country] = arr;
return acc;
}, {});

Insert cell
// save an array of datasets for the chosen country
selectedCountryData = countrydatasets[source] ?? {};
Insert cell
Insert cell
Insert cell
// Define options of dataset and populism definition
dsoptions = ({
"V-Party": ["Government Weighted Populism Score", "Head of State Populism Score", "Government Mean Populism Score"],
"PopuList": ["Any Party in Government Populist", "Senior Party in Government Populist"],
"Ruth-Lovell & Grahn": ["Populist Prime Minister"]
})
Insert cell
// Filter the variable options (dsoptions) to the Datasets that include the respective country (source), for this I use the selectedCountryData array
filteredOptions = Object.entries(dsoptions)
.filter(([key, value]) => selectedCountryData.includes(key))
.reduce((obj, [key, value]) => {
obj[key] = value;
return obj;
}, {});
Insert cell
Insert cell
//assign value of dataset to populism score
filtered_populism = filtered.map(d => {
let populism_score;
if (Vars === "Populist Prime Minister") {
populism_score = String(d.ruth_populism);
} else if (Vars === "Government Mean Populism Score"){
populism_score = parseFloat(d.gov_popul_mean);
} else if (Vars === "Government Weighted Populism Score"){
populism_score = parseFloat(d.gov_popul_weighted);
} else if (Vars === "Head of State Populism Score"){
populism_score = parseFloat(d.gov_popul_prime);
} else if (Vars === "Any Party in Government Populist"){
populism_score = String(d.rooduijn_government);
} else if (Vars === "Senior Party in Government Populist"){
populism_score = String(d.rooduijn_government_senior);
} else {
populism_score = d.gov_popul_weighted;
}
return {
...d,
populism_score
}
})
Insert cell
Insert cell
// filter constitutional change years for yrules (can't get strokeDasharray to be based on evnt)
constchange = filtered_populism.filter(function(filtered_populism) {
return filtered_populism.evnttype == 1;
})
Insert cell
// new constitutions for solid lines
newconst = filtered_populism.filter(function(filtered_populism) {
return filtered_populism.evnttype == 3;
})
Insert cell
Insert cell
// Replace NAs as null values
filtered_populism_null = filtered_populism.map(d => {
const updatedData = { ...d };
for (const key in updatedData) {
if (updatedData[key] === "NA") {
updatedData[key] = null;
}
}
return updatedData;
});
Insert cell
Insert cell
Insert cell
Insert cell
vpartycodebook = "<a href='https://www.v-dem.net/documents/6/vparty_codebook_v2.pdf'>codebook</a>"
Insert cell
ruthlovell = "<a href='https://ejpr.onlinelibrary.wiley.com/doi/full/10.1111/1475-6765.12564 '>Ruth-Lovell & Grahn</a>"
Insert cell
huberschimp = "<a href='https://journals.sagepub.com/doi/10.1111/1467-9248.12219'>Huber & Schimpf 2016</a>"
Insert cell
huberschimpf = "<a href='https://www.cogitatiopress.com/politicsandgovernance/article/view/919'>Huber & Schimpf 2017</a>"
Insert cell
ruth = "<a href='https://journals.sagepub.com/doi/abs/10.1177/0032321717723511'>Ruth 2018</a>"
Insert cell
definition =
(Vars === "Populist Prime Minister") ? "The background color represents the government whether the government was <font color='#D1A09C'>populist</font> or <font color='#EDD5BB'>not populist</font> according to Ruth-Lovell & Grahn. The Dataset by " + ruthlovell + " is based on articles by " + huberschimp + " & " + huberschimpf + " and " + ruth :
(Vars === "Government Mean Populism Score") ? "The color represents how populist the country's government at the time was according to the " + vpartylink + " populism score. We calculated the mean populism score per government without considering the strength per party. The score is continuuous and equals 0 if a government was <font color='#EDD5BB'>not populist</font> at all, and 1 if the party was <font color='#D1A09C'>very populist</font>. The " + vpartylink + " dataset's populism score is based on their anti-elite and people-centred items. Find out more in their " + vpartycodebook + "!" :
(Vars === "Government Weighted Populism Score") ? "The color represents how populist the country's government at the time was according to the " + vpartylink + " populism score. We calculated the weighted populism score per government by weighing the populism score per party with their strength in government (share of seats of overall government seats in parliament). The score is continuuous and equals 0 if a government was <font color='#EDD5BB'>not populist</font> at all, and 1 if the government was <font color='#D1A09C'>very populist</font>. The " + vpartylink + " dataset's populism score is based on their anti-elite and people-centred items. Find out more in their " + vpartycodebook + "!":
(Vars === "Head of State Populism Score") ? "The color represents how populist the country's head of state's party was at the time was according to the " + vpartylink + " populism score. The score is continuuous and equals 0 if a government was <font color='#EDD5BB'>not populist</font> at all, and 1 if the party was <font color='#D1A09C'>very populist</font>. The " + vpartylink + " dataset'spopulism score is based on their anti-elite and people-centred items. Find out more in their " + vpartycodebook + "!":
(Vars === "Any Party in Government Populist") ? "The color represents whether the government at the time was <font color='#D1A09C'>populist</font> or <font color='#EDD5BB'>not populist</font> according to the " + populistlink + ". In this definition a government is considered populist as soon as one party is coded populist in the PopuList dataset. The " + populistlink + " is based on expert surveys.":
(Vars === "Government Seniority Weighted Populism Score") ? "The color represents whether the government at the time was <font color='#D1A09C'>populist</font> or <font color='#EDD5BB'>not populist</font> according to the " + populistlink + ". In this definition, a government is considered populist if the head of state's party is coded populist in the PopuList dataset. The " + populistlink + "is based on expert surveys." :
"More explanation on the dataset here.";
Insert cell
Insert cell
Insert cell
Insert cell
latestdate = d3.max(filtered_populism.map(d=>d.new_date));
Insert cell
earliesttdate = d3.min(filtered_populism.map(d=>d.new_date));
Insert cell
endplot = parseryear("2020-07-01")
Insert cell
Insert cell
myContinuousColors = ['#FAF2E2', '#F1DDC1', '#DEB9A7', "#BB7688"]
Insert cell
myBinaryColors = ["#F4E4CC", "#D1A09C", "#dadada"]
Insert cell
myGrey = '#dadada'
Insert cell
// Define dynamic rules for continuuous and binary data
colors = (Dataset==="V-Party") ?
{type: "sqrt",
interpolate: "lab",
legend: false,
range: myContinuousColors,
domain: [0, 0.3, 0.6, 1]
} :
(Dataset!="V-Party") ?
{legend: false,
range: myBinaryColors,
domain : ["Non-Populist", "Populist", "NA"]} :
// got no idea what the rest is for
{legend:false,
style:{fontSize: "14px", fontFamily:'Atkinson Hyperlegible', paddingBottom:'40px', paddingTop:'1px', paddingLeft:'1px'}};
Insert cell
Insert cell
Insert cell
Insert cell
paper = "<p style='text-align: justify';> All data, code and the <a href='https://www.jasminskoenig.com/uploads/working-paper.pdf'>paper</a> can be found <a href='https://github.com/jasminskoenig/constitutional-regression'>here</a>.</p>"
Insert cell
// Copyright 2021, Observable Inc.
// Released under the ISC license
// https://observablehq.com/@d3/color-legend
function Legend(color, {
title,
tickSize = 8,
width = 320,
height = 100 + tickSize,
width_full = 650,
height_full = 100 + tickSize,
marginTopheader = 25,
marginTop = 70,
marginRight = 20,
marginBottom = 16 + tickSize,
marginRect = 20,
marginLeft = 25 + marginRect,
ticks = width / 64,
tickFormat,
tickValues,
tickLabels
} = {}) {

function ramp(color, n = 256) {
const canvas = document.createElement("canvas");
canvas.width = n;
canvas.height = 1;
const context = canvas.getContext("2d");
for (let i = 0; i < n; ++i) {
context.fillStyle = color(i / (n - 1));
context.fillRect(i, 0, 1, 1);
}
return canvas;
}

const svg = d3.create("svg")
.attr("width", width_full)
.attr("height", height_full)
.attr("viewBox", [0, 0, width_full, height_full])
.style("overflow", "visible")
.style("display", "block");

let tickAdjust = g => g.selectAll(".tick line").attr("y1", marginTop + marginBottom - height);
let x;
// Continuous
if (color.interpolate) {
const n = Math.min(color.domain().length, color.range().length);

x = color.copy().rangeRound(d3.quantize(d3.interpolate(marginLeft, width - marginRight), n));

svg.append("image")
.attr("x", marginLeft)
.attr("y", marginTop)
.attr("width", width - marginLeft - marginRight)
.attr("height", height - marginTop - marginBottom)
.attr("preserveAspectRatio", "none")
.attr("xlink:href", ramp(color.copy().domain(d3.quantize(d3.interpolate(0, 1), n))).toDataURL());
}

// Sequential
else if (color.interpolator) {
x = Object.assign(color.copy()
.interpolator(d3.interpolateRound(marginLeft, width - marginRight)),
{range() { return [marginLeft, width - marginRight]; }});

svg.append("image")
.attr("x", marginLeft)
.attr("y", marginTop)
.attr("width", width - marginLeft - marginRight)
.attr("height", height - marginTop - marginBottom)
.attr("preserveAspectRatio", "none")
.attr("xlink:href", ramp(color.interpolator()).toDataURL());

// scaleSequentialQuantile doesn’t implement ticks or tickFormat.
if (!x.ticks) {
if (tickValues === undefined) {
const n = Math.round(ticks + 1);
tickValues = d3.range(n).map(i => d3.quantile(color.domain(), i / (n - 1)));
}
if (typeof tickFormat !== "function") {
tickFormat = d3.format(tickFormat === undefined ? ",f" : tickFormat);
}
}
}

// Threshold
else if (color.invertExtent) {
const thresholds
= color.thresholds ? color.thresholds() // scaleQuantize
: color.quantiles ? color.quantiles() // scaleQuantile
: color.domain(); // scaleThreshold

const thresholdFormat
= tickFormat === undefined ? d => d
: typeof tickFormat === "string" ? d3.format(tickFormat)
: tickFormat;

x = d3.scaleLinear()
.domain([-1, color.range().length - 1])
.rangeRound([marginLeft, width - marginRight]);

svg.append("g")
.selectAll("rect")
.data(color.range())
.join("rect")
.attr("x", (d, i) => x(i - 1))
.attr("y", marginTop)
.attr("width", (d, i) => x(i) - x(i - 1))
.attr("height", height - marginTop - marginBottom)
.attr("fill", d => d);

tickValues = d3.range(thresholds.length);
tickFormat = i => thresholdFormat(thresholds[i], i);
}

// Ordinal
else {
x = d3.scaleBand()
.domain(color.domain())
.rangeRound([marginLeft, width - marginRight]);

svg.append("g")
.selectAll("rect")
.data(color.domain())
.join("rect")
.attr("x", x)
.attr("y", marginTop)
.attr("width", Math.max(0, x.bandwidth() - 1))
.attr("height", height - marginTop - marginBottom)
.attr("fill", color);

tickAdjust = () => {}
}

svg.append("g")
.attr("transform", `translate(0,${height - marginBottom})`)
.call(d3.axisBottom(x)
.ticks(ticks, typeof tickFormat === "string" ? tickFormat : undefined)
.tickFormat(typeof tickFormat === "function" ? tickFormat(function(d,i){ return tickLabels[i] }) : undefined)
.tickSize(tickSize)
.tickValues(tickValues))
//.tickFormat(function(d,i){ return tickLabels[i] })
.call(tickAdjust)
.call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", marginLeft)
.attr("y", marginTop + marginBottom - height - 6)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px")
.attr("class", "title")
.text(title));

// add how to

svg.append("text") // add text for dashed lines
.attr("x", 0)
.attr("y", marginTopheader)
.text("How to read this graph:")
.style("font", "14px 'Atkinson Hyperlegible'")
.style("font-weight", "bold");
// add manual legend for dashed lines and constitutions

svg.append("text") // add label for cc
.attr("x", width + 88)
.attr("y", marginTop - 6)
.text("Constitutional Change")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px");
svg.append("line") // dashed line for legend
.attr("x1", width + 95)
.attr("x2", width + 95)
.attr("y1", marginTop)
.attr("y2", height - 20)
.style("stroke-width", 2)
.style("stroke-dasharray","5,5") // dashed array for line
.style("stroke", "black");

svg.append("text") // add text for dashed lines
.attr("x", width + 105)
.attr("y", marginTop + 13)
.text("Amendment")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px");


svg.append("line")
.attr("x1", width + 205)
.attr("x2", width + 205)
.attr("y1", marginTop)
.attr("y2", height - 20)
.style("stroke-width", 2)
.style("stroke", "black");

svg.append("text") // dashed lines are amendments
.attr("x", width + 215)
.attr("y", marginTop + 13)
.text("New")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px");


// add NA

svg.append("rect")
.attr("x", width + 14.5)
.attr("y", marginTop)
.attr('width', height - marginTop - marginBottom)
.attr('height', height - marginTop - marginBottom)
.attr('fill', myGrey);

svg.append("text") // add label NA
.attr("x", width + 15)
.attr("y", height - 6)
.text("NA")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "10px");


return svg.node();
}
Insert cell
// Copyright 2021, Observable Inc.
// Released under the ISC license
// https://observablehq.com/@d3/color-legend
function Binary(color, {
title,
width = 320,
height = 100,
width_full = 650,
height_full = height,
marginTopheader = 25,
marginTop = 70,
marginRight = 0,
marginBottom = 16,
marginRect = 20,
marginLeft = 8
} = {}) {

const svg = d3.create("svg")
.attr("width", width_full)
.attr("height", height_full-10)
.attr("viewBox", [0, 0, width_full, height_full])
.style("overflow", "visible")
.style("display", "block");

let tickAdjust = g => g.selectAll(".tick line").attr("y1", marginTop + marginBottom - height);
let x;
// add how to

svg.append("text") // add text for dashed lines
.attr("x", -33)
.attr("y", marginTopheader)
.text("How to read this graph:")
.style("font", "16px 'Atkinson Hyperlegible'")
.style("font-weight", "bold");

// add manual legend for binary color

svg.append("text") // add label for cc
.attr("x", marginLeft)
.attr("y", marginTop - 6)
.text("Populism Score")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px");
svg.append("rect") // colors
.attr("x", marginLeft)
.attr("y", marginTop)
.attr('width', height - marginTop - marginBottom)
.attr('height', height - marginTop - marginBottom)
.attr('fill', myBinaryColors[0]);

svg.append("text") // add text
.attr("x", marginLeft + 20)
.attr("y", marginTop + 13)
.text("Non-Populist")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px");

svg.append("rect") // colors
.attr("x", marginLeft + 123)
.attr("y", marginTop)
.attr('width', height - marginTop - marginBottom)
.attr('height', height - marginTop - marginBottom)
.attr('fill', myBinaryColors[1]);

svg.append("text") // add text
.attr("x", marginLeft + 143)
.attr("y", marginTop + 13)
.text("Populist")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "14px");

// add NA

svg.append("rect") // colors
.attr("x", marginLeft + 218)
.attr("y", marginTop)
.attr('width', height - marginTop - marginBottom)
.attr('height', height - marginTop - marginBottom)
.attr('fill', '#dadada');

svg.append("text") // add text
.attr("x", marginLeft + 238)
.attr("y", marginTop + 13)
.text("NA")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "16px");

// add manual legend for dashed lines and constitutions

svg.append("text") // add label for cc
.attr("x", width + 88)
.attr("y", marginTop - 6)
.text("Constitutional Change")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "16px");
svg.append("line") // dashed line for legend
.attr("x1", width + 95)
.attr("x2", width + 95)
.attr("y1", marginTop)
.attr("y2", height - 15)
.style("stroke-width", 3)
.style("stroke-dasharray","5,5") // dashed array for line
.style("stroke", "black");

svg.append("text") // add text for dashed lines
.attr("x", width + 105)
.attr("y", marginTop + 13)
.text("Amendment")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "16px");

svg.append("line")
.attr("x1", width + 205)
.attr("x2", width + 205)
.attr("y1", marginTop)
.attr("y2", height - 15)
.style("stroke-width", 3)
.style("stroke", "black");

svg.append("text") // dashed lines are amendments
.attr("x", width + 215)
.attr("y", marginTop + 13)
.text("New")
.style("font-family", "Atkinson Hyperlegible")
.style("font-size", "16px");


return svg.node();
}
Insert cell
Insert cell
import {serialize} from "@palewire/saving-json"
Insert cell
DOM.download(serialize(filtered_populism), null, "Download JSON")
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more