Public
Edited
Sep 29, 2023
Insert cell
Insert cell
Insert cell
Insert cell
<p>this is HTML code </p>
<svg width=300 height=200 style="border: 1px solid #ccc">
<path d='M0,0L300,200'
style="stroke: #000">
</path>
</svg>
Insert cell
<p>not a best practice to put contents out of the svg</p>
<svg width=300 height=200 style="overflow:visible; border: 1px solid #ccc">
<path d='M 300,200 L 400,200'
style="stroke: #000">
</path>
</svg>
Insert cell
<svg width=300 height=200 style="border: 1px solid #ccc">
<path d='M 150, 0 L 300, 200 L 0, 200 Z'
style="fill: steelblue;">
</path>
</svg>
Insert cell
<svg width=300 height=200 style="border: 1px solid #ccc">
<path d='M 150, 0 L 300, 200 L 0, 200 L 150,0'
style="fill: none; stroke:#000">
</path>
</svg>
Insert cell
Insert cell
Insert cell
Insert cell
myData = [0, 10, 5, 30]
Insert cell
{
const width = 500;
const height = 100;
//margin on bottom bigger to give space for axis
const margin = { top: 10, bottom: 30, left: 10, right: 10 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
//transform and translate for the margins
const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top}`);
return svg.node();
}
Insert cell
{
const width = 500;
const height = 100;
//margin on bottom bigger to give space for axis
const margin = { top: 10, bottom: 30, left: 10, right: 10 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
//transform and translate for the margins
const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top}`);

