Public
Edited
Sep 30, 2023
Insert cell
Insert cell
toc({
headers: "h2,h3,h4",
hideStartingFrom: "Appendix"
})
Insert cell
Insert cell
Insert cell
Insert cell
addTooltips(
Plot.plot({
x: { label: "Currency" },
y: { grid: true, nice: true, label: "Currency Value in CNY" },
color: { legend: true },
marks: [
Plot.barY(
data,
Plot.groupX(
{
y: "sum",
title: (group) => currencySum(group, true)
},
{
x: "currency",
y: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "currency",
sort: { x: "-y" }
}
)
),
Plot.textY(
data,
Plot.groupX(
{ y: "sum", text: "sum" },
{
x: "currency",
y: (d) => d.amount * currencyExchangeRate(d.currency),
text: (d) => d.amount * currencyExchangeRate(d.currency),
lineAnchor: "bottom",
dy: -5,
}
)
),
Plot.ruleY([0])
]
})
)
Insert cell
Insert cell
addTooltips(
Plot.plot({
x: { label: "Date" },
y: { grid: true, nice: true, label: "Expense Value in CNY" },
color: { legend: true },
marks: [
Plot.barY(
data,
Plot.groupX(
{
y: "sum",
title: (group) => currencySum(group, true)
},
{
x: "date",
y: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "currency"
}
)
),
Plot.textY(
data,
Plot.groupX(
{ y: "sum", text: "sum" },
{
x: "date",
y: (d) => d.amount * currencyExchangeRate(d.currency),
text: (d) => d.amount * currencyExchangeRate(d.currency),
lineAnchor: "bottom",
dy: -5,
}
)
),
Plot.ruleY([0])
]
})
)
Insert cell
Insert cell
addTooltips(
Plot.plot({
x: { label: "Country" },
y: { grid: true, nice: true, label: "Expense Value in CNY" },
color: { legend: true },
marks: [
Plot.barY(
data,
Plot.groupX(
{
y: "sum",
title: (group) => currencySum(group, true)
},
{
x: "country",
y: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "currency",
sort: { x: "-y" },
filter: (d) => d.subcategory != "Plane"
}
)
),
Plot.textY(
data,
Plot.groupX(
{ y: "sum", text: "sum" },
{
x: "country",
y: (d) => d.amount * currencyExchangeRate(d.currency),
filter: (d) => d.subcategory != "Plane",
text: (d) => d.amount * currencyExchangeRate(d.currency),
lineAnchor: "bottom",
dy: -5
}
)
),
Plot.ruleY([0])
]
})
)
Insert cell
Insert cell
addTooltips(
Plot.plot({
marginTop: 30,
marginBottom: 30,
x: { label: "City", tickFormat: (name) => (name ? name : "None") },
y: { grid: true, nice: true, label: "Expense Value in CNY" },
color: { legend: true },
marks: [
Plot.barY(
data,
Plot.groupX(
{
y: "sum",
title: (group) => currencySum(group, true)
},
{
x: "city",
y: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "currency",
sort: { x: "-y" },
filter: (d) => d.subcategory != "Plane"
}
)
),
Plot.textY(
data,
Plot.groupX(
{ y: "sum", text: "sum" },
{
x: "city",
y: (d) => d.amount * currencyExchangeRate(d.currency),
filter: (d) => d.subcategory != "Plane",
text: (d) => d.amount * currencyExchangeRate(d.currency),
lineAnchor: "bottom",
dy: -5
}
)
),
Plot.ruleY([0])
]
})
)
Insert cell
Insert cell
addTooltips(
Plot.plot({
x: { label: "Category" },
y: { grid: true, nice: true, label: "Expense Value in CNY" },
color: { legend: true },
marks: [
Plot.barY(
data,
Plot.groupX(
{
y: "sum",
title: (group) => currencySum(group, true)
},
{
x: "category",
y: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "currency",
sort: { x: "-y" }
}
)
),
Plot.textY(
data,
Plot.groupX(
{ y: "sum", text: "sum" },
{
x: "category",
y: (d) => d.amount * currencyExchangeRate(d.currency),
text: (d) => d.amount * currencyExchangeRate(d.currency),
lineAnchor: "bottom",
dy: -5
}
)
),
Plot.ruleY([0])
]
})
)
Insert cell
expenseByCategory = Object.entries(Object.groupBy(data, ({ category }) => category))
Insert cell
chart = PieChart(expenseByCategory, {
name: (d) => d[0],
value: (d) => currencySumInCNY(d[1]),
title: (d) => {
let categorySumInCNY = currencySumInCNY(d[1], false);
return `${d[0]}\n${formatToCNY(categorySumInCNY)}\n${(
categorySumInCNY / totalExpenseInCNY
).toLocaleString(locale, {
style: "percent",
minimumFractionDigits: 2
})}`;
}
})
Insert cell
Insert cell
addTooltips(
Plot.plot({
marginLeft: 80,
y: { label: null, tickFormat: (name) => (name ? name : "None") },
x: { grid: true, nice: true, label: "Expense Value in CNY" },
color: { legend: true },
marks: [
Plot.barX(
data,
Plot.groupY(
{
x: "sum",
title: (group) => currencySum(group, true)
},
{
y: "subcategory",
x: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "currency",
sort: { y: "-x" }
}
)
),
Plot.textX(
data,
Plot.groupY(
{ x: "sum", text: "sum" },
{
y: "subcategory",
x: (d) => d.amount * currencyExchangeRate(d.currency),
text: (d) => d.amount * currencyExchangeRate(d.currency),
fill: "black",
textAnchor: "start",
dx: 5
}
)
),
Plot.ruleX([0])
]
})
)
Insert cell
Insert cell
Insert cell
data
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
Insert cell
function currencyExchangeRate(currency) {
let rate;
switch (currency) {
case "USD":
rate = 7.3043
break
case "THB":
rate = 0.1989
break
case "KHR":
rate = 0.0018
break
default:
rate = 1
}
return rate
}
Insert cell
locale = "en-US"
Insert cell
function currencySum(records, format = false) {
let sum = records.map((d) => d.amount).reduce((x, y) => x + y, 0);
return format ? `${records[0].currency} ${sum.toLocaleString(locale)}` : sum;
}
Insert cell
function currencySumInCNY(records, format = false) {
let sum = records
.map((d) => d.amount * currencyExchangeRate(d.currency))
.reduce((x, y) => x + y, 0);
return format ? formatToCNY(sum) : sum;
}
Insert cell
function formatToCNY(amount) {
return amount.toLocaleString(locale, {
style: "currency",
currency: "CNY"
});
}
Insert cell
Select a data source…
Type Table, then Shift-Enter. Ctrl-space for more options.

Insert cell
totalExpenseInCNY = currencySumInCNY(data)
Insert cell
workbook = FileAttachment("泰国—柬埔寨.xlsx").xlsx()
Insert cell
workbook.sheetNames
Insert cell
data = workbook.sheet(0, {
headers: true,
// range: "A1:J10"
})
Insert cell
import { toc } from "@nebrius/indented-toc"
Insert cell
import {addTooltips} from "@mkfreeman/plot-tooltip"
Insert cell
import {PieChart} from "@d3/pie-chart-component"
Insert cell
import {Treemap} from "@d3/treemap-component"
Insert cell
import {Sunburst} from "@d3/sunburst-component"
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