Published unlisted
Edited
Oct 26, 2021
Importers
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
let container = html`<div>`;
var svg = d3
.select(container)
.append("svg")
.attr("width", width)
.attr("height", chart_height + 100);

// Add the X Axis
svg
.append("g")
.attr('id', 'x_axis_g')
.call(d3.axisTop(x).tickFormat(d3.format("d")));

svg.select('#x_axis_g').style('transform', 'translate(110px, 50px)');

// Y Axis
// Add circles for count by place
let circles_count = svg
.append('g')
.style('transform', 'translate(110px, 50px)')
.attr('id', 'g_circles_count')
.selectAll('circle')
.data(places_counts)
.enter()
.append('circle')
.attr('class', 'circles_count')
.attr('cx', d => -circle_radius * (d.count / places_counts[0].count))
.attr('cy', d => y(d.place) + box_height / 2)
.attr('r', d => circle_radius * (d.count / places_counts[0].count))
.attr('fill', d => d3.interpolatePuBuGn(d.count / places_counts[0].count))
.append('title')
.text(d => d.count + ' classifications');

// Add y axis
svg
.append("g")
.attr('id', 'y_axis_g')
.call(d3.axisLeft(y));

svg.select('#y_axis_g').style('transform', 'translate(110px, 50px)');
svg
.select('#y_axis_g')
.selectAll('.tick')
.select('text')
.style(
'transform',
`translate(0px, ${chart_height / places.length / 2}px)`
);

// Duplicate y ticks text - so we can add white outline behind
svg
.select('#y_axis_g')
.selectAll('.tick')
.select('text')
.attr('class', 'y_ticks_below')
.clone('deep')
.attr('class', 'y_ticks_above');

// Extend ticks to make gridlines
svg
.select('#y_axis_g')
.selectAll('.tick')
.select('line')
.attr('x1', chart_width)
.attr('stroke', 'lightgray');

// Extend y axis to cover the final tick
let path_d = svg
.select('#y_axis_g')
.select('path')
.attr('d')
.split('V');
let path_d_value = path_d[1].split('H');
svg
.select('#y_axis_g')
.select('path')
.attr(
'd',
path_d[0] +
"V" +
String(+path_d_value[0] + box_height) +
"H" +
path_d_value[1]
);

svg
.select('#x_axis_g')
.selectAll('.tick')
.select('line')
.attr('y1', chart_height)
.attr('stroke', 'lightgray');

// Add y axis label
svg
.append('text')
.text('Places ordered by classification count →')
.attr('x', 20)
.attr('y', 100)
.style('transform', 'translateX(120px) translatey(270px) rotate(90deg)')
.style('font-family', 'var(--sans-serif)')
.style('font-size', '13px');

let data_text = svg
.append('g')
.attr('id', 'data_text')
.style('transform', 'translate(110px, 50px)');

// Draw data
data_byPlace_top5_10yrStep.forEach(function(place_data, i) {
place_data.data_byYear.forEach(function(year_data, j) {
year_data.data.forEach(function(machine, k) {
data_text
.append('rect')
.attr('y', y(place_data.place) + (box_height / 5 - 2) * k)
.attr('width', machineCountRect_scale(machine.count))
.attr('height', box_height / 5)
.attr('x', x(year_data.x0))
.attr('fill', '#73FBD3');

data_text
.append('text')
.attr('y', y(place_data.place))
.attr('x', x(year_data.x0) + 1)
.attr('dy', (box_height / 5 - 2) * k + 10)
.text(
machine.machine.length > 18
? machine.machine.slice(0, 18).concat('…')
: machine.machine
)
.attr('font-family', 'var(--sans-serif)')
.attr('font-size', '11px')
.append('title')
.text(machine.machine + ": " + machine.count);
});
});
});

return container;
}
Insert cell
html`
<style>
.circles_count {
mix-blend-mode: hue;
}
.y_ticks_below {
stroke: white;
stroke-width:5;
stroke-linejoin: round;
opacity:0.7;
}
#y_axis_g .tick text {
font-size: 10.5px;
}
.tick line{
stroke-dasharray:4, 2;
}
#y_axis_g .tick line{
transform: translateY(${box_height}px)
}
</style>`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
y = d3
.scaleOrdinal()
.domain(places_orderedByClassificationCount)
.range(d3.range(places.length).map(d => (d / places.length) * chart_height))
Insert cell
x = d3
.scaleLinear()
.domain(years_extent)
.range([0, chart_width])
Insert cell
machineCountRect_scale = d3
.scalePow()
.exponent(0.7)
.domain([0, max_count])
.range([0, box_width])
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
data_byPlace_byYear = d3.groups(
machine_classifications.map(d => ({
...d,
'newspaper year': +d['newspaper year']
})),
d => d['newspaper place'],
d => d['newspaper year']
)
Insert cell
// Create bin function (using d3.bin) to group data by 10yr intervals
bin = d3
.bin()
.value(d => d[0])
.domain(years_extent)
.thresholds(year_thresholds) // specify the time step
Insert cell
data_byPlace_top5_10yrStep = data_byPlace_byYear.map(d => ({
place: d[0],
data_byYear: bin(d[1]).map(d => ({
x0: d['x0'],
x1: d['x1'],
data: d3
.groups(d.map(k => k[1]).flat(), v => v.tidied_annotations)
.sort((a, b) => b[1].length - a[1].length)
.map(k => ({
machine: k[0],
count: k[1].length,
data: k[1]
}))
.slice(0, 5)
.filter(k => k.count > 1)
}))
}))
Insert cell
Insert cell
Insert cell
Insert cell
places = data_byPlace_top5_10yrStep.map(d => d.place)
Insert cell
places_orderedByClassificationCount = d3
.rollups(machine_classifications, v => v.length, d => d['newspaper place'])
.sort((a, b) => b[1] - a[1])
.map(d => d[0])
Insert cell
places_counts = d3
.rollups(machine_classifications, v => v.length, d => d['newspaper place'])
.sort((a, b) => b[1] - a[1])
.map(d => ({ place: d[0], count: d[1] }))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
annotations_Ads = FileAttachment("annotations_3tasks_grouped_merged.csv").csv()
Insert cell
d3
.groups(annotations_Ads, d => d['Ads?'], d => d['Classify!'])
.sort((a, b) => b[1].length - a[1].length)
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