Public
Edited
May 11, 2022
4 stars
Insert cell
Insert cell
Insert cell
## TODO - split this up so that section 1 is plot only, section 2 is text to lef,t section 3 is text right, etc
Insert cell
chart = {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, data.length * inputs.rowHeight])
.style("font", "13px sans-serif"),
height = data.length * inputs.rowHeight,
margin = { top: 35, bottom: 35, left: 350 },
domainRegex = /(?:-?\d+),\s?(?:-?\d+)/,
xDomain = domainRegex.test(inputs.xdomain)
? inputs.xdomain.split(",").map((d) => parseInt(d))
: [d3.min(data, (d) => d.Lower), d3.max(data, (d) => d.Upper)],
x = d3
.scaleLinear()
.domain(xDomain)
.range([margin.left, margin.left + inputs.plotWidth])
.clamp(true),
y = d3
.scaleLinear()
.domain([-1, data.length])
.range([margin.top, height - margin.bottom]),
yAxis = (g) =>
g.attr("transform", `translate(${margin.left},0)`).call(d3.axisLeft(y)),
xAxis = (g) =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(inputs.numTicks))
.style("font", "13px sans-serif"),
radiusScale = d3
.scaleSqrt()
.domain([1, d3.max(data, (d) => d.N)])
.range(inputs.radius);

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

const labels_g = svg
.append("g")
.attr("text-anchor", "middle")
.attr("class", "labels");

labels_g
.append("text")
.attr("font-weight", "bold")
.attr("x", d3.mean(x.range()))
.attr("y", y(-1) - 5)
.text(inputs.title);

labels_g
.append("text")
.attr("x", d3.mean(x.range()))
.attr("y", y(data.length) + 33)
.text(inputs.xlabel);

svg
.append("g")
.append("line")
.attr("stroke", "#A1A1A1")
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4 4")
.attr("x1", (d) => x(0))
.attr("x2", (d) => x(0))
.attr("y1", y.range()[0])
.attr("y2", y.range()[1]);

var lines = svg.append("g");
lines
.selectAll("line.a")
.data(data)
.join("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("x1", (d) => x(d.Lower))
.attr("x2", (d) => x(d.Upper))
.attr("y1", (d, i) => y(i))
.attr("y2", (d, i) => y(i));
lines
.selectAll("path.lower")
.data(data)
.join("path")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("d", (d, i) => {
const dx = d.Lower < xDomain[0] ? inputs.rowHeight * 0.3 : 0;
return d3.line()([
[x(d.Lower) + dx, y(i) - inputs.rowHeight * 0.2],
[x(d.Lower), y(i)],
[x(d.Lower) + dx, y(i) + inputs.rowHeight * 0.2]
]);
});

lines
.selectAll("path.upper")
.data(data)
.join("path")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("d", (d, i) => {
const dx = d.Upper > xDomain[1] ? inputs.rowHeight * 0.3 : 0;
return d3.line()([
[x(d.Upper) - dx, y(i) - inputs.rowHeight * 0.2],
[x(d.Upper), y(i)],
[x(d.Upper) - dx, y(i) + inputs.rowHeight * 0.2]
]);
});

const addText = (colName, xPosition, headerName, textAnchor) => {
let g = svg.append("g");

g.selectAll("text")
.data(data)
.join("text")
.attr("x", xPosition)
.attr("y", (d, i) => y(i))
.attr("text-anchor", textAnchor)
.attr("alignment-baseline", "central")
.text((d) => d[colName]);

g.append("text")
.attr("x", xPosition)
.attr("y", y(-1) - 5)
.attr("text-anchor", textAnchor)
.attr("font-weight", "bold")
.text((d) => headerName);
};
addText("Author(s) and Year", 20, "Author(s) and Year", "start");
addText("Confidence", 190, "Confidence", "middle");
addText("Timing", 265, "Timing", "middle");
addText("N", 330, "N", "end");
addText("Result", margin.left + inputs.plotWidth + 150, "Result", "end");

lines
.selectAll("line.upper")
.data(data)
.join("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y(data.length))
.attr("y2", y(data.length));

lines
.selectAll("line.lower")
.data(data)
.join("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y(-1))
.attr("y2", y(-1));

svg
.append("g")
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (d) => x(d.Estimate))
.attr("cy", (d, i) => y(i))
.attr("r", (d) => radiusScale(d[inputs.radiusScaledTo]));

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
chart2 = {
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, data.length * inputs.rowHeight])
.style("font", "13px sans-serif"),
height = data.length * inputs.rowHeight,
margin = { top: 35, bottom: 35, left: 350 },
domainRegex = /(?:-?\d+),\s?(?:-?\d+)/,
xDomain = domainRegex.test(inputs.xdomain)
? inputs.xdomain.split(",").map((d) => parseInt(d))
: [d3.min(data, (d) => d.Lower), d3.max(data, (d) => d.Upper)],
x = d3
.scaleLinear()
.domain(xDomain)
.range([margin.left, margin.left + inputs.plotWidth])
.clamp(true),
y = d3
.scaleLinear()
.domain([-1, data.length + 2])
.range([margin.top, height - margin.bottom]),
yAxis = (g) =>
g.attr("transform", `translate(${margin.left},0)`).call(d3.axisLeft(y)),
xAxis = (g) =>
g
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x).ticks(inputs.numTicks))
.style("font", "13px sans-serif"),
radiusScale = d3
.scaleSqrt()
.domain([1, d3.max(data, (d) => d.N)])
.range([3, 8]);

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

