Insert cell
Insert cell
diamonds.csv
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
chart_scatter = {
const { xScale, yScale, colorScale } = scales_scatter;

const svg = d3.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top);

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

// --- Eje X ---
g.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(xScale))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("stroke-opacity", 0.1)
.attr("y2", -(height - margin.top - margin.bottom)))
.call(g => g.selectAll(".tick text")
.attr("font-size", "12px")
.attr("fill", "gray"));


g.append("text")
.attr("x", width / 2)
.attr("y", height - margin.bottom / 2 + 15)
.attr("text-anchor", "middle")
.attr("font-size", "14px")
.attr("fill", "black")
.text("Quilate (Carat)");

g.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line")
.clone()
.attr("stroke-opacity", 0.1)
.attr("x2", width - margin.left - margin.right))
.call(g => g.selectAll(".tick text")
.attr("font-size", "12px")
.attr("fill", "gray"));


g.append("text")
.attr("x", -height / 2)
.attr("y", margin.left / 2 - 20)
.attr("text-anchor", "middle")
.attr("transform", "rotate(-90)")
.attr("font-size", "14px")
.attr("fill", "black")
.text("Precio ($)");

svg.append("text")
.attr("x", (width + margin.left + margin.right) / 2)
.attr("y", margin.top / 2)
.attr("text-anchor", "middle")
.attr("font-size", "15px")
.attr("font-weight", "bold")
.text("Relación entre Quilate y Precio por Tipo de Corte de Diamantes");


const circles = g.selectAll("circle")
.data(scatter_data)
.join(
enter => enter.append("circle")
.attr("cx", d => xScale(d.carat))
.attr("cy", d => yScale(d.price))
.attr("r", 0)
.attr("fill", d => colorScale(d.cut))
.attr("opacity", 0.7),
update => update,
exit => exit.remove()
);

circles
.transition()
.duration(500)
.delay((d, i) => i * 0.1)
.attr("r", 3);

circles
.on("mouseover", function(event, d) {
d3.select(this)
.transition().duration(100)
.attr("r", 6)
.attr("opacity", 1)
.attr("stroke", "black")
.attr("stroke-width", 1);
})
.on("mouseout", function(event, d) {
d3.select(this)
.transition().duration(200)
.attr("r", 3)
.attr("opacity", 0.7)
.attr("stroke", "none");
});

const n = scatter_data.length;
const sumX = d3.sum(scatter_data, d => d.carat);
const sumY = d3.sum(scatter_data, d => d.price);
const sumXY = d3.sum(scatter_data, d => d.carat * d.price);
const sumX2 = d3.sum(scatter_data, d => d.carat * d.carat);

const m = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); // Pendiente
const b = (sumY - m * sumX) / n;
const xMin = d3.min(scatter_data, d => d.carat);
const xMax = d3.max(scatter_data, d => d.carat);

const regressionLineData = [
{ x: xMin, y: m * xMin + b },
{ x: xMax, y: m * xMax + b }
];

const lineRegressionGenerator = d3.line()
.x(d => xScale(d.x))
.y(d => yScale(d.y));

g.append("path")
.datum(regressionLineData)
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("stroke-dasharray", ("5, 5"))
.attr("d", lineRegressionGenerator);


const cutCategories = Array.from(new Set(scatter_data.map(d => d.cut))).sort((a, b) => {
const order = ["Fair", "Good", "Very Good", "Premium", "Ideal"];
return order.indexOf(a) - order.indexOf(b);
});

const legend = svg.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("transform", `translate(${width + margin.left + 5}, ${margin.top})`);

legend.append("text")
.attr("x", 0)
.attr("y", -10)
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Corte del Diamante");


const legendItems = legend.selectAll("g")
.data(cutCategories)
.join("g")
.attr("transform", (d, i) => `translate(0,${i * 20})`);

legendItems.append("rect")
.attr("x", 0)
.attr("width", 15)
.attr("height", 15)
.attr("fill", colorScale);

legendItems.append("text")
.attr("x", 20)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(d => d)
.attr("fill", "black");

return svg.node();
}
Insert cell
diamond_data = FileAttachment("diamonds.csv").csv({typed: true})
Insert cell
d3 = require("d3@6")
Insert cell
// Celda: transformed_data
// Depende de: `diamond_data`, `d3`
transformed_data = Array.from(d3.rollup(diamond_data,
group => ({
avg_price: d3.mean(group, d => d.price),
avg_carat: d3.mean(group, d => d.carat),
avg_depth: d3.mean(group, d => d.depth),
avg_table: d3.mean(group, d => d.table),
avg_x: d3.mean(group, d => d.x),
avg_y: d3.mean(group, d => d.y),
avg_z: d3.mean(group, d => d.z)
}),
d => d.cut
))
.map(([cut, values]) => ({ cut, ...values }))
.sort((a, b) => {
const order = ["Fair", "Good", "Very Good", "Premium", "Ideal"];
return order.indexOf(a.cut) - order.indexOf(b.cut);
});
Insert cell
width = 800
Insert cell
height = 400
Insert cell
margin_top = 20
Insert cell
margin_right = 100
Insert cell
margin_bottom = 40
Insert cell
margin_left = 60
Insert cell
scatter_data = diamond_data.map(d => ({
cut: d.cut,
carat: d.carat,
price: d.price
}))
.filter(d => d.carat != null && !isNaN(d.carat) && d.price != null && !isNaN(d.price))
.sort((a, b) => a.price - b.price);
Insert cell
margin = ({
top: margin_top,
right: margin_right,
bottom: margin_bottom,
left: margin_left
})
Insert cell
scales_scatter = {
const xScale_scatter = d3.scaleLinear()
.domain(d3.extent(scatter_data, d => d.carat)).nice()
.range([margin.left, width - margin.right]);

const yScale_scatter = d3.scaleLinear()
.domain([0, d3.max(scatter_data, d => d.price)]).nice()
.range([height - margin.bottom, margin.top]);

const colorScale_scatter = d3.scaleOrdinal(d3.schemeCategory10)
.domain(Array.from(new Set(scatter_data.map(d => d.cut))));
return {
xScale: xScale_scatter,
yScale: yScale_scatter,
colorScale: colorScale_scatter
};
}
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