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

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