const labels_g = svg
.append("g")
.attr("text-anchor", "middle")
.attr("class", "labels");

labels_g
.append("text")
.attr("font-weight", "bold")
.attr("x", d3.mean(x.range()))
.attr("y", y(-1) - 5)
.text(inputs.title);

labels_g
.append("text")
.attr("x", d3.mean(x.range()))
.attr("y", y(data.length) + inputs.rowHeight * 2 + 23)
.text(inputs.xlabel);

svg
.append("g")
.append("line")
.attr("stroke", "#A1A1A1")
.attr("stroke-width", 2)
.attr("stroke-dasharray", "4 4")
.attr("x1", (d) => x(0))
.attr("x2", (d) => x(0))
.attr("y1", y.range()[0])
.attr("y2", y.range()[1]);

const regy = y(data.length + 1);
if (regressionInputs.showBaseline) {
svg
.append("g")
.append("line")
.attr("stroke", regressionInputs.color)
.attr("stroke-width", 2)
.attr("x1", (d) => x(regressionInputs.estimate))
.attr("x2", (d) => x(regressionInputs.estimate))
.attr("y1", y.range()[0])
.attr("y2", y.range()[1]);
}

var lines = svg.append("g");
lines
.selectAll("line.a")
.data(data)
.join("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("x1", (d) => x(d.Lower))
.attr("x2", (d) => x(d.Upper))
.attr("y1", (d, i) => y(i))
.attr("y2", (d, i) => y(i));
lines
.selectAll("path.lower")
.data(data)
.join("path")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("d", (d, i) => {
const dx = d.Lower < xDomain[0] ? inputs.rowHeight * 0.3 : 0;
return d3.line()([
[x(d.Lower) + dx, y(i) - inputs.rowHeight * 0.2],
[x(d.Lower), y(i)],
[x(d.Lower) + dx, y(i) + inputs.rowHeight * 0.2]
]);
});

lines
.selectAll("path.upper")
.data(data)
.join("path")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("d", (d, i) => {
const dx = d.Upper > xDomain[1] ? inputs.rowHeight * 0.3 : 0;
return d3.line()([
[x(d.Upper) - dx, y(i) - inputs.rowHeight * 0.2],
[x(d.Upper), y(i)],
[x(d.Upper) - dx, y(i) + inputs.rowHeight * 0.2]
]);
});

const addText = (colName, xPosition, headerName, textAnchor) => {
let g = svg.append("g");

g.selectAll("text")
.data(data)
.join("text")
.attr("x", xPosition)
.attr("y", (d, i) => y(i))
.attr("text-anchor", textAnchor)
.attr("alignment-baseline", "central")
.text((d) => d[colName]);

g.append("text")
.attr("x", xPosition)
.attr("y", y(-1) - 5)
.attr("text-anchor", textAnchor)
.attr("font-weight", "bold")
.text((d) => headerName);
};
addText("Author(s) and Year", 20, "Author(s) and Year", "start");
addText("Confidence", 190, "Confidence", "middle");
addText("Timing", 265, "Timing", "middle");
addText("N", 330, "N", "end");
addText("Result", margin.left + inputs.plotWidth + 150, "Result", "end");

// regression text
svg
.append("text")
.attr("font-weight", "bold")
.attr("x", margin.left + inputs.plotWidth + 150)
.attr("y", regy)
.attr("text-anchor", "end")
.attr("alignment-baseline", "central")
.text(
`${regressionInputs.estimate} [${regressionInputs.lower_ci}, ${regressionInputs.upper_ci}]`
);

svg
.append("text")
.attr("font-weight", "bold")
.attr("x", 20)
.attr("y", regy)
.attr("text-anchor", "start")
.attr("alignment-baseline", "central")
.text(regressionInputs.description);

lines
.selectAll("line.upper")
.data(data)
.join("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y(data.length + 2))
.attr("y2", y(data.length + 2));

lines
.selectAll("line.lower")
.data(data)
.join("line")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y(-1))
.attr("y2", y(-1));

svg
.append("g")
.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", (d) => x(d.Estimate))
.attr("cy", (d, i) => y(i))
.attr("r", (d) => radiusScale(d.N));

// add regression
svg
.append("path")
.attr("stroke", "black")
.attr("fill", regressionInputs.color)
.attr("stroke-width", 2)
.attr("d", (d, i) => {
return d3.line()([
[x(regressionInputs.lower_ci), regy],
[x(regressionInputs.estimate), regy + inputs.rowHeight * 0.4],
[x(regressionInputs.upper_ci), regy],
[x(regressionInputs.estimate), regy - inputs.rowHeight * 0.4],
[x(regressionInputs.lower_ci), regy]
]);
});

return svg.node();
}
Insert cell
import { interval } from "@mootari/range-slider@1326"
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