Public
Edited
Jan 23, 2024
Importers
Insert cell
Insert cell
data = d3.tsvParse(`Sector PBI
Agricultura 600
Servicio 300
Mineria 1000
Comercio 500
`)
Insert cell
pieChart(
data,
"Sector",
"PBI",
"interpolateViridis",
"Madre de Dios: Distribución del PBI real, 2022",
"Fuente: INEI"
)
Insert cell
Insert cell
bigPercentage(72)
Insert cell
bigPercentage = (pct, c) => {
const width = 130;
const height = width;
const color = c ? c : "steelblue";
const radius = Math.min(width, height) / 2;
let arc = d3.arc().innerRadius(radius * 0.85).outerRadius(radius - 1).cornerRadius(4);

const pie = d3.pie()
.padAngle(0)
.sort(null)
.value(d => d.value)

const arcs = pie([
{value: pct, fg: true},
{value: (100 - pct), fg: false}
]);
const div = d3.create("div")
.style("width", width + "px")
.style("position", "relative");
//Pie
const svg = div.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height]);

svg.selectAll("path")
.data(arcs)
.join("path")
.attr("fill", d => d.data.fg ? color : "rgba(0, 0, 0, 0.2)")
.attr("stroke", "white")
.attr("d", arc)

svg.append("g")
.attr("font-family", "var(--sans-serif)")
.attr("font-size", 12)
.attr("text-anchor", "middle")
.selectAll("text")
.data(arcs);

