diff options
author | William Carroll <wpcarro@gmail.com> | 2023-01-06T18·16-0800 |
---|---|---|
committer | wpcarro <wpcarro@gmail.com> | 2023-01-08T22·49+0000 |
commit | 899828adeb68bad5bf528bc3319ab2a66a2e5553 (patch) | |
tree | 2d83ab43f48a31f7a6e6a38e36f92db59d71e859 /users/wpcarro/ynabsql/dataviz/index.js | |
parent | 1d59d3ba8f0f77d33e246984447c0ae5b3889316 (diff) |
feat(wpcarro/ynab): Proof-of-concept viz for finances r/5630
After experimenting with existing "data engineering solutions" like datasette, periscope, I think rolling my own dataviz for this project might be easiest (surprisingly). **Wish List:** - Benthos job to dump my financial transactions into a SQL table. - Scatter plot of expenses (or just transactions generally). - Support filtering the data using "Simple Select" query language. - Stacked histogram of income/expenses with a line overlaying the "idealized" savings. Change-Id: Iec2948641dba8c4c6d5ad19a0e1ea142b81198af Reviewed-on: https://cl.tvl.fyi/c/depot/+/7784 Tested-by: BuildkiteCI Reviewed-by: wpcarro <wpcarro@gmail.com>
Diffstat (limited to 'users/wpcarro/ynabsql/dataviz/index.js')
-rw-r--r-- | users/wpcarro/ynabsql/dataviz/index.js | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/users/wpcarro/ynabsql/dataviz/index.js b/users/wpcarro/ynabsql/dataviz/index.js new file mode 100644 index 000000000000..cc58591d7ff8 --- /dev/null +++ b/users/wpcarro/ynabsql/dataviz/index.js @@ -0,0 +1,72 @@ +const colors = { + red: 'rgb(255, 99, 132)', + orange: 'rgb(255, 159, 64)', + yellow: 'rgb(255, 205, 86)', + green: 'rgb(75, 192, 192)', + blue: 'rgb(54, 162, 235)', + purple: 'rgb(153, 102, 255)', + grey: 'rgb(201, 203, 207)' +}; + +function randomExpense() { + // 10/1000 expenses are O(1,000) + // 100/2000 expenses are O(100) + // 1,000/2000 expenses are O(10) + // 10,000/2000 expenses are O(1) + const r = Math.random(); + + if (r <= 0.02) { + return Math.floor(Math.random() * 5000); + } else if (r <= 0.1) { + return Math.floor(Math.random() * 1000); + } else if (r <= 0.5) { + return Math.floor(Math.random() * 100); + } else { + return Math.floor(Math.random() * 10); + } +} + +// Browser starts to choke around 10,000 data points. +function generateData() { + return Array(2000).fill(0).map(x => ({ + // select a random day [0, 365] + x: Math.floor(Math.random() * 365), + // select a random USD amount in the range [1, 5,000] + y: randomExpense(), + // TODO(wpcarro): Attach transaction to `metadata` key. + metadata: { foo: 'bar' }, + })); +} + +//////////////////////////////////////////////////////////////////////////////// +// Main +//////////////////////////////////////////////////////////////////////////////// + +const mount = document.getElementById('mount'); + +new Chart(mount, { + type: 'scatter', + data: { + datasets: [ + { + label: 'Expenses', + data: generateData(), + backgroundColor: colors.red, + } + ], + }, + options: { + plugins: { + tooltip: { + callbacks: { + title: function (x) { + return `$${x[0].raw.y}`; + }, + label: function (x) { + return JSON.stringify(x.raw.metadata); + }, + }, + }, + }, + }, +}) |