Public
Edited
Aug 3, 2023
2 forks
Insert cell
Insert cell
Insert cell
(d3.extent(data, d => d.Date))
Insert cell
d3.max(data)
Insert cell
d3.min(data)
Insert cell
d3.isoParse("2011-08-24")
Insert cell
viewof time = Inputs.number({value: Date.now()})
Insert cell
{
const today = Date.now()
// const today = new Date(time)
// return `${today.toISOString()}`
return `${d3.isoParse(today)}`
}
Insert cell
d3.now()
Insert cell
Date.now()

Insert cell
luxon = require("luxon@1/build/amd/luxon.js")
Insert cell
DateTime = luxon.DateTime
Insert cell
local = DateTime.local()
Insert cell
local.toLocaleString("en-US")
//local.toLocaleString("en-US", {month: "long", day: "numeric", year: "numeric"})
Insert cell
import {brushFilterX} from "@observablehq/brush-filter-x"
Insert cell
formatDate = d3.timeFormat("%B %d, %Y")
Insert cell
Insert cell
function arealineY(data, {color, fillOpacity = 0.1, ...options} = {}) {
return Plot.marks(
Plot.ruleY([0]),
Plot.areaY(data, {fill: color, fillOpacity, ...options}),
Plot.lineY(data, {stroke: color, ...options})
);
}
Insert cell
arealineY(data,
{x: d => (!years || d.Date >= years[0] && d.Date <= years[1]) ? d.Date : NaN,
y: "Close",
color: "gray"})
.plot({
x: {
label: "Year",
domain: years
},
y: {
grid: true,
label: "Share Price [$]",
domain: [0, d3.max(data, (d) => d.Close)]
},
marks: [
Plot.ruleX(data, Plot.pointerX({x: "Date", py: "Close", stroke: "red"})),
Plot.dot(data, Plot.pointerX({x: "Date", y: "Close", stroke: "red"})),
Plot.text(data, Plot.pointerX({px: "Date", py: "Close", dy: -15, frameAnchor: "top", fontVariant: "tabular-nums", text: (d) => [`Apple's share price closed at $${d.Close.toFixed(2)} on ${Plot.formatIsoDate(d.Date)}`]})),
//https://archive.nytimes.com/www.nytimes.com/library/cyber/week/091797apple.html
Plot.tip(
[`Apple names Steve Jobs interim CEO`],{x: new Date("1997-09-16"), y:0.2, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2006/05/16Apple-Unveils-New-MacBook-Featuring-Intel-Core-Duo-Processors/
Plot.tip(
[`Apple starts selling MacBook`],{x: new Date("2006-05-16"), y:2.32, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2007/06/28iPhone-Premieres-This-Friday-Night-at-Apple-Retail-Stores/
Plot.tip(
[`Apple starts selling iPhone`],{x: new Date("2007-06-29"), y:4.36, dy: -43, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2010/01/27Apple-Launches-iPad/
Plot.tip(
[`Apple starts selling iPad`],{x: new Date("2010-04-03"), y:8.52, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2011/08/24Steve-Jobs-Resigns-as-CEO-of-Apple/
Plot.tip(
[`Tim Cook suceeds Steve Jobs as Apple's CEO`],{x: new Date("2011-08-24"), y:13.44, dy: -43, dx: 10, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2015/03/09Apple-Watch-Available-in-Nine-Countries-on-April-24/
Plot.tip(
[`Apple starts selling Apple Watch`],{x: new Date("2015-04-24"), y:32.57, dy: 19, anchor: "top"},
),
Plot.tip(
[`Apple starts selling Apple TV+`],{x: new Date("2019-11-01"), y:63.96, dy: -3, anchor: "bottom"},
)
],
width: 1200
})
Insert cell
Plot.areaY(
data,
{
x: d => (!years || d.Date >= years[0] && d.Date <= years[1]) ? d.Date : NaN,
y: "Close",
fillOpacity: 0.2,
},
).plot({
x: {
label: "Year",
domain: years
} ,
y: {
grid: true,
label: "Share Price [$]",
domain: [0, d3.max(data, (d) => d.Close)]
},
marks: [
Plot.ruleX(data, Plot.pointerX({x: "Date", py: "Close", stroke: "red"})),
Plot.dot(data, Plot.pointerX({x: "Date", y: "Close", stroke: "red"})),
Plot.text(data, Plot.pointerX({px: "Date", py: "Close", dy: -15, frameAnchor: "top", fontVariant: "tabular-nums", text: (d) => [`Apple's share price closed at $${d.Close.toFixed(2)} on ${Plot.formatIsoDate(d.Date)}`]})),
//https://archive.nytimes.com/www.nytimes.com/library/cyber/week/091797apple.html
Plot.tip(
[`Apple names Steve Jobs interim CEO`],{x: new Date("1997-09-16"), y:0.2, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2006/05/16Apple-Unveils-New-MacBook-Featuring-Intel-Core-Duo-Processors/
Plot.tip(
[`Apple starts selling MacBook`],{x: new Date("2006-05-16"), y:2.32, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2007/06/28iPhone-Premieres-This-Friday-Night-at-Apple-Retail-Stores/
Plot.tip(
[`Apple starts selling iPhone`],{x: new Date("2007-06-29"), y:4.36, dy: -43, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2010/01/27Apple-Launches-iPad/
Plot.tip(
[`Apple starts selling iPad`],{x: new Date("2010-04-03"), y:8.52, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2011/08/24Steve-Jobs-Resigns-as-CEO-of-Apple/
Plot.tip(
[`Tim Cook suceeds Steve Jobs as Apple's CEO`],{x: new Date("2011-08-24"), y:13.44, dy: -33, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2015/03/09Apple-Watch-Available-in-Nine-Countries-on-April-24/
Plot.tip(
[`Apple starts selling Apple Watch`],{x: new Date("2015-04-24"), y:32.57, dy: 3, anchor: "top"},
),
Plot.tip(
[`Apple starts selling Apple TV+`],{x: new Date("2019-11-01"), y:63.96, dy: -3, anchor: "bottom"},
)
],
width: 1200
})
Insert cell
Plot.areaY(
data,
{
x: d => (!years || d.Date >= years[0] && d.Date <= years[1]) ? d.Date : NaN,
y: "Close",
fillOpacity: 0.1
},
).plot({
x: {
label: "Year",
domain: years
} ,
y: {
grid: true,
label: "Share Price [$]",
domain: [0, d3.max(data, (d) => d.Close)]
},
marks: [
Plot.ruleX(data, Plot.pointerX({x: "Date", py: "Close", stroke: "red"})),
Plot.dot(data, Plot.pointerX({x: "Date", y: "Close", stroke: "red"})),
Plot.text(data, Plot.pointerX({px: "Date", py: "Close", dy: -15, frameAnchor: "top", fontVariant: "tabular-nums", text: (d) => [`Apple's share price closed at $${d.Close.toFixed(2)} on ${Plot.formatIsoDate(d.Date)}`]})),
//https://archive.nytimes.com/www.nytimes.com/library/cyber/week/091797apple.html
Plot.tip(
[`Apple names Steve Jobs interim CEO`],{x: new Date("1997-09-16"), y:0.2, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2006/05/16Apple-Unveils-New-MacBook-Featuring-Intel-Core-Duo-Processors/
Plot.tip(
[`Apple starts selling MacBook`],{x: new Date("2006-05-16"), y:2.32, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2007/06/28iPhone-Premieres-This-Friday-Night-at-Apple-Retail-Stores/
Plot.tip(
[`Apple starts selling iPhone`],{x: new Date("2007-06-29"), y:4.36, dy: -43, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2010/01/27Apple-Launches-iPad/
Plot.tip(
[`Apple starts selling iPad`],{x: new Date("2010-04-03"), y:8.52, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2011/08/24Steve-Jobs-Resigns-as-CEO-of-Apple/
Plot.tip(
[`Tim Cook suceeds Steve Jobs as Apple's CEO`],{x: new Date("2011-08-24"), y:13.44, dy: -33, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2015/03/09Apple-Watch-Available-in-Nine-Countries-on-April-24/
Plot.tip(
[`Apple starts selling Apple Watch`],{x: new Date("2015-04-24"), y:32.57, dy: 3, anchor: "top"},
),
Plot.tip(
[`Apple starts selling Apple TV+`],{x: new Date("2019-11-01"), y:63.96, dy: -3, anchor: "bottom"},
)
],
width: 1200
})
Insert cell
Plot.plot({
width: 1000,
caption: "AAPL",
y: {grid: true, label: "Share Price [$]"},
x: {label: "Year"},
marks: [
Plot.areaY(data, {x: "Date", y: "Close", fillOpacity: 0.1}),
Plot.lineY(data, {x: "Date", y: "Close"}),
Plot.ruleX(data, Plot.pointerX({x: "Date", py: "Close", stroke: "red"})),
Plot.dot(data, Plot.pointerX({x: "Date", y: "Close", stroke: "red"})),
Plot.text(data, Plot.pointerX({px: "Date", py: "Close", dy: -10, frameAnchor: "top", fontVariant: "tabular-nums", text: (d) => [`Date: ${Plot.formatIsoDate(d.Date)}`, `Close: $${d.Close.toFixed(2)}`].join(" ")})),
//https://archive.nytimes.com/www.nytimes.com/library/cyber/week/091797apple.html
Plot.tip(
[`Apple names Steve Jobs interim CEO`],{x: new Date("1997-09-16"), y:0.2, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2007/06/28iPhone-Premieres-This-Friday-Night-at-Apple-Retail-Stores/
Plot.tip(
[`Apple starts selling the iPhone`],{x: new Date("2007-06-29"), y:4.36, dy: -3, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2010/01/27Apple-Launches-iPad/
Plot.tip(
[`Apple starts selling iPad`],{x: new Date("2010-04-03"), y:8.52, dy: -33, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2011/08/24Steve-Jobs-Resigns-as-CEO-of-Apple/
Plot.tip(
[`Tim Cook suceeds Steve Jobs as Apple's CEO`],{x: new Date("2011-08-24"), y:13.44, dy: -63, anchor: "bottom"},
),
//https://www.apple.com/newsroom/2015/03/09Apple-Watch-Available-in-Nine-Countries-on-April-24/
Plot.tip(
[`Apple starts selling Apple Watch`],{x: new Date("2015-04-24"), y:32.57, dy: 23, anchor: "top"},
),
]
})
Insert cell
Plot.plot({
width: 1000,
y: {grid: true, label: "Price [$]"},
marks: [
Plot.areaY(data, {x: "Date", y: "Close", fillOpacity: 0.1}),
Plot.line(data, {x: "Date", y: "Close"}),
Plot.tip(data, Plot.pointerX({x: "Date", y: "Close"}))
]
})
Insert cell
Plot.plot({
width: 1000,
y: {grid: true, label: "Price [$]"},
marks: [
Plot.areaY(data, {x: "Date", y: "Close", fillOpacity: 0.1}),
Plot.line(data, {x: "Date", y: "Close", tip: true})
]
})
Insert cell
Plot.plot({
width: 1000,
y: {grid: true, label: "Price [$]"},
marks: [
Plot.line(data, {x: "Date", y: "Close", tip: true})
]
})
Insert cell
Plot.area(data.map((d) => [d.Date, d.Close])).plot()
Insert cell
width
Insert cell
Insert cell
Insert cell
chart = {
//declare the chart dimensions and margins
const width = 1000;
const height = 300;
const margin = {top: 10, right: 30, bottom: 30, left: 30};

//declare the x-axis
const x = d3.scaleUtc()
.domain(d3.extent(data, d => d.Date))
.range([margin.left, width - margin.right]);

//declare the y-axis
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.Close)])
.range([height - margin.bottom, margin.top]);
//create the SVG container
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: intrinsic; font: 10px sans-serif;")
.style("overflow","visible")
.on("pointerenter pointermove", pointermoved)
.on("pointerleave", pointerleft)
.on("touchstart", event => event.preventDefault());

//add the x-axis
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x))

//add the y-axis, remove the domain line, add grid lines, add label
svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margin.left - margin.right)
.attr("stroke-opacity", 0.1))
.call(g => g.append("text")
.attr("x", -margin.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Price ($)"));
//append a path for the line
svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(d => x(d.Date))
.y(d => y(d.Close))
(data));

// Create the tooltip container.
const tooltip = svg.append("g");

function formatValue(value) {
return value.toLocaleString("en", {
style: "currency",
currency: "USD"
});
}
function formatDate(date) {
return date.toLocaleString("en", {
month: "long",
day: "numeric",
year: "numeric",
timeZone: "UTC"
});
}
// Add the event listeners that show or hide the tooltip.
const bisect = d3.bisector(d => d.Date).center;
function pointermoved(event) {
const i = bisect(data, x.invert(d3.pointer(event)[0]));
tooltip.style("display", null);
tooltip.attr("transform", `translate(${x(data[i].Date)},${y(data[i].Close)})`);

const path = tooltip.selectAll("path")
.data([,])
.join("path")
.attr("fill", "white")
.attr("stroke", "black");

const text = tooltip.selectAll("text")
.data([,])
.join("text")
.call(text => text
.selectAll("tspan")
.data([formatDate(data[i].Date), formatValue(data[i].Close)])
.join("tspan")
.attr("x", 0)
.attr("y", (_, i) => `${i * 1}em`)
.attr("font-weight", (_, i) => i ? null : "bold")
.text(d => d));

size(text, path);
}

function pointerleft() {
tooltip.style("display", "none");
}

// Wraps the text with a callout path of the correct size, as measured in the page.
function size(text, path) {
const {x, y, width: w, height: h} = text.node().getBBox();
text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}

return svg.node();
}
Insert cell
chart1 = {
//declare the chart dimensions and margins
const width = 1000;
const height = 300;
const margin = {top: 10, right: 30, bottom: 30, left: 30};

//declare the x-axis
const x = d3.scaleUtc()
.domain(d3.extent(data, d => d.Date))
.range([margin.left, width - margin.right]);

//declare the y-axis
const y = d3.scaleLinear()
.domain([0, d3.max(data, d => d.Close)])
.range([height - margin.bottom, margin.top]);
//create the SVG container
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("style", "max-width: 100%; height: intrinsic; font: 10px sans-serif;")
.style("overflow","visible")
.on("pointerenter pointermove", pointermoved)
.on("pointerleave", pointerleft)
.on("touchstart", event => event.preventDefault());

//add the x-axis
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x))

//add the y-axis, remove the domain line, add grid lines, add label
svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y))
.call(g => g.select(".domain").remove())
.call(g => g.selectAll(".tick line").clone()
.attr("x2", width - margin.left - margin.right)
.attr("stroke-opacity", 0.1))
.call(g => g.append("text")
.attr("x", -margin.left)
.attr("y", 10)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.text("Price ($)"));
//append a path for the line
svg.append("path")
.attr("fill", "none")
.attr("stroke", "black")
.attr("stroke-width", 1.5)
.attr("d", d3.line()
.x(d => x(d.Date))
.y(d => y(d.Close))
(data));

// Create the tooltip container.
const tooltip = svg.append("g");

function formatValue(value) {
return value.toLocaleString("en", {
style: "currency",
currency: "USD"
});
}
function formatDate(date) {
return date.toLocaleString("en", {
month: "long",
day: "numeric",
year: "numeric",
timeZone: "UTC"
});
}
// Add the event listeners that show or hide the tooltip.
const bisect = d3.bisector(d => d.Date).center;
function pointermoved(event) {
const i = bisect(data, x.invert(d3.pointer(event)[0]));
tooltip.style("display", null);
tooltip.attr("transform", `translate(${x(data[i].Date)},${y(data[i].Close)})`);

const path = tooltip.selectAll("path")
.data([,])
.join("path")
.attr("fill", "white")
.attr("stroke", "black");

const text = tooltip.selectAll("text")
.data([,])
.join("text")
.call(text => text
.selectAll("tspan")
.data([formatDate(data[i].Date), formatValue(data[i].Close)])
.join("tspan")
.attr("x", 0)
.attr("y", (_, i) => `${i * 1}em`)
.attr("font-weight", (_, i) => i ? null : "bold")
.text(d => d));

size(text, path);
}

function pointerleft() {
tooltip.style("display", "none");
}

// Wraps the text with a callout path of the correct size, as measured in the page.
function size(text, path) {
const {x, y, width: w, height: h} = text.node().getBBox();
text.attr("transform", `translate(${-w / 2},${15 - y})`);
path.attr("d", `M${-w / 2 - 10},5H-5l5,-5l5,5H${w / 2 + 10}v${h + 20}h-${w + 20}z`);
}

return svg.node();
}
Insert cell
data
Insert cell
data[data.length-1].Date
Insert cell

data[data.length-1].Close
Insert cell
data[data.length-1]
Insert cell
data = d3.csv(getCsvUrl(url), d3.autoType)
Insert cell
SummaryTable(data)
Insert cell
Insert cell
// Converts a Google Sheets website URL to its CSV URL. You can also go to “File → Publish to web”, select the “Comma-separated values (.csv)” type, select the sheet with your data, and use that CSV URL directly with `d3.csv` above. If you need data to update faster, try the Google Sheets API: https://stackoverflow.com/questions/30082277/accessing-a-new-style-public-google-sheet-as-json/44479726#44479726

getCsvUrl = url => {
url = new URL(url);
const id = url.pathname.split("/")[3]
const gid = new URLSearchParams(url.hash.slice(1)).get("gid") || 0;
return `https://docs.google.com/spreadsheets/d/${id}/export?format=csv&gid=${gid}`
}
Insert cell
import { SummaryTable } from "@observablehq/summary-table"
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