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

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

var g_yaxis = svg.append("g")
.attr("id", "y-axis");
g_yaxis.call(yAxis);
draw_break_ticks(g_yaxis, y);

svg.append("g")
.selectAll("circle")
.data(data)
.join("circle")
.filter(d => d.body_mass_g)
.attr("cx", d => x(d.flipper_length_mm))
.attr("cy", d => y.transform(d.body_mass_g))
.attr("r", 4);

return svg.node();
}
Insert cell
draw_break_ticks = (g, y) =>{
for(let i = 0; i < y.breaks.length; i++){
var y_break = y.breaks[i].end;
g.append("rect")
.attr("fill", "black")
.attr("x", -5)
.attr("y", y.transform(y_break) + 2)
.attr("width", 10)
.attr("height", 1);

g.append("rect")
.attr("fill", "black")
.attr("x", -5)
.attr("y", y.transform(y_break) - 3)
.attr("width", 10)
.attr("height", 1);

g.append("rect")
.attr("fill", "white")
.attr("x", -5)
.attr("y", y.transform(y_break) - 2)
.attr("width", 10)
.attr("height", 4);
}
}
Insert cell
x = d3.scaleLinear()
.domain(d3.extent(data, d => d.flipper_length_mm)).nice()
.range([margin.left, width - margin.right])
Insert cell
y = {
var y = new breakScale();
y.domain(data, d => d.body_mass_g, d => 50);
y.range([height - margin.bottom, margin.top]);
return y;
}

Insert cell
xAxis = g => g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x))
Insert cell
yAxis = g => g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y.linear_no_breaks)
.tickFormat(d => y.transform_tick(d)));
Insert cell
Insert cell
class breakScale{
constructor(){
this.linear = d3.scaleLinear();
this.linear_no_breaks = d3.scaleLinear();
}

domain(data, acess_pos, acess_length){
this.breaks = [];
var ind = data.map((d, i) => i);
ind = ind.sort(function(a, b){ return acess_pos(data[a]) - acess_pos(data[b]);});
var start_pos, end_pos, space;
for(let i = 0; i < (data.length - 1); i++){
if(i == 0){
end_pos = acess_pos(data[ind[i]]) + acess_length(data[ind[i]])/2;
}else{
end_pos = d3.max(ind.slice(0, i + 1).map(ii => acess_pos(data[ii]) + acess_length(data[ii])/2));
}
start_pos = acess_pos(data[ind[i+1]]) - acess_length(data[ind[i+1]])/2;
space = start_pos - end_pos;
if(space > 0){
this.breaks.push({start: end_pos, end: start_pos, space: space});
}
}

var minVal = d3.min(data.map(d => acess_pos(d) - acess_length(d)/2));
var maxVal = d3.max(data.map(d => acess_pos(d) + acess_length(d)/2));
this.linear.domain([minVal, maxVal]);
this.linear_no_breaks.domain([minVal, maxVal - this.breaks.map(d => d.space).reduce((a, b) => a + b)]);
}
range(rangeVal){
this.linear.range(rangeVal);
this.linear_no_breaks.range(rangeVal);
}

transform(y){
var y_old = y;
for(let i = 0; i < this.breaks.length; i++){
if(y_old >= this.breaks[i].end){
y = y - this.breaks[i].space;
}
}
return this.linear_no_breaks(y);
}

transform_tick(y){
var i = 0;
while(y > this.breaks[i].start){
y = y + this.breaks[i].space;
i = i + 1;
}
return y;
}
}
Insert cell
Insert cell
data_original = FileAttachment("penguins.csv").csv({typed: true})
Insert cell
data = data_original.filter(d => !Number.isNaN(d.flipper_length_mm))
.filter(d => !((d.body_mass_g >= 3500) & (d.body_mass_g <= 4000)) &
!((d.body_mass_g >= 5000) & (d.body_mass_g <= 5650)))
Insert cell
Insert cell
margin = ({top: 25, right: 20, bottom: 35, left: 40})
Insert cell
height = 400
Insert cell
width = 720
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