Published
Edited
Jul 8, 2020
Insert cell
Insert cell
chart = {
const redData = data.filter(d=> red.indexOf(d.state) > -1)
const blueData = data.filter(d=> blue.indexOf(d.state) > -1)
const blueDataSummed = d3.rollups(blueData, v => d3.sum(v, d => d.newCases), d => +d.date)
const redDataSummed = d3.rollups(redData, v => d3.sum(v, d => d.newCases), d => +d.date)
const grouped = d3.group(data, d => d.state)
grouped.forEach(function(value, key) {
for(var i=0; i<value.length; i++) {
var element = value[i];
var newCases = 0;
if(i > 1) newCases = value[i].cases - value[i-1].cases;
element['newCases'] = newCases;

element['newCasesRolling'] = 0;
if(i >= 6) {
let sum = 0;
for (var j=0; j<6; j++) {
sum += value[i-j].newCases;
}
element['newCasesRolling'] = sum / 7;
}
}
});
const maxCases = d3.rollup(data, v => d3.max(v, d => d.cases), d => d.state)
const sortedStates = Array.from(maxCases, ([name, value]) => ({name, value})).sort((a,b)=>b.value-a.value)
sortedStates.forEach(function(state) {
if(blue.indexOf(state.name) >= 0) state.category = 0;
else if(red.indexOf(state.name) >= 0) state.category = 1;

//console.log(state.name, grouped.get(state.name)[0]['date']);
})
const categorized = d3.group(sortedStates, d=>d.category)

const hierData = Object.create({
children: [
{
name: 'Democratic governor',
children: categorized.get(0)
},
{
name: 'Republican governor',
children: categorized.get(1)
}
]
})

const hierarchy = d3.hierarchy( hierData )
.sum(d => d.value)
.sort((a, b) => b.value - a.value)

const treemap = d3.treemap()
// set the size of the treemap render area
.size([ width, height ])
// set the padding between every rectangle in px
.padding(2)
// set the padding at the top of each group of rectangles
// so that we can fit the country labels
.paddingTop(10)
// Set the generator to round pixel values to the nearest value
// (makes things look better)
.round(true)

const root = treemap(hierarchy)
const addHeight = 300
const svg = d3.create('svg')
.style('font-family', 'sans-serif')
.attr('width', width)
.attr('height', height + addHeight )

const g = svg.append('g')
.attr('class', 'treemap-container')
g.selectAll('text.category')
.data( root.children )
.join('text')
.attr('class', 'category')
.attr('x', d => d.x0)
.attr('y', d => d.y0)
.attr('dy', '0.6em')
.attr('dx', 3)
.style('font-size', 11)
.text(function(d) {
var count = d3.sum(d.data.children, d=>d.value);
return d.data.name + ', Cases: ' + format(count)}
)
const leaf = g.selectAll('g.leaf')
.data(root.leaves())
.join('g')
.attr('class', 'leaf')
.attr('transform', d => `translate(${ d.x0 },${ d.y0 })`)
.style('font-size', 10)
leaf.append('title')
.text(d => `${ d.data.name }\n${ d.value.toLocaleString() }`)

var rect = leaf.append('rect')
.attr('fill', function(d) {
if(d.parent.data.name == 'Democratic governor'){
return 'lightblue';
} else if(d.parent.data.name == 'Republican governor'){
return 'red';
}
})
.attr('opacity', 0.7)
.attr('width', d => d.x1 - d.x0)
.attr('height', d => d.y1 - d.y0)
.attr('rx', 3)
.attr('ry', 3);
leaf.each((d, i, arr) => {
const current = arr[i]
const left = d.x0,
right = d.x1,
width = right - left,
top = d.y0,
bottom = d.y1,
height = d.y1 - d.y0

const tooSmall = width < 34 || height < 25
const text = d3.select( current ).append('text')
.attr('opacity', tooSmall ? 0 : 0.9)
.attr('fill', function(d) {
if(d.parent.data.name == 'Democratic governor'){
return 'black';
} else if(d.parent.data.name == 'Republican governor'){
return 'white';
}
})
.selectAll('tspan')
.data(d => [ d.data.name, d.value.toLocaleString() ])
.join('tspan')
.attr('x', 3)
.attr('y', (d,i) => i ? '2.5em' : '1.15em')
.text(d => d)

})
root.children.forEach(function(group) {
group.children.forEach(function(state){
var extentCases = d3.extent(grouped.get(state.data.name), d => d.newCasesRolling);
var extentDates = d3.extent(grouped.get(state.data.name), d => d.date);
const offset = 5;
const y = d3.scaleLinear()
.domain(extentCases).nice()
.range([state.y1 - offset, state.y0]);
const x = d3.scaleUtc()
.domain(extentDates)
.range([state.x0 + offset, state.x1 - offset]);
const line = d3.line()
.curve(d3.curveMonotoneX)
.x(d => x(d.date))
.y(d => y(d.newCasesRolling));
svg.append('path')
.attr('fill', 'none')
.attr('stroke', 'black')
.attr('opacity', 0.4)
.attr('stroke-width', 1.5)
.attr('d', line(grouped.get(state.data.name)));
})
});
//add the aggregate red/blue "new cases" linecharts
var maxDailyCases = d3.max(redDataSummed, d => d[1]);
if(d3.max(blueDataSummed, d => d[1]) > maxDailyCases) maxDailyCases = d3.max(blueDataSummed, d => d[1]);
var extentCases = [0, maxDailyCases];
var extentDates = d3.extent(data, d => d.date);

var y = d3.scaleLinear()
.domain(extentCases)
.range([addHeight-margin.bottom, 10]);
// console.log(y(0), y(35000))

var x = d3.scaleUtc()
.domain(extentDates)
.range([margin.left, width-margin.left]);
// console.log(x(1579996800000), x(1593820800000));

const line = d3.line()
.curve(d3.curveMonotoneX)
.x(d => x(d[0]))
.y(d => y(d[1]))

var redLine = svg.append('g').attr('transform', `translate(0,${height})`)
redLine.append('path')
.attr('fill', 'none')
.attr('stroke', 'red')
.attr('opacity', 0.7)
.attr('stroke-width', 3)
.attr('d', line(redDataSummed));
var blueLine = svg.append('g').attr('transform', `translate(0,${height})`)
blueLine.append('path')
.attr('fill', 'none')
.attr('stroke', 'lightblue')
.attr('opacity', 0.7)
.attr('stroke-width', 3)
.attr('d', line(blueDataSummed));
var xAxis = g => g
.attr("transform", `translate(0,${height + addHeight - margin.bottom})`)
.call(d3.axisBottom(x))
svg.append("g")
.call(xAxis);
var yAxis = g => g
.attr("transform", `translate(${margin.left},${height})`)
.call(d3.axisLeft(y).ticks(5))
// .call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", 10)
.attr("y", 15)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("↑ Daily new cases"))
svg.append("g")
.call(yAxis);
return svg.node();

}
Insert cell
Insert cell
red = ['Alabama', 'Alaska','Arizona','Arkansas','Florida','Georgia','Idaho','Indiana','Iowa', 'Maryland','Massachusetts','Mississippi','Missouri','Nebraska','New Hampshire', 'North Dakota','Ohio','Oklahoma','South Carolina','South Dakota','Tennessee','Texas','Utah', 'Vermont', 'West Virginia', 'Wyoming']
Insert cell
blue = ['District of Columbia','California', 'Colorado', 'Connecticut', 'Delaware', 'Hawaii', 'Illinois', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Michigan', 'Minnesota', 'Montana', 'Nevada', 'New Jersey', 'New Mexico', 'New York', 'North Carolina', 'Oregon', 'Pennsylvania', 'Rhode Island', 'Virginia', 'Washington', 'Wisconsin']
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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