Public
Edited
May 22
1 fork
Insert cell
Insert cell
workbook = FileAttachment("GlobalSuperstore.xlsx").xlsx()
Insert cell
store = workbook.sheet(0, {
headers: true,
// range: "A1:J10"
})
Insert cell
store
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
function removeOutliers(arr, accessor) {
const values = arr.map(accessor).filter(x => !isNaN(x)).sort((a, b) => a - b);
const q1 = d3.quantile(values, 0.25);
const q3 = d3.quantile(values, 0.75);
const iqr = q3 - q1;
const lower = q1 - 1.5 * iqr;
const upper = q3 + 1.5 * iqr;
return arr.filter(d => {
const value = accessor(d);
return value >= lower && value <= upper;
});
}
Insert cell
filteredData = removeOutliers(store, d => d.Profit)
Insert cell
Insert cell
viewof svg = {
const margin = {top: 40, right: 40, bottom: 60, left: 70},
width = 800,
height = 500;

const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;

// Clean numeric fields
const data = filteredData.filter(d => !isNaN(d.Profit) && !isNaN(d.Sales))
.map(d => ({
Profit: +d.Profit,
Sales: +d.Sales,
Market: d.Market
}));

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.style("font-family", "sans-serif");

const g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);

// Scales
const x = d3.scaleLinear()
.domain(d3.extent(data, d => d.Profit)).nice()
.range([0, innerWidth]);

const y = d3.scaleLinear()
.domain(d3.extent(data, d => d.Sales)).nice()
.range([innerHeight, 0]);

const color = d3.scaleOrdinal()
.domain([...new Set(data.map(d => d.Market))])
.range(d3.schemeTableau10);

// Gridlines
g.append("g")
.attr("class", "grid")
.call(d3.axisLeft(y).tickSize(-innerWidth).tickFormat(""))
.selectAll("line")
.attr("stroke", "#ccc")
.attr("stroke-opacity", 0.3);

// Axes
g.append("g")
.attr("transform", `translate(0,${innerHeight})`)
.call(d3.axisBottom(x));

g.append("g")
.call(d3.axisLeft(y));

// Axis Labels
svg.append("text")
.attr("text-anchor", "middle")
.attr("x", margin.left + innerWidth / 2)
.attr("y", height - 15)
.attr("font-size", "14px")
.text("Profit");

svg.append("text")
.attr("text-anchor", "middle")
.attr("x", -margin.top - innerHeight / 2)
.attr("y", 20)
.attr("transform", "rotate(-90)")
.attr("font-size", "14px")
.text("Sales");

// Dots
g.selectAll("circle")
.data(data)
.join("circle")
.attr("cx", d => x(d.Profit))
.attr("cy", d => y(d.Sales))
.attr("r", 4)
.attr("fill", d => color(d.Market))
.attr("opacity", 0.6)
.attr("stroke", "white")
.attr("stroke-width", 0.5);

// Legend
const legend = svg.append("g")
.attr("transform", `translate(${width - margin.right - 120}, ${margin.top})`);

const markets = color.domain();
markets.forEach((market, i) => {
const row = legend.append("g")
.attr("transform", `translate(0, ${i * 20})`);
row.append("rect")
.attr("width", 12)
.attr("height", 12)
.attr("fill", color(market));
row.append("text")
.attr("x", 18)
.attr("y", 10)
.attr("font-size", "12px")
.text(market);
});

return svg.node();
}

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