//add scales
const x = d3.scaleLinear().domain([0, 3]).range([0, width]);
//inverted coordinates that's why we first put the height and then the origin 0
const y = d3.scaleLinear().domain(d3.extent(myData)).range([height, 0]);
//logic for how to generate the line (not drawing the line)
const line = d3
.line()
.x((d, i) => x(i))
.y((d) => y(d));
//by default it fills the path, that's why we get a triangle
g.append("path")
.datum(myData)
.attr("d", line)
;
return svg.node();
}
Insert cell
{
const width = 500;
const height = 100;
//margin on bottom bigger to give space for axis
const margin = { top: 10, bottom: 30, left: 10, right: 10 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
//transform and translate for the margins
const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top}`);

//add scales
const x = d3.scaleLinear().domain([0, 3]).range([0, width]);
//inverted coordinates that's why we first put the height and then the origin 0
const y = d3.scaleLinear().domain(d3.extent(myData)).range([height, 0]);
//logic for how to generate the line (not drawing the line)
const line = d3
.line()
.x((d, i) => x(i))
.y((d) => y(d));
//by default it fills the path, that's why we get a triangle
g.append("path")
.datum(myData)
.attr("d", line)
.style("stroke", "#000")
.style("fill", "none")
;
//we can inspect the page to see the <path d="M0,100L83.333,100L83.333,66.667L250,66.667L250,83.333L416.667,83.333L416.667,0L500,0" class="line" style="stroke: rgb(0, 0, 0); stroke-width: 1; fill: none;"></path>
//if we select this line and go to console and type $0.__data__ we get the array of data that we had initially
//$0.__data__
//(4) [0, 10, 5, 30]
return svg.node();
}
Insert cell
{
const width = 500;
const height = 100;
//margin on bottom bigger to give space for axis
const margin = { top: 10, bottom: 30, left: 10, right: 10 };

const svg = d3
.create("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
//transform and translate for the margins
const g = svg
.append("g")
.attr("transform", `translate(${margin.left}, ${margin.top}`);

//add scales
const x = d3.scaleLinear().domain([0, 3]).range([0, width]);
//inverted coordinates that's why we first put the height and then the origin 0
const y = d3.scaleLinear().domain(d3.extent(myData)).range([height, 0]);
//logic for how to generate the line (not drawing the line)
const line = d3
.line()
.x((d, i) => x(i))
.y((d) => y(d));
//another way to do it using data instead of datum. Note that we need to use an array for myData otherwise it will try to create as many paths as elements in the myData
g.selectAll("path")
.data([myData])
.join("path")
.attr("d", line)
.style("stroke", "#000")
.style("fill", "none")
;
return svg.node();
}
Insert cell
{
const width = 500;
const height = 100;
const margin = { top: 10, bottom: 30, left: 10, right: 10 };

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

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

const x = d3.scaleLinear().domain([0, 3]).range([0, width]);

const y = d3.scaleLinear().domain(d3.extent(myData)).range([height, 0]);

const line = d3
.line()
.curve(d3.curveStep)
.x((d, i) => x(i))
.y((d) => y(d));

//showing the 2 different ways to do it, either with data or datum
g.append("path")
.datum(myData)
.attr("d", line)
.style("stroke", "steelblue")
.style("stroke-width", 10)
.style("fill", "none");

g.selectAll(".line")
.data([myData])
.join("path")
.attr("d", line)
.attr("class", "line")
.style("stroke", "#000")
.style("stroke-width", 1)
.style("fill", "none");

return svg.node();
}
Insert cell
{
const width = 500;
const height = 100;
const margin = { top: 10, bottom: 30, left: 10, right: 10 };

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

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

const x = d3.scaleLinear().domain([0, 3]).range([0, width]);

const y = d3.scaleLinear().domain(d3.extent(myData)).range([height, 0]);

const line = d3
.line()
.curve(d3.curveBasis)
.x((d, i) => x(i))
.y((d) => y(d));

//datum = singular
//data = plural
g.append("path")
.datum(myData)
.attr("d", line)
.style("stroke", "steelblue")
.style("stroke-width", 10)
.style("fill", "none");

g.selectAll(".line")
.data([myData])
.join("path")
.attr("d", line)
.attr("class", "line")
.style("stroke", "#000")
.style("stroke-width", 1)
.style("fill", "none");

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const width = 500;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 40;

//time scale scaleUtc()
//nice() rounds the domain min and max, e.g if the max is 99.9 it will put 100
//if we remove it then the axis will have funny values
const x = d3
.scaleUtc()
.domain(d3.extent(appleStockData, (d) => d.Date))
.nice()
.range([0, width]);

const y = d3
.scaleLinear()
.domain(d3.extent(appleStockData, (d) => d.Close))
.nice()
.range([height, 0]);

const svg = d3
.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

//to paint the X axis
//g.append("g")
// .attr("transform", `translate(0,${height})`)
// .call(d3.axisBottom(x).ticks(width / 80));
//to paint the Y axis
// g.append("g").call(d3.axisLeft(y));

g.append("g")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.selectAll("circle")
.data(appleStockData)
.join("circle")
.attr("cx", (d) => x(d.Date))
.attr("cy", (d) => y(d.Close))
.attr("r", 2);


return svg.node();
}
Insert cell
chart2 = {
const width = 500;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 40;

//time scale scaleUtc()
//nice() rounds the domain min and max, e.g if the max is 99.9 it will put 100
//if we remove it then the axis will have funny values
const x = d3
.scaleUtc()
.domain(d3.extent(appleStockData, (d) => d.Date))
.nice()
.range([0, width]);

const y = d3
.scaleLinear()
.domain(d3.extent(appleStockData, (d) => d.Close))
.nice()
.range([height, 0]);

const svg = d3
.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

//to paint the X axis
g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(width / 80));
//to paint the Y axis
g.append("g").call(d3.axisLeft(y));

g.append("g")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.selectAll("circle")
.data(appleStockData)
.join("circle")
.attr("cx", (d) => x(d.Date))
.attr("cy", (d) => y(d.Close))
.attr("r", 2);


return svg.node();
}
Insert cell
chart1 = {
const width = 900;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 40;

const x = d3
.scaleUtc()
.domain(d3.extent(appleStockData, (d) => d.Date))
.nice()
.range([0, width]);

const y = d3
.scaleLinear()
.domain(d3.extent(appleStockData, (d) => d.Close))
.nice()
.range([height, 0]);

const line = d3
.line()
//.curve(d3.curveBasis)
.x((d) => x(d.Date))
.y((d) => y(d.Close));

const svg = d3
.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(width / 80));

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

//.stype is related to css
//.attr is the elements attribute.
g.append("g")
.attr("stroke", "steelblue")
.attr("stroke-width", 1.5)
.attr("fill", "none")
.selectAll("circle")
.data(appleStockData)
.join("circle")
.attr("cx", (d) => x(d.Date))
.attr("cy", (d) => y(d.Close))
.attr("r", 2);

g.append("g")
.append("path")
.datum(appleStockData)
.attr("d", line)
.style("fill", "none")
.style("stroke", "#000")
.style("stroke-width", 1.5);

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
<p> this is also HTML code. A rectangle needs a width and a height, a circle needs a center and a radio.</p>
<p> Note that we need to offset the x of the rectangle to have it in the center and that we start drawing from the top left .</p>
<svg width=300 height=200 style="border: 1px solid #ccc">
<rect x=100 y=100 width=100 height=100
></rect>
</svg>
Insert cell
Insert cell
Insert cell
appeleStockData = appleStockData.filter((d, i) => i < 20)
Insert cell
{
const width = 900;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 40;

const x = d3
.scaleUtc()
.domain(d3.extent(appleStockData, (d) => d.Date))
.nice()
.range([0, width]);

const y = d3
.scaleLinear()
.domain(d3.extent(appleStockData, (d) => d.Close))
.nice()
.range([height, 0]);

const svg = d3
.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(width / 80));

g.append("g").call(d3.axisLeft(y));
//order is important, otherwise things can overlap
g.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(appleStockData)
.join("rect")
.attr("x", (d) => x(d.Date))
.attr("y", (d) => y(d.Close))
.attr("width",2)
.style("opacity", 0.2)
//we need to subrstract from the height because it's painted the other way around
//.attr("height", (d) => y(d.Close))
.attr("height", (d) => height - y(d.Close))
;

g.append("g")
.attr("fill", "#000")
.selectAll("circle")
.data(appleStockData)
.join("circle")
.attr("cx", (d) => x(d.Date))
.attr("cy", (d) => y(d.Close))
.attr("r", 2);

return svg.node();
}
Insert cell
{
const width = 900;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 40;

const x = d3
.scaleUtc()
.domain(d3.extent(appleStockData, (d) => d.Date))
.nice()
.range([0, width]);

//adding bandScale for the width of rectangles because when we give them before a fixed value of 2 they were overlaping
// ordinal scale with the distinct values of dates
const bandScale = d3
.scaleBand()
.domain(Array.from(new Set(appleStockData.map((d) => d.Date))))
.range([0, width]);
const y = d3
.scaleLinear()
.domain(d3.extent(appleStockData, (d) => d.Close))
.nice()
.range([height, 0]);

const svg = d3
.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(width / 80));

g.append("g").call(d3.axisLeft(y));
//order is important, otherwise things can overlap
g.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(appleStockData)
.join("rect")
.attr("x", (d) => x(d.Date))
//it's also possible to use the bandScale for the X
//.attr("x", (d) => bandScale(d.Date))
.attr("y", (d) => y(d.Close))
.attr("width",bandScale.bandwidth)
.style("opacity", 0.2)
//we need to subrstract from the height because it's painted the other way around
//.attr("height", (d) => y(d.Close))
.attr("height", (d) => height - y(d.Close))
;

g.append("g")
.attr("fill", "#000")
.selectAll("circle")
.data(appleStockData)
.join("circle")
//.attr("cx", (d) => bandScale(d.Date))
.attr("cx", (d) => x(d.Date))
.attr("cy", (d) => y(d.Close))
.attr("r", 2);

return svg.node();
}
Insert cell
{
const width = 900;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 40;

const x = d3
.scaleUtc()
.domain(d3.extent(appleStockData, (d) => d.Date))
.nice()
.range([0, width]);

const bandScale = d3
.scaleBand()
.domain(Array.from(new Set(appleStockData.map((d) => d.Date))))
.range([0, width]);

const y = d3
.scaleLinear()
.domain(d3.extent(appleStockData, (d) => d.Close))
.nice()
.range([height, 0]);

const svg = d3
.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg
.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x).ticks(width / 80));

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

g.append("g")
.attr("fill", "steelblue")
.selectAll("rect")
.data(appleStockData)
.join("rect")
.attr("x", (d) => x(d.Date))
.attr("y", (d) => y(d.Close))
.attr("width", bandScale.bandwidth())
.style("opacity", 0.5)
.attr("height", (d) => height - y(d.Close));

g.append("g")
.attr("fill", "#000")
.selectAll("circle")
.data(appleStockData)
.join("circle")
.attr("cx", (d) => x(d.Date))
.attr("cy", (d) => y(d.Close))
.attr("r", 2);

return svg.node();
}
Insert cell
Insert cell
Insert cell
populationData
Insert cell
keys = ["<10", "10-19", "20-29", "30-39", "40-49", "50-59", "60-69", "70-79", "≥80"]
Insert cell
stackLayout = d3.stack()
.keys()
Insert cell
{

const width = 900;
const height = 300;
const marginTop = 25;
const marginRight = 20;
const marginBottom = 35;
const marginLeft = 100;

const x = d3.scaleBand()

const y = d3.scaleLinear()

const color = d3.scaleOrdinal()

const svg = d3.create("svg")
.attr("width", width + marginLeft + marginRight)
.attr("height", height + marginTop + marginBottom);

const g = svg.append("g")
.attr("transform", `translate(${marginLeft}, ${marginTop})`);

g.append("g")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x))
g.append("g")
.call(d3.axisLeft(y))

g.selectAll(".states");

return svg.node();
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
populationData = await FileAttachment("us-population-state-age.csv").csv({typed: true});
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