const bigNumber = div.append("div")
.style("position", "absolute")
.style("display", "flex")
.style("align-items", "center")
.style("top", "0")
.style("left", "0")
.style("text-align", "center")
.style("font-size", "36px")
.style("font-weight", "bold")
.style("width", width + "px")
.style("height", height + "px")
.style("color", color)
.append("div")
.style("width", width + "px")
.text(d3.format(".0%")(pct / 100))
return div.node();
}
Insert cell
bigPercentageWithDescription(87, html`hello <b>world</b> this is a short description about the number`)
Insert cell
bigPercentageWithDescription = function(value, description) {
return html`
<style>

.big-percentage-with-description .description {
font-size: 20px;
line-height: 28px;
max-width: 330px;
font-style: italic;
color: #666;
}
.big-percentage-with-description .big-circle {
width: 100px;
}
.big-percentage-with-description .description b {
color: #333
}
@media only screen and (min-width: 450px) {
.big-percentage-with-description .description {
font-size: 24px;
line-height: 34px;
}
.big-percentage-with-description .big-circle {
width: 130px;
}
}
</style>
<div class="big-percentage-with-description" style="max-width: 640px;">
<div style="display: flex; align-items: center; gap: 40px;">
<div class="big-circle">
${bigPercentage(value, "steelblue")}
</div>
<div class="description" style="">
${description}
</div>
</div>
</div>`
}
Insert cell
yearsOfExperience = d3.tsvParse(`Years of Experience H2 '20 H1 '21
Less than 1 year 20.1 19.7
1 – 3 years 25.0 25.8
3 – 5 years 19.8 20.2
More than 5 years 35.2 34.3`, d3.autoType)
Insert cell
surveyCompareBars(yearsOfExperience, "Years of Experience", ["H2 '20", "H1 '21"], ["H2 '20", "H1 '21"], "During each survey, 20 percent of the practitioners had just entered the field.")
Insert cell
surveyCompareBars = function(data, labelCol, rowCols, rowLabels, annotation ) {
const pctFormat = d3.format(".0%")
const nested = rowCols.map((col, ci) => {
const label = rowLabels[ci];
const total = d3.sum(data, d => d[col])
const values = data.map(d => ({
label: d[labelCol],
value: d[col],
ratio: d[col] / total
}))
return {
label, data: values
}
});

const gridColumns = width < 500 ? "75px 1fr" : "160px 1fr"
const color = d3.scaleOrdinal()
.domain(d3.range(data.length))
.range(d3.quantize(t => d3.interpolateBlues((1 - t) * 0.6 + 0.4), data.length));
const container = d3.create("div")
.style("display", "flex")
.style("max-width", "640px")
.style("flex-direction", "column")
.style("gap", "4px")
.style("font-family", "var(--sans-serif)")
.style("font-size", "13px")
.style("line-height", "13px")

const header = container.append("div")
.style("display", "grid")
.style("grid-template-columns", gridColumns)
.style("gap", "20px")
header.append("div")
const barsHeader = header.append("div")
.style("display", "flex")
.style("align-items", "flex-end")

barsHeader.selectAll(".bars-header")
.data(nested[0].data).join("div")
.attr("class", "bars-header")
.style("width", d => d.ratio * 100 + "%")
.style("border-left", "solid 1px #ddd")
.append("div")
.style("margin", "0px 0 6px 6px")
.text(d => d.label)
const question = container.selectAll(".question")
.data(nested).join("div")
.attr("class", "question")
.style("display", "grid")
.style("grid-template-columns", gridColumns)
.style("gap", "20px")
.style("align-items", "center")
question.append("div")
.style("text-overflow", "ellipsis")
.style("white-space", "nowrap")
.style("overflow", "hidden")
.text(d => d.label)
const bars = question.append("div")
.style("display", "flex")
.style("border-radius", "5px")
.style("align-items", "center")
.style("overflow", "hidden")
const bar = bars.selectAll("div")
.data(d => d.data).join("div")
.style("background", (d, i) => color(i))
.style("border-left", "solid 1px rgba(255, 255, 255, 0.6)")
.style("width", d => d.ratio * 100 + "%")
.style("color", "rgba(255, 255, 255, 1.0)")
.style("line-height", "28px")
.style("font-weight", "bold")
.style("text-overflow", "ellipsis")
.style("white-space", "nowrap")
.style("overflow", "hidden")
.style("padding", "0 0 0 8px")
.style("box-sizing", "border-box")
.text(d => pctFormat(d.ratio))

const annotations = container.append("div")
.style("display", "grid")
.style("font-family", "var(--serif)")
.style("font-size", "14px")
.style("line-height", "1.4em")
.style("color", "#666")
.style("grid-template-columns", gridColumns)
.style("gap", "20px")

annotations.append("div")
const a = annotations.append("div").style("display", "flex");

const swoopy = a.append("svg")
.attr("width", 30)
.attr("height", 35)
.style("margin", "4px 4px")

swoopy.append("path")
.attr("stroke", "#aaa")
.attr("fill", "none")
.attr("d", `
M 25 18 C 10 18, 10 10, 10 0
M 6 4 L 10 0 L 14 4
`)

a.append("div")
.style("max-width", "250px")
.style("font-style", "italic")
.style("line-height", "1.5em")
.style("margin", "10px 0 0 0")
.text(annotation)
return container.node()
}
Insert cell
collaborationFrequency = d3.tsvParse(`Frequency pct
0-25% of the time 58.7
25-50% of the time testing a long answer for wrapping 24.5
50-75% of the time 7
75-100% of the time 9.7`, d3.autoType)
Insert cell
surveyHistograms(collaborationFrequency, "Frequency", "pct", true, true)
Insert cell
surveyHistograms = (data, labelCol, valueCol, sort, highlight, labelWidth) => {
const margin = ({top: 2, right: 0, bottom: 10, left: 150});

labelWidth = labelWidth ? labelWidth : 140;
const pctFormat = d3.format(".0%")
const commaFormat = d3.format(",")
const rowHeight = 14;
const barHeight = 14;
const rowPadding = 4;

const numResponses = d3.sum(data, d => d[valueCol]);
const maxValue = d3.max(data, d=> d[valueCol]);
const gap = 20
const h = Math.ceil((numResponses + 0.2) * rowHeight) + margin.top + margin.bottom
const w = width / 2 - gap;

if (sort) {
data = data.slice().sort((a, b) => d3.descending(a[valueCol], b[valueCol]))
}
const x = d3.scaleLinear()
.domain([0, 70])
.range([0, 100])
const y = d3.scaleBand()
.domain(d3.range(numResponses))
.rangeRound([margin.top, h - margin.bottom])
.padding(0.2)
const chart = d3.create("div")
.style("display", "grid")
.style("max-width", "640px")
.style("grid-template-columns", "1fr")
.style("gap", `0 ${gap}px`)
.style("padding-bottom", "16px")
const question = chart.append("div")
.style("font-family", "var(--sans-serif)")
.style("font-size", "13px")
.style("line-height", "1.2em")
.attr("class", "question")

const highlightComparator = (d, i) => {
if (Array.isArray(highlight)) {
return highlight.includes(i)
} else {
return d[valueCol] === maxValue && highlight
}
}
const row = question.selectAll(".row")
.data(data)
.join("div")
.attr("class", "row")
.style("align-items", "flex-start")
.style("padding-top", `${rowPadding}px`)
.style("margin-top", `${rowPadding}px`)
.style("border-top", (d, i) => i === 0 ? "" : "dotted 1px rgba(0, 0, 0, 0.1)")
.style("display", "flex")
.style("gap", "20px")
.style("font-weight", (d, i) => highlightComparator(d, i) ? "600" : "normal")
row.append("div")
.text(d => d[labelCol])
.style("color", d => d[labelCol])
.style("width", labelWidth ? labelWidth + "px" : "220px")
row.append("div")
.text(d => pctFormat(d[valueCol] / 100))
.style("width", "40px")
.style("text-align", "right")
row.append("div")
.attr("class", "bar")
.style("flex", "1 0")
.append("div")
.style("border-radius", "2px")
.style("background", "rgb(70 130 180)")
.style("opacity", (d, i) => highlightComparator(d, i) || !highlight ? 1 : 0.5)
.style("height", `${barHeight}px`)
.style("width", d => x(d[valueCol]) + "%")
return chart.node();
}
Insert cell
Insert cell
longList(tools, "unique tools reported, ordered by popularity")
Insert cell
longList = function(tools, description) {
const categories = [...new Set(tools.map(tool => tool.category))].map(id => ({id, label: id}))
return html`
<div>
<style>
.tools-container {
font-family: var(--sans-serif);
font-size: 12px;
display: flex;
align-items: flex-start;
gap: 20px;
padding: 30px 0 20px 0;
}
.tools-container .sidebar {
width: 110px;
flex: 1 0 auto;
}
@media only screen and (min-width: 600px) {
.tools-container .sidebar {
width: 160px;
}
}
.tools-container .big-number {
font-family: "Source Serif Pro";
font-size: 16px;
line-height: 1.5em;
}
.tools-container .big-number b {
display: block;
font-size: 48px;
line-height: 48px;
margin-bottom: 4px;
}
.tools-container .color-key {
border-top: solid 1px rgba(0, 0, 0, 0.08);
margin-top: 20px;
padding-top: 20px;
width: 100%
}
.tools-container .color {
color: #666;
display: flex;
gap: 8px;
margin-top: 2px;
display: flex;
align-items: center;
}
.tools-container .swatch {
width: 12px;
height: 12px;
border-radius: 4px;
}
.tools-container .tools {
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.tools-container .tool {
border-radius: 4px;
padding: 2px 4px;
}
.tools-container .category-Analytics {
background-color: MistyRose;
}
.tools-container .category-Business {
background-color: SeaShell;
}
.tools-container .category-Collaboration {
background-color: PapayaWhip;
}
.tools-container .category-Communication {
background-color: Beige;
}
.tools-container .category-Data {
background-color: hsl(157deg 47% 93%);
}
.tools-container .category-Design {
background-color: AliceBlue;
}
.tools-container .category-Developer {
background-color: Lavender;
}
.tools-container .category-Other {
background-color: WhiteSmoke;
}
</style>
<div class="tools-container">
<div class="sidebar">
<div class="big-number"><b>${tools.length}</b> ${description}</div>
<div class="color-key">
<div>
${categories.sort((a, b) => d3.ascending(a.label, b.label)).map(category => html`<div class="color">
<div class="swatch category-${category.id}"></div> ${category.label}
</div>`)}
</div>
</div>
</div>
<div class="tools">
${tools.sort().map(tool => html`<span class="tool category-${tool.category}">${tool.tool}</span>`)}
</div>
</div>
`
}
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