Published
Edited
Dec 15, 2020
1 fork
Insert cell
Insert cell
data_pd=d3.csvParse(await FileAttachment("unpd_enrol_set2.csv").text(), d3.autoType)
Insert cell
pd_c=Array.from(new Set(data_pd.map(d=>d.Country)))
Insert cell
eu_c=Array.from(new Set(data_eu.map(d=>d.Country)))
Insert cell
pd_data=data_small.filter(d=>(pd_c.includes(d.key)))
Insert cell
two=pd_data.map(d=>d.key)
Insert cell
only_pd={
let result=[]
for (const element of pd_c){
if(two.includes(element)){}
else{result.push(element)}
}
return result.join('; ')
}
Insert cell
data_eu=d3.csvParse(await FileAttachment("stack_eu.csv").text(), d3.autoType)
Insert cell
chart = (country) => {
const data =data_single(country)
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, 400])
.style("overflow", "visible");
const aboveUid = DOM.uid("above");
const belowUid = DOM.uid("below");
const x = d3.scaleLinear()
.domain(d3.extent(data[1][1].map(d=>d.age)))
.range([margin.left, width - margin.right])

const y = d3.scaleLinear()
.domain([0, d3.max(data[2].map(d=>d.popu))]).nice()
.range([400 - margin.bottom, margin.top])
const colorscale=d3.scaleOrdinal()
.domain(['unsd_popu','unpd_popu','enrl_popu'])
.range(['green','red','blue'])
const xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0))
const yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.select(".tick:last-of-type text").clone()
.attr("x", 3)
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.text('Population'))
const line = d3.line()
.defined(d => d.popu!=null)
.x(d=>x(d.age))
.y(d =>y(d.popu))
const area = d3.area()
// .defined(d => {return d.percent<0})
.x(d=>x(d.age))
.y0(0)
.y1(d => y(d.popu))

svg.append("g")
.call(xAxis);

svg.append("g")
.call(yAxis);
svg.append("g").attr('class','area1')
svg.append("g").attr('class','area2')

svg.append("g").attr('class','clip1')
svg.append("g").attr('class','clip2')

svg.select('.clip1').selectAll('clipPath')
.data([data[0][0]])
.enter()
.append("clipPath")
.attr("id", 'clip1')
.append("path")
.attr("d", d3.area()
.x(d => x(d.age))
.y0(0)
.y1(d => y(d.popu)));
svg.select('.clip2').selectAll('clipPath')
.data([data[0][1]])
.enter()
.append("clipPath")
.attr("id", 'clip2')
.append("path")
.attr("d", d3.area()
.x(d => x(d.age))
.y0(0)
.y1(d => y(d.popu)));
svg.select('.area1')
.attr("clip-path", "url(#clip1)")
.selectAll("path")
.data([data[0][2]])
.join("path")
.attr("fill", d=>{console.log(d);
{return 'green'}})
.style('opacity',0.2)
.attr("d", d3.area()
.x(d => x(d.age))
.y0(400)
.y1(d => y(d.popu)))
svg.select('.area2')
.attr("clip-path", "url(#clip2)")
.selectAll("path")
.data([data[0][2]])
.join("path")
.attr("fill", d=>{console.log(d);
{return 'red'}})
.style('opacity',0.2)
.attr("d", d3.area()
.x(d => x(d.age))
.y0(400)
.y1(d => y(d.popu)))
svg.append("g").attr('class','light')
svg.select('.light')
.selectAll("path")
.data(data[1])
.join("path")
.attr("d", d => line(d))
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
svg.append("g").attr('class','solid')
svg.select('.solid')
.selectAll("path")
.data(data[0])
.join("path")
.attr("d", d => line(d))
.attr("fill", "none")
.attr("stroke", d=> {return colorscale(d[0].source)})
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")


// svg.call(hover, path);

