Public
Edited
Feb 26
Fork of Stripplot
Insert cell
Insert cell
Plot.plot({
width: 960,
height: 560,
x: {
axis: "top",
grid: true,
domain: [0, 10]
},
fy: {
grid: true,
tickFormat: (d) => d ?? "N/A",
label: null,
padding: 0
},
facet: {
data: movies,
y: "Major Genre",
marginLeft: 120
},
marks: [
Plot.dot(
movies,
Plot.dodgeY(
{
anchor: "middle",
padding: -2
},
{
x: "IMDB Rating",
stroke: "Major Genre",
r: 2.5
}
)
)
]
})
Insert cell
movies = FileAttachment("movies.json").json()
Insert cell
import * as d3 from "d3";
Insert cell
data = [
{ x: 1, y1: 10, y2: 5 },
{ x: 2, y1: 15, y2: 7 },
{ x: 3, y1: 20, y2: 10 },
{ x: 4, y1: 25, y2: 12 },
{ x: 1, y1: 11, y2: 6 },
{ x: 1, y1: 1, y2: 10 }
]

Insert cell
{
// Set dimensions
const width = 500, height = 400, margin = 50;

// Create SVG container
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height);

// Create scales
const xScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.x) + 1])
.range([margin, width - margin]);

const yScale = d3.scaleLinear()
.domain([-d3.max(data, d => d.y2) - 5, d3.max(data, d => d.y1) + 5])
.range([height - margin, margin]);

// Add X and Y axes
svg.append("g")
.attr("transform", `translate(0, ${height - margin})`)
.call(d3.axisBottom(xScale).ticks(5));

svg.append("g")
.attr("transform", `translate(${margin}, 0)`)
.call(d3.axisLeft(yScale).ticks(10)
.tickFormat(d => Math.abs(d)) // Convert negative labels to positive
);

// Remove extra Y-axis grid lines
svg.selectAll(".tick line").attr("stroke", "none"); // Hides all tick lines

// Add vertical line at X = 0
svg.append("line")
.attr("x1", xScale(0))
.attr("x2", xScale(0))
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("stroke-dasharray", "4,2"); // Dashed line for clarity

// Create a tooltip div (initially hidden)
const tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("background", "#fff")
.style("border", "1px solid black")
.style("padding", "5px")
.style("border-radius", "5px")
.style("visibility", "hidden")
.style("opacity", 0);

// Add scatter points for Y1 (Positive Y-axis)
svg.selectAll(".y1-points")
.data(data)
.enter()
.append("circle")
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(d.y1))
.attr("r", 5)
.attr("fill", "blue")
.on("mouseover", function(event, d) {
tooltip.transition().duration(200).style("opacity", .9);
tooltip.html(`(${d.x}, ${d.y1})`)
.style("visibility", "visible")
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 10) + "px");
})
.on("mouseout", function() {
tooltip.transition().duration(200).style("opacity", 0);
});

// Add scatter points for Y2 (Negative Y-axis but labels shown as positive)
svg.selectAll(".y2-points")
.data(data)
.enter()
.append("circle")
.attr("cx", d => xScale(d.x))
.attr("cy", d => yScale(-d.y2)) // Flip Y2 to negative
.attr("r", 5)
.attr("fill", "red")
.on("mouseover", function(event, d) {
tooltip.transition().duration(200).style("opacity", .9);
tooltip.html(`(${d.x}, ${d.y2})`) // Keep actual values in tooltip
.style("visibility", "visible")
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 10) + "px");
})
.on("mouseout", function() {
tooltip.transition().duration(200).style("opacity", 0);
});

return svg.node(); // Return the generated SVG
}

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