Published
Edited
Jun 28, 2020
Insert cell
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height]);

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

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

svg.append("path")
.attr("fill", "none")
.attr("stroke", "#a4a4a4")
.attr("stroke-miterlimit", 1)
.attr("stroke-width", 3)
.attr("stroke-dasharray", ("3, 3"))
.attr("d", lineNew(data));
svg.append("path")
.attr("fill", "none")
.attr("stroke", "currentColor")
.attr("stroke-miterlimit", 1)
.attr("stroke-width", 3)
.attr("d", avgLine(data));
const legendData = svg
.append("text")
.style("font", "22px sans-serif")
.attr("x", 10)
.attr("y", 100);
const legendR = svg
.append("text")
.style("font", "22px sans-serif")
.attr("x", 10)
.attr("y", 125);
const marker = svg
.append("circle")
.attr("r", 3)
.attr("cx", -100)
.attr("fill", "black")
.attr("fill-opacity", 0)
const formatTime = d3.utcFormat("%d/%m/%Y");
if (!mutable lookup) mutable lookup = new Date(data[0].date);

const bar = svg
.append("line") // vertical moving line
.attr("style", "stroke-width:1")
.attr('stroke', 'blue')
// .attr("style", 'gray')
.attr("y2", height*0.94)
.attr("x1", xScale(mutable lookup))
.attr("x2", xScale(mutable lookup));

function update(date) {
const i = bisect.right(data, date);
if (i < data.length && i > 0) {
const date1 = data[i].parsedDate;
console.log(i, date1)
legendData.text(`${data[i].date}`);
legendR.text(`Média 7d.: ${data[i].weeklyAvg.toFixed(0)}`);
marker.attr("cx", xScale(date1)).attr("cy", yScaleNew(data[i].ml));
legendData.attr("x", (i > data.length/2)? xScale(date1) - 170 : xScale(date1))
legendR.attr("x", (i > data.length/2)? xScale(date1) - 170 : xScale(date1))
}
mutable lookup = new Date(date);
bar.attr("x1", xScale(mutable lookup)).attr("x2", xScale(mutable lookup));
}
svg.on("mousemove click touchmove", function() {
const m = d3.mouse(this);
update(xScale.invert(m[0]));
});

return svg.node();
}
Insert cell
xScale(date(data[2].date))
Insert cell
mutable lookup = null
Insert cell
bisect = d3.bisector(d => date(d.date))
Insert cell
lineNew = d3.line()
.x(d => xScale(date(d.date)))
.y(d => yScaleNew(d.new))
Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale).ticks(width / 80).tickSizeOuter(0))
Insert cell
avgLine = d3.line()
.x(d => xScale(date(d.date)))
.y(d => yScaleAvg(d.weeklyAvg))
Insert cell
xScale = d3.scaleTime()
.domain(d3.extent(data, d => date(d.date)))
.range([margin.left, width - margin.right])
Insert cell
yScaleNew = d3.scaleLinear()
.domain([0, d3.max(data, d => d.new)])
.range([height - margin.bottom, margin.top])
Insert cell
height = 400
Insert cell
yScaleAvg = d3.scaleLinear()
.domain([0, d3.max(data, d => d.weeklyAvg)])
.range([height - margin.bottom, margin.top])
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScaleNew))
.call(g => g.selectAll(".tick line") // horizontal line
.select(function() { return this.parentNode.appendChild(this.cloneNode()); })
.attr("stroke-opacity", d => d === 1 ? null : 0.1) // if value is 1 opacity is different
.attr("stroke-width", 1.8)
.attr("stroke", 'darkgray')
.attr("x2", width - margin.left - margin.right))
.call(g => g.select(".domain").remove())

Insert cell
margin = ({top: 20, right: 30, bottom: 30, left: 40})
Insert cell
Insert cell
data = {
let addNew = confirmedRaw2.map((d, i) => d.map((dd, ii) => {
dd.new = (i > 0) ? dd.confirmed - confirmedRaw2[i-1][ii].confirmed : 0;
return dd;
}))
// filter by state
addNew = addNew.map(dd => dd.filter(d => d.state === "PR")[0]);
// get average property
addNew = movAvg(addNew, 'new')
addNew = addNew.map(d => {
d.parsedDate = date(d.date)
return d;
})
// last 60 days
addNew = addNew.slice(Math.max(addNew.length - 60, 1), -1)
return addNew;
}
Insert cell
confirmedRaw2 = {
let dd = d3.csvParse(await FileAttachment("latest_cities_shrink.csv").text())
.map((d) => {
d.confirmed = +d.confirmed;
d.deaths = +d.deaths;
return d
});
let data_with_holes = Array.from(d3.group(dd, d => d.date), ([key, value]) => value).sort((a,b) => a.date - b.date).reverse();
let mutableArray = [...data_with_holes].filter(function (el) {
return el != null;
});
for (let i = 1; i < data_with_holes.length; i++) {
for (let j = 0; j < mutableArray[i - 1].length; j++) {
let found = mutableArray[i].find(element => element.state === mutableArray[i - 1][j].state && element.city === mutableArray[i - 1][j].city);
if (found !== undefined) {
continue;
}
let newCase = {...data_with_holes[i - 1][j]};
newCase.date = data_with_holes[i][0].date;
mutableArray[i].push(newCase);
}
}
// // this can also be commented, transition will still be buggy
// // now backtrack to fill the data with zeros
for (let i = data_with_holes.length - 2; i >= 0; i--) {
for (let j = 0; j < mutableArray[i + 1].length; j++) {
let found = mutableArray[i].find(element => element.state === mutableArray[i + 1][j].state && element.city === mutableArray[i + 1][j].city);
if (found !== undefined) {
continue;
}

let newCase = {...data_with_holes[i + 1][j]};
newCase.confirmed = 0;
newCase.deaths = 0;
newCase.date = mutableArray[i][0].date;
mutableArray[i].push(newCase);
}
// trying to sort
mutableArray[i] = mutableArray[i].sort((a,b) => `${a.state}${a.city}`.localeCompare(`${b.state}${b.city}`));
}
return mutableArray;
}
Insert cell
function movAvg(data, accessor, window = 7) {
const means = data.map((d, i) => {
const sums = d3.sum(data.slice(i - window, i), d => d[accessor]);
console.log("sums is ", sums, "returning: ", sums/ window)
const _d = data[i];
_d.weeklyAvg = sums/window;
return _d;
})
return means;
}
Insert cell
date = d3.utcParse("%Y-%m-%d")
Insert cell
d3 = require("d3@5", "d3-array")
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