return svg.node();
}
Insert cell
chart('Guinea-Bissau')
Insert cell
data_single('Guinea-Bissau')[0][2]
Insert cell
data=d3.csvParse(await FileAttachment("stack_new.csv").text(), d3.autoType)
Insert cell
Insert cell
data_single('Guinea')
Insert cell
function hover(svg, path) {
if ("ontouchstart" in document) svg
.style("-webkit-tap-highlight-color", "transparent")
.on("touchmove", moved)
.on("touchstart", entered)
.on("touchend", left)
else svg
.on("mousemove", moved)
.on("mouseenter", entered)
.on("mouseleave", left);

const dot = svg.append("g")
.attr("display", "none");

dot.append("circle")
.attr("r", 2.5);

dot.append("text")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.attr("y", -8);

function moved(event) {
event.preventDefault();
const pointer = d3.pointer(event, this);
const xm = x.invert(pointer[0]);
const ym = y.invert(pointer[1]);
const i = d3.bisectCenter(data.dates, xm);
const s = d3.least(data.series, d => Math.abs(d.values[i] - ym));
path.attr("stroke", d => d === s ? null : "#ddd").filter(d => d === s).raise();
dot.attr("transform", `translate(${x(data.dates[i])},${y(s.values[i])})`);
dot.select("text").text(s.name);
}

function entered() {
path.style("mix-blend-mode", null).attr("stroke", "#ddd");
dot.attr("display", null);
}

function left() {
path.style("mix-blend-mode", "multiply").attr("stroke", null);
dot.attr("display", "none");
}
}
Insert cell
height = 5400

Insert cell
margin = ({top: 20, right: 20, bottom: 30, left: 30})
Insert cell
d3 = require("d3@^6.1")
Insert cell
import { vl } from "@vega/vega-lite-api"
Insert cell
chart_heat = (country)=> {

const margin2 = ({ top: 40, right: 50, bottom: 70, left: 50 })
const data=data_single(country)
const ages=data[2].filter(d=>d.source=='unpd_popu').map(d=>d.age)
const w = width - margin2.left - margin2.right
const gridSize = Math.floor(w /(ages.length+1) )
const h = gridSize * 2 + margin2.top + margin2.bottom

const extent=d3.extent(data[2].map(d => d.percent))
const color = d3.scaleDiverging()
.domain([extent[0], 0, extent[1]])
.range([0,0.5,1])
// const svg = d3.select(DOM.svg(width, h))
// .classed("crashes-heatmap", true)
const svg = d3.select('#heatmap').append('svg')
.attr('width', width)
.attr('height', h)
.classed("crashes-heatmap", true)
const defs = svg.append("defs");
const linearGradient = defs.append("linearGradient")
.attr("id", "linear-gradient");
linearGradient.selectAll("stop")
.data(color.ticks().map((t, i, n) => ({ offset: `${100*i/n.length}%`, color: color(t) })))
.enter().append("stop")
.attr("offset", d => d.offset)
.attr("stop-color", d => d.color);
const g = svg.append("g")
.attr("transform", `translate(${margin2.left}, ${margin2.top})`)
g.selectAll("rect")
.data(data[2].filter(d=>d.source!='enrl_popu'))
.enter().append("rect")
.attr("x", (d,i) => (d.age-d.start) * gridSize)
.attr("y", d => (d.source=='unpd_popu'?0:1) * gridSize)
.attr("width", gridSize)
.attr("height", gridSize)
.attr("fill", d => d3.interpolateRdBu(color(d.percent)))
.attr("stroke", "#e2e2e2")
// .append("title")
// .text(d => d.percent)
g.selectAll(".source")
.data(['unsd','unpd'])
.enter().append("text")
.text(d => d)
.classed("source", true)
.attr("x", 0)
.attr("y", (d, i) => i * gridSize)
.attr("text-anchor", "end")
.attr("transform", "translate(-6," + gridSize / 1.5 + ")")
g.selectAll(".age")
.data(ages)
.enter().append("text")
.text(d => d)
.classed("age", true)
.attr("x", (d, i) => i * gridSize)
.attr("y", 0)
.attr("text-anchor", "middle")
.attr("transform", "translate(" + gridSize / 2 + ", -6)");
g.selectAll(".label")
.data(data[2].filter(d=>d.source!='enrl_popu'))
.enter().append("text")
.text(d => d.percent.toFixed(0))
.classed("label", true)
.attr("x", d => (d.age-d.start+0.5) * gridSize)
.attr("y", d =>(d.source=='unpd_popu'?0:1) *gridSize+1/2*gridSize)
.attr("text-anchor", "middle")
.attr('fill',(d)=>{
const scale=color(d.percent)
if ((scale>0.75)|(scale<0.25)){return 'white'}
else{return 'black'}}
)
// .attr("transform", "translate(" + gridSize / 2 + ", -6)");
// g.append("g")
// .call(legend)
// return svg.node();
}
Insert cell
draw = (country)=>{
return
chart(country)
chart_heat(country)
}

