Public
Edited
Dec 17, 2023
1 fork
2 stars
Insert cell
Insert cell
{
const svg = d3.create("svg").attr("width", width).attr("height", height);

// Axis
svg
.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(xAxis);

svg.append("g").attr("transform", `translate(${margin.left}, 0)`).call(yAxis);

// Bubbles
const bubbles = svg
.selectAll("bubbles")
.data(data)
.enter()
.append("circle")
.attr("cx", (d) => xScale(d["Income per person"]))
.attr("cy", (d) => yScale(d["Life expectancy "]))
.attr("r", (d) => radiusScale(d.Population))
.attr("fill", (d) => colorScale(d.region))
.style("fill-opacity", 0.94)
.attr("stroke", "#fff")
.on("mousemove", function (event, d, index) {
tooltip
.style("left", event.pageX + 0 + "px")
.style("top", event.pageY - 52 + "px")
.style("display", "block")
.html(`${d.name}`);

d3.select(this).style("stroke-width", 3).attr("stroke", "#111");
})
.on("mouseout", function () {
tooltip.style("display", "none");
d3.select(this).style("stroke-width", 1).attr("stroke", "#fff");
});

// Tooltip
const tooltip = d3.select("body").append("div").attr("class", "tooltip");

// Units
const yUnit = svg
.append("text")
.attr("transform", "translate(20," + height / 2 + ") rotate(-90)")
.text("Life expectancy")
.attr("class", "unit");

const xUnit = svg
.append("text")
.attr("transform", `translate(${width / 2}, ${height - 18})`)
.text("GDP per capita")
.attr("class", "unit");

// Legend
const legendRects = svg
.selectAll("legend-rects")
.data(regionList)
.enter()
.append("rect")
.attr("x", (d, i) => width - margin.right - 80)
.attr("y", (d, i) => height - margin.bottom - 70 - 25 * i)
.attr("width", 12)
.attr("height", 17)
.attr("fill", (d) => colorScale(d));

const legendTexts = svg
.selectAll("legend-texts")
.data(regionList)
.enter()
.append("text")
.attr("x", (d, i) => width - margin.right - 80 + 20)
.attr("y", (d, i) => height - margin.bottom - 70 - 25 * i + 15)
.text((d) => d)
.attr("class", "legend-texts");

return svg.node();
}
Insert cell
Insert cell
regionList
Insert cell
colorScale = d3
.scaleOrdinal()
.domain([...new Set(data.map((d) => d.region))])
.range(["#8160C8", "#FFA602", "#CDC0E4", "#cfcfcf"]) // #ccc
// .range(["#ff5872", "#7fea00", "#00d5e9", "#ffe700"]) // #ccc
Insert cell
Insert cell
yAxis = d3.axisLeft(yScale).ticks(4) //.ticks(5)
Insert cell
Insert cell
xAxis = d3
.axisBottom(xScale)
.tickFormat(d3.format("~s")) //숫자를 간결하게 표현하기 위한 포매팅 방식 (K, M)
.tickValues([500, 1000, 2000, 4000, 8000, 16000, 32000, 64000])
Insert cell
Insert cell
yScale = d3
.scaleLinear()
.domain(d3.extent(data, (d) => d["Life expectancy "]))
.range([height - margin.bottom, margin.top])
Insert cell
xScale = d3
.scaleLog()
// .domain(d3.extent(data, (d) => d["Income per person"]))
.domain([500, d3.max(data, (d) => d["Income per person"])])
.range([margin.left, width - margin.right])
Insert cell
radiusScale = d3
.scaleSqrt()
.domain([0, d3.max(data, (d) => d.Population)])
.range([0, 55])
Insert cell
margin = ({ top: 30, right: 30, bottom: 70, left: 60 })
Insert cell
height = 560
Insert cell
Insert cell
Insert cell
regionList = [...new Set(data.map((d) => d.region))]
Insert cell
Insert cell
data = life_expectancy
.map((d) => {
d["Income per person"] = gdp_per_capita.find((g) => g.name === d.name)[
"Income per person"
];
d.Population = population.find((p) => p.name === d.name).Population;
d.region = region.find((r) => r.name === d.name).four_regions;
return d;
})
.sort((a, b) => b.Population - a.Population)
Insert cell
life_expectancy = d3
.csvParse(
await FileAttachment("gapminder_lifeExpectancy.csv").text(),
d3.autoType
)
.filter((d) => d.time === 2022)
Insert cell
gdp_per_capita = d3
.csvParse(
await FileAttachment("gapminder_gdpPerCapita.csv").text(),
d3.autoType
)
.filter((d) => d.time === 2022)
Insert cell
population = d3
.csvParse(
await FileAttachment("gapminder_population.csv").text(),
d3.autoType
)
.filter((d) => d.time === 2022)
Insert cell
region = d3.csvParse(
await FileAttachment("gapminder_region.csv").text(),
d3.autoType
)
Insert cell
Insert cell
html`<style>
.tooltip {
position: absolute;
background-color: rgba(255,255,255,.95);
// padding: 3px 8px;
padding: 0px 4px;
font-size: 22px;
// border: 1px solid #333;
// border-radius: 8px;
border-radius: 2px;
font-family: arial;
font-weight: 500;
line-height: 1.2;
letter-spacing: -.5px;
}

.tick {
font-size: 12.5px;
color: #777;
}
.domain {
// display: none;
stroke: #aaa;
}

.unit {
font-family: arial;
font-weight: bold;
font-size: 15px;
text-anchor: middle;
}

.legend-texts {
font-family: arial;
text-transform: capitalize;
font-size: 14px;
}
</style>`
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