Public
Edited
Jul 10, 2024
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
interests = [1, 2, 3, 5, 6, 8, 12] // number of interest lines plotted
Insert cell
length = 75 // number of years to show (Plot.x.domain)
Insert cell
domain = ({
x: [0, length],
y: [0, 25],
})
Insert cell
Insert cell
Insert cell
// Factor[] for all years and all interests
viewof factors = tabler(factorForYear(domain.x[1]))(interests)
Insert cell
// Factor[] for interest line labels
viewof lineLabelPoints =
{
// move inward from frame; values are in user space
const xMax = domain.x[1] - 3;
const yMax = domain.y[1] - 2;
const x = yearsToReachFactor(yMax);
const y = futureValue(xMax);
return Inputs.table(
interests.map(
interest => ({
interest,
factor: Math.min(yMax, y(interest)),
year: Math.min(xMax, x(interest)),
})
),
tableFormat
)
}
Insert cell
// Factor[] for all interests where factor === growthFactor
viewof yearsForFactor = tabler
(yearForFactor(factor)(yearsToReachFactor(factor)))
(interests)
Insert cell
// takes factor, returns function that
// takes yearFactor, returns function that
// takes interest
// returns corresponding Factor with corresponding year
yearForFactor =
(factor) =>
(yearFactor) =>
(interest) =>
({
factor,
interest,
year: yearFactor(interest),
})
Insert cell
yearForFactor(3)(yearsToReachFactor(3))(2)
Insert cell
yearsToReachFactor(3)(2)
Insert cell
compoundInterestPlot = (domain, factors, yearsForFactor, lineLabelPoints) => {
const percentage = d => d.interest + "%";
return Plot.plot({
title: "Compound interest’s exponential growth curves",
subtitle: "acts like cancer in our social structure, hence self-destructs if untreated",
grid: true,
legend: true,
// clip: true,
x: {
domain: domain.x,
},
y: {
domain: domain.y,
ticks: 5,
},
marks: [
Plot.line(
factors,
{
x: "year",
y: "factor",
stroke: "interest",
}
),
Plot.dot(
yearsForFactor,
{
x: "year",
y: "factor",
r: 3,
fill: "red"
}
),
Plot.dot(
lineLabelPoints,
{
x: "year",
y: "factor",
z: "interest",
fill: "white",
r: 9
}
),
Plot.text(
lineLabelPoints,
{
x: "year",
y: "factor",
z: "interest",
text: percentage,
fontWeight: "bold",
}
),
Plot.tip(
factors,
Plot.pointerX({x: "year", y: "factor"})
),
],
// make plot size multiples of 36
// make margins 1 x 36
// therefore: frame = 16*36 x 10*36: resulting in a square grid
width: 18*36,
height: 12*36,
marginTop: 36,
marginBottom: 36,
marginLeft: 36,
marginRight: 36,
})
}
Insert cell
// takes number of years interest accrues, returns function that
// takes interest percentage
// returns Factor[] for each of the years
factorForYear =
(length) =>
(interest) =>
Array.from(
{length},
(_, year) => futureFactor(year)(interest)
)
Insert cell
// take interest percentage, returns function that
// takes year
// returns Factor for given interest and year
futureFactor =
(year) =>
(interest) =>
({
year,
interest,
factor: futureValue(year)(interest)
})
Insert cell
futureValue =
(year) =>
(interest) =>
Math.pow(1 + interest / 100, year)
Insert cell
futureFactor(35)(2) // 2% interests leads to double the principle in 35 years
Insert cell
// takes factor, returns function that
// takes interest
// returns year in which interest leads to exactly factor
// e.g. yearsToReachFactor(2)(3) → 35
yearsToReachFactor =
(factor) =>
(interest) =>
Math.log(factor) / Math.log(1 + interest / 100)
Insert cell
// 2% interests leads to double the principle in 35 years
// given a 2% compound interest rate,
// in which year is the Future Value twice the Present Value
yearsToReachFactor
(2) // doubled
(2) // interest rate in percent
// → ~35
Insert cell
tabler = table(tableFormat)
Insert cell
tableFormat = {
const ƒ = (d) => d.toFixed(2);
return ({width: 320, format: {year: ƒ, factor: ƒ}});
}
Insert cell
table =
(format) =>
(ƒ) =>
(list) =>
Inputs.table(list.flatMap(ƒ), format)
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more