Insert cell
chart_heat('Malawi')
Insert cell
d3.group(data, d => d.Country, d=>d.source)
Insert cell
data_small=Array.from(d3.group(data, d => d.Country), ([key, value]) => ({key, value}))
Insert cell
data_small.map((d,i)=>
{d.data=data_single(d.key),
d.age_range=Array.from(new Set(d.value.map(o=>o.age))),
d.popu_range=d3.extent(d.value.map(o=>o.popu))
d['unsd_year']=d.value[0]['unsd_year'],
d['enrl_year']=d.value[0]['enrol_year'],
d['row']=parseInt(i/3),
d['column']=i%3}
)
Insert cell
data_small
Insert cell
chart_small = (data_s) => {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("overflow", "visible");
const g = svg
.attr('preserveAspectRatio', 'xMidYMid')
.append('g')
.attr('transform', `translate(${margin.left + margin.right}, ${margin.top - margin.bottom})`)
const countries = g.selectAll('.countries')
.data(data_s)
.enter().append('g')
.attr('class', 'country-groups');
const x_column = ({column}) => column * 300
const y_column = ({row}) => row * 330
// const xAxis = d3.axisBottom(x).ticks(width / 80).tickSizeOuter(0)

const colorscale=d3.scaleOrdinal()
.domain(['enrl_popu','unsd_popu','unpd_popu'])
.range(d3.schemeCategory10)
const canvas=countries.append('g').attr('class','canvas').attr('transform', d => `translate(${x_column(d)}, ${y_column(d)})`)
const header=canvas.append('g').attr('class','header').attr('transform','translate(100,0)')
const lines=canvas.append('g').attr('class','linechart').attr('transform','translate(0,115)')
header.append('g').attr('class','key label').attr('text-anchor', 'middle')
.append('text').attr('x',0).attr('y',30)
.text(d=>d.key).attr('font-size',12).attr('font-weight','bold')

header.append('g').attr('class','year label').append('text')
.attr('x',0).attr('y',42).attr('text-anchor', 'middle')
.text(d=>('Enrl&UNPD data: '+d['enrl_year']+' | UNSD data: ' +d['unsd_year'])).attr('font-size',8)

lines.append('g').attr('class','xAxis')
.each(function(d, i){
const mapX = d3.scalePoint() .domain(d.age_range).range([0,200])
const x_axis = d3.axisBottom(mapX).ticks(width / 80).tickSizeOuter(0)
d3.select(this).attr("transform", `translate(0,200)`)
.call(x_axis)
})
lines.append('g').attr('class','yAxis')
.each(function(d, i){
const mapY = d3.scaleLinear()
.domain(d.popu_range).nice()
.range([200,0])
const y_axis = d3.axisLeft(mapY).tickFormat(d3.format(".3s")).tickSizeInner(2)
d3.select(this).call(y_axis)
})
// lines.append('g').attr('class','area')
lines.append("g").attr('class','area1')
lines.append("g").attr('class','area2')

lines.append("g").attr('class','clip1')
lines.append("g").attr('class','clip2')

lines.select('.clip1').selectAll('clipPath')
.data(d=>[d.data[0][0]])
.join("clipPath")
.attr("id", d=>d[0].ISO)
.append("path")
.attr("d", d=>{
const country=d[0].Country
const mapX = d3.scalePoint().domain(d3.range(d[0].start,d[0].end+1))
.range([0,200])
const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
const mapY = d3.scaleLinear()
.domain(popu_range).nice()
.range([200,0])
const area = d3.area()
.x((o) => mapX(o.age))
.y0(0)
.y1(o => mapY(o.popu))
return area(d)
});
lines.select('.clip2').selectAll('clipPath')
.data(d=>[d.data[0][1]])
.join("clipPath")
.attr("id", d=>(d[0].ISO+'2'))
.append("path")
.attr("d", d=>{
const country=d[0].Country
const mapX = d3.scalePoint().domain(d3.range(d[0].start,d[0].end+1))
.range([0,200])
const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
const mapY = d3.scaleLinear()
.domain(popu_range).nice()
.range([200,0])
const area = d3.area()
.x((o,i) => mapX(o.age))
.y0(0)
.y1(o => mapY(o.popu))
return area(d)
});
lines.select('.area1')
.selectAll("path")
.data(d=>[d.data[0][1]])
// .attr("clip-path", 'url(#Malawi)')
.join("path")
.attr("fill", d=> {return colorscale('unsd_popu')})
.attr("clip-path", d=>"url(#"+d[0].ISO+")")
.style('opacity',0.2)
.attr("d", d=>{
// console.log(d[0],d,'1')
const country=d[0].Country
const mapX = d3.scalePoint().domain(d3.range(d[0].start,d[0].end+1))
.range([0,200])
const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
const mapY = d3.scaleLinear()
.domain(popu_range).nice()
.range([200,0])
const area = d3.area()
.x((o,i) => mapX(o.age))
.y0(200)
.y1(o => mapY(o.enrl_base))
return area(d)
});
lines.select('.area2')
.selectAll("path")
.data(d=>[d.data[0][1]])
.join("path")
.attr("fill", d=> {return colorscale('unpd_popu')})
.attr("clip-path", d=>"url(#"+d[0].ISO+"2)")
.style('opacity',0.2)
.attr("d", d=>{
// console.log(d[0],d,'1')
const country=d[0].Country
const mapX = d3.scalePoint().domain(d3.range(d[0].start,d[0].end+1))
.range([0,200])
const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
const mapY = d3.scaleLinear()
.domain(popu_range).nice()
.range([200,0])
const area = d3.area()
.x((o,i) => mapX(o.age))
.y0(200)
.y1(o => mapY(o.enrl_base))
return area(d)
});
// lines.select('.area')
// .selectAll("path")
// .data(d=>d.data[0])
// .join("path")
// .attr("fill", d=> {return colorscale(d[0].source)})
// .attr('fill-opacity',0.3)
// .attr("d", d=>{
// const country=d[0].Country
// const mapX = d3.scaleLinear().domain([d[0].start,d[0].end])
// .range([0,200])
// const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
// const mapY = d3.scaleLinear()
// .domain(popu_range).nice()
// .range([200,0])
// const area = d3.area()
// .defined(o => {return (o.enrl_base-o.popu)>0})
// .x((o,i) => mapX(o.age))
// .y0(o=> mapY(o.enrl_base))
// .y1(o => mapY(o.popu))
// return area(d)
// });
lines.append("g").attr('class','light')
canvas.select('.light')
.selectAll("path")
.data(d=>d.data[1])
.join("path")
.attr("d", (d,i) => {
const country=d[0].Country
const mapX = d3.scalePoint().domain(d3.range(d[0].start,d[0].end+1))
.range([0,200])
const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
const mapY = d3.scaleLinear()
.domain(popu_range).nice()
.range([200,0])

const line = d3.line()
.defined(o => o.popu!=null)
.x(o=>mapX(o.age))
.y(o=>mapY(o.popu))
return line(d)
})
.attr("fill", "none")
.attr("stroke", "#ccc")
.attr("stroke-width", 1)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
lines.append("g").attr('class','solid')
canvas.select('.solid')
.selectAll("path")
.data(d=>d.data[0])
.join("path")
.attr("d", (d,i) => {
const country=d[0].Country
const mapX = d3.scalePoint().domain(d3.range(d[0].start,d[0].end+1))
.range([0,200])
const popu_range = data_small.filter(d=>d.key==country)[0].popu_range
const mapY = d3.scaleLinear()
.domain(popu_range).nice()
.range([200,0])

const line = d3.line()
.defined(o => o.popu!=null)
.x(o=>mapX(o.age))
.y(o=>mapY(o.popu))
// console.log(d,line(d))
return line(d)
})
.attr("fill", "none")
.attr("stroke", d=> {return colorscale(d[0].source)})
.attr("stroke-width", 1.5)
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")

const margin2 = ({ top: 40, right: 50, bottom: 70, left: 50 })
// const data=data_single(country)
// const ages=data[2].filter(d=>d.source=='unpd_popu').map(d=>d.age)
const w = 150
const gridSize = 18
// Math.floor(w /(ages.length+1) )
const h = gridSize * 2 + margin2.top + margin2.bottom

const heatmap=canvas.append('g').attr('class','heatmap').attr('transform','translate(0,50)')
heatmap.selectAll('rect')
.data(d=> {
return d.value.filter(o=>o.source!='enrl_popu')})
.join("rect")
.attr("x", (d) => {
// console.log(d,d.age,d.source)
return (d.age-d.start) * 200/(d.end-d.start)})
.attr("y", d => (d.source=='unsd_popu'?0:1)* 200/(d.end-d.start))
.attr("width", d=> 200/(d.end-d.start))
.attr("height",d=> 200/(d.end-d.start))
// .style("text-anchor", "middle")
.attr('transform',d=>'translate('+(-100/(d.end-d.start))+',0)')
.attr("fill", d => {
const country= d.Country
const percents = data_small.filter(d=>d.key==country)[0].value.map(o=>o.percent).filter(d=>!isNaN(d))
const extent = d3.extent(percents)
let color= function() {}
if (extent[0]<0){
color = d3.scaleDiverging()
.domain([extent[0], 0, extent[1]])
.range([0,0.5,1])
}
else{
color = d3.scaleLinear()
.domain([0, extent[1]])
.range([0.5,1])
}
if (!isNaN(d.percent)){
return d3.interpolateRdYlBu(color(d.percent))
}else{
return 'white'
}
// console.log(color(d.percent))
// return d3.interpolateRdBu(color(d.percent))
})
.attr("stroke", "#e2e2e2")
// .append("title")
// .text(d => d.percent)
heatmap.selectAll(".source")
.data(['unsd','unpd','age'])
.join("text")
.text(d => d)
.classed("source", true)
.attr("x", 0)
.attr("y", (d, i) => i * gridSize)
.attr("text-anchor", "end")
.attr("transform", "translate(-10," + gridSize / 1.5 + ")")
.attr('font-size',9)
// canvas.append('g')
// .attr('class','area')
// canvas.select('.area')
// .selectAll("path")
// .data(d=>d.data[0])
// .join("path")
heatmap.selectAll(".age")
.data(d=>d.value.filter(o=>o.source=='unpd_popu'))
.join("text")
.text(d => d.age)
.classed("age", true)
.attr("x", d=>(d.age-d.start) * 200/(d.end-d.start))
.attr("y", d=>200/(d.end-d.start)*2.6)
.attr("text-anchor", "middle")
.attr('font-size',9)
heatmap.append('g').attr('class','label_heat')
heatmap.select(".label_heat").selectAll('text')
.data(d=>d.value.filter(d=>d.source!='enrl_popu'))
.join("text")
.text(d => {
if (!isNaN(d.percent)){
return (d.percent).toFixed(0)}else{return 'm'}})
.classed("label", true)
.attr("x", d => (d.age-d.start+0.5) * 200/(d.end-d.start))
.attr("y", d =>(d.source=='unsd_popu'?0:1) *200/(d.end-d.start))
.attr('transform',d=>'translate('+(-100/(d.end-d.start))+','+100/(d.end-d.start)+')')
.attr("text-anchor", "middle")
.attr('fill',(d)=>{
const country= d.Country
const percents = data_small.filter(d=>d.key==country)[0].value.map(o=>o.percent).filter(d=>!isNaN(d))
const extent = d3.extent(percents)
let color= function() {}
if (extent[0]<0){
color = d3.scaleDiverging()
.domain([extent[0], 0, extent[1]])
.range([0,0.5,1])
}
else{
color = d3.scaleLinear()
.domain([0, extent[1]])
.range([0.5,1])
}
const scale=color(d.percent)
if ((scale>0.75)|(scale<0.25)){return 'white'}
else{return 'black'}}).attr('font-size',8)
// svg.call(hover, path);

return svg.node();
}
Insert cell
scaley=d3.scaleOrdinal().domain(['unpd_popu','unsd_popu','enrl_popu']).range([0,1,2])
Insert cell
scaley('unpd_popu')
Insert cell
data_two=data_small.filter(d=>!(pd_c.includes(d.key)|eu_c.includes(d.key)))
Insert cell
Insert cell
data_Set2= (datas,r1,r2)=>{
const data=datas.slice(r1,r2)
data.map((d,i)=>{d['row']=parseInt(i/2),
d['column']=i%2})
return data
}
Insert cell
chart_small(Array.from(data_Set2(pd_data,30,62)))
Insert cell
// chart_small(Array.from(data_Set2(pd_data,40,78)))
Insert cell
data_Set2(pd_data,0,80).map(d=>d.key)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more