|
| 1 | +<!doctype html> |
| 2 | +<notebook theme="air"> |
| 3 | + <title>Brushable scatterplot</title> |
| 4 | + <script id="1" type="text/markdown"> |
| 5 | + <div style="color: grey; font: 13px/25.5px var(--sans-serif); text-transform: uppercase;"><h1 style="display: none;">Brushable scatterplot</h1><a href="https://d3js.org/">D3</a> › <a href="/@d3/gallery">Gallery</a></div> |
| 6 | + |
| 7 | + # Brushable scatterplot |
| 8 | + |
| 9 | + This chart shows the inverse relationship between engine power (*y*-axis) and fuel efficiency (*x*-axis) in ${data.length} cars from 1970–1982. Brushing this scatterplot will show the selected data points. |
| 10 | + </script> |
| 11 | + <script id="10" type="application/vnd.observable.javascript" pinned=""> |
| 12 | + viewof selection = { |
| 13 | + |
| 14 | + // Specify the chart’s dimensions. |
| 15 | + const width = 928; |
| 16 | + const height = 600; |
| 17 | + const marginTop = 20; |
| 18 | + const marginRight = 30; |
| 19 | + const marginBottom = 30; |
| 20 | + const marginLeft = 40; |
| 21 | + |
| 22 | + // Create the horizontal (x) scale, positioning N/A values on the left margin. |
| 23 | + const x = d3.scaleLinear() |
| 24 | + .domain([0, d3.max(data, d => d["Miles_per_Gallon"])]).nice() |
| 25 | + .range([marginLeft, width - marginRight]) |
| 26 | + .unknown(marginLeft); |
| 27 | + |
| 28 | + // Create the vertical (y) scale, positioning N/A values on the bottom margin. |
| 29 | + const y = d3.scaleLinear() |
| 30 | + .domain([0, d3.max(data, d => d["Horsepower"])]).nice() |
| 31 | + .range([height - marginBottom, marginTop]) |
| 32 | + .unknown(height - marginBottom); |
| 33 | + |
| 34 | + // Create the SVG container. |
| 35 | + const svg = d3.create("svg") |
| 36 | + .attr("viewBox", [0, 0, width, height]) |
| 37 | + .property("value", []); |
| 38 | + |
| 39 | + // Append the axes. |
| 40 | + svg.append("g") |
| 41 | + .attr("transform", `translate(0,${height - marginBottom})`) |
| 42 | + .call(d3.axisBottom(x)) |
| 43 | + .call(g => g.select(".domain").remove()) |
| 44 | + .call(g => g.append("text") |
| 45 | + .attr("x", width - marginRight) |
| 46 | + .attr("y", -4) |
| 47 | + .attr("fill", "#000") |
| 48 | + .attr("font-weight", "bold") |
| 49 | + .attr("text-anchor", "end") |
| 50 | + .text("Miles per Gallon")); |
| 51 | + |
| 52 | + svg.append("g") |
| 53 | + .attr("transform", `translate(${marginLeft},0)`) |
| 54 | + .call(d3.axisLeft(y)) |
| 55 | + .call(g => g.select(".domain").remove()) |
| 56 | + .call(g => g.select(".tick:last-of-type text").clone() |
| 57 | + .attr("x", 4) |
| 58 | + .attr("text-anchor", "start") |
| 59 | + .attr("font-weight", "bold") |
| 60 | + .text("Horsepower")); |
| 61 | + |
| 62 | + // Append the dots. |
| 63 | + const dot = svg.append("g") |
| 64 | + .attr("fill", "none") |
| 65 | + .attr("stroke", "steelblue") |
| 66 | + .attr("stroke-width", 1.5) |
| 67 | + .selectAll("circle") |
| 68 | + .data(data) |
| 69 | + .join("circle") |
| 70 | + .attr("transform", d => `translate(${x(d["Miles_per_Gallon"])},${y(d["Horsepower"])})`) |
| 71 | + .attr("r", 3); |
| 72 | + |
| 73 | + // Create the brush behavior. |
| 74 | + svg.call(d3.brush().on("start brush end", ({selection}) => { |
| 75 | + let value = []; |
| 76 | + if (selection) { |
| 77 | + const [[x0, y0], [x1, y1]] = selection; |
| 78 | + value = dot |
| 79 | + .style("stroke", "gray") |
| 80 | + .filter(d => x0 <= x(d["Miles_per_Gallon"]) && x(d["Miles_per_Gallon"]) < x1 |
| 81 | + && y0 <= y(d["Horsepower"]) && y(d["Horsepower"]) < y1) |
| 82 | + .style("stroke", "steelblue") |
| 83 | + .data(); |
| 84 | + } else { |
| 85 | + dot.style("stroke", "steelblue"); |
| 86 | + } |
| 87 | + |
| 88 | + // Inform downstream cells that the selection has changed. |
| 89 | + svg.property("value", value).dispatch("input"); |
| 90 | + })); |
| 91 | + |
| 92 | + return svg.node(); |
| 93 | + } |
| 94 | + </script> |
| 95 | + <script id="65" type="application/vnd.observable.javascript" pinned=""> |
| 96 | + selection |
| 97 | + </script> |
| 98 | + <script id="3" type="application/vnd.observable.javascript" pinned=""> |
| 99 | + data = FileAttachment("cars-2.csv").csv({typed: true}) |
| 100 | + </script> |
| 101 | + <script id="281" type="text/markdown"> |
| 102 | + Thanks to [John Alexis Guerra Gómez](/@john-guerra) for suggestions. |
| 103 | + </script> |
| 104 | +</notebook> |
0 commit comments