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})`);
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();
}