├── requirements.txt ├── Figures ├── Treemap_Expenses.png └── Income_Expenses_over_Time.png ├── README.md └── Notebooks ├── Beancount_Multiperiod_Reports_by_Year.ipynb └── Beancount_Multiperiod_Reports_by_Quarter.ipynb /requirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | jupyterlab 3 | squarify==0.4.3 4 | matplotlib 5 | 6 | -------------------------------------------------------------------------------- /Figures/Treemap_Expenses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isabekov/beancount-multiperiod-reports/HEAD/Figures/Treemap_Expenses.png -------------------------------------------------------------------------------- /Figures/Income_Expenses_over_Time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isabekov/beancount-multiperiod-reports/HEAD/Figures/Income_Expenses_over_Time.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Multiperiod Reports by Month/Quarter/Year in Beancount 2 | If you've ever used [hledger](https://hledger.org/), you might like its ability to produce nice reports. 3 | One of the reports' feature is the table structure, where rows are accounts and columns are weeks, months, quarters or years. 4 | Looking at earnings and spendings as a function of time can give you more insights about your finances. 5 | 6 | However, if you are using [beancount](http://furius.ca/beancount/), this feature is not supported yet in the command line interface. 7 | You need to use [fava](https://github.com/beancount/fava), an awesome web-interface for beancount, which has a graph drawing capability as described in this tutorial. 8 | fava is not ideal and sometimes you might need more custom reports than the ones available in fava. 9 | 10 | This notebook provides methodology and tools to: 11 | - Process BQL query's output using Pandas library 12 | - Generate yearly totals (multiperiod reports by year) by pivoting a table 13 | - Aggregate values at different account levels for the provided account hierarchy 14 | - Draw treemap plots of expenses for all time period 15 | 16 | Details are in [this blog post](https://www.isabekov.pro/multiperiod-hledger-style-reports-in-beancount-pivoting-a-table/). 17 | 18 | ## Installation and Running 19 | 20 | $ git clone https://github.com/isabekov/beancount-multiperiod-reports 21 | $ cd beancount-multiperiod-reports 22 | $ sudo pip install -r requirements.txt 23 | $ jupyter lab 24 | 25 | For example, "quarter" version of the notebook (__Beancount_Multiperiod_Reports_by_Quarter.ipynb__) will do the following operations on an example file: 26 | 27 | ### Executing a BQL Query 28 | 29 | 30 | ```python 31 | cols, rows = run_query(entries, opts, 32 | "SELECT account, YEAR(date) AS year,\ 33 | MONTH(date) as month,\ 34 | SUM(convert(position, '{}', date)) AS amount\ 35 | WHERE account ~ 'Expenses'\ 36 | OR account ~ 'Income'\ 37 | GROUP BY account, year, month\ 38 | ORDER BY account, year, month".format(currency) 39 | ) 40 | ``` 41 | 42 | 43 | ### Converting Result Rows to a Pandas Dataframe 44 | | | Account | YearMonth | Amount (USD) | 45 | |---:|:-------------------------------|:------------|---------------:| 46 | | 0 | Expenses:Financial:Commissions | 2018-10 | 44.75 | 47 | | 1 | Expenses:Financial:Commissions | 2018-11 | 35.8 | 48 | | 2 | Expenses:Financial:Commissions | 2018-12 | 35.8 | 49 | | 3 | Expenses:Financial:Commissions | 2019-05 | 35.8 | 50 | | 4 | Expenses:Financial:Commissions | 2019-06 | 8.95 | 51 | 52 | 53 | ### Pivoting a Table by a Time Interval (e.g. Quarter) 54 | | | Account | 2018-Q1 | 2018-Q2 | 2018-Q3 | 2018-Q4 | 2019-Q1 | 2019-Q2 | 2019-Q3 | 2019-Q4 | 2020-Q1 | 2020-Q2 | 2020-Q3 | 2020-Q4 | 55 | |---:|:-------------------------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| 56 | | 0 | Expenses:Financial:Commissions | 0 | 0 | 0 | 116.35 | 0 | 44.75 | 71.6 | 8.95 | 0 | 98.45 | 62.65 | 71.6 | 57 | | 1 | Expenses:Financial:Fees | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 58 | | 2 | Expenses:Food:Coffee | 0 | 5.49 | 0 | 0 | 0 | 0 | 36.76 | 0 | 0 | 0 | 43.07 | 0 | 59 | | 3 | Expenses:Food:Groceries | 582.97 | 559.27 | 616.3 | 540.3 | 480.78 | 722.67 | 520.4 | 641.2 | 711.49 | 581.02 | 442.03 | 557.04 | 60 | | 4 | Expenses:Food:Restaurant | 948.18 | 948.24 | 1139.92 | 1027.88 | 983.15 | 1127.47 | 1780.95 | 1064.27 | 1109.25 | 1143.04 | 1214.8 | 933.98 | 61 | 62 | 63 | ### Creating Multi-Level Accounts 64 | | | Account_L0 | Account_L1 | Account_L2 | Account_L3 | Account_L4 | Account_L5 | 2018-Q1 | 2018-Q2 | 2018-Q3 | 2018-Q4 | 2019-Q1 | 2019-Q2 | 2019-Q3 | 2019-Q4 | 2020-Q1 | 2020-Q2 | 2020-Q3 | 2020-Q4 | 65 | |---:|:-------------|:-------------|:-------------|:-------------|:-------------|:-------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| 66 | | 0 | Expenses | Financial | Commissions | | | | 0 | 0 | 0 | 116.35 | 0 | 44.75 | 71.6 | 8.95 | 0 | 98.45 | 62.65 | 71.6 | 67 | | 1 | Expenses | Financial | Fees | | | | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 12 | 68 | | 2 | Expenses | Food | Coffee | | | | 0 | 5.49 | 0 | 0 | 0 | 0 | 36.76 | 0 | 0 | 0 | 43.07 | 0 | 69 | | 3 | Expenses | Food | Groceries | | | | 582.97 | 559.27 | 616.3 | 540.3 | 480.78 | 722.67 | 520.4 | 641.2 | 711.49 | 581.02 | 442.03 | 557.04 | 70 | | 4 | Expenses | Food | Restaurant | | | | 948.18 | 948.24 | 1139.92 | 1027.88 | 983.15 | 1127.47 | 1780.95 | 1064.27 | 1109.25 | 1143.04 | 1214.8 | 933.98 | 71 | 72 | 73 | ### Aggregation at Different Account Levels 74 | At level 1: 75 | 76 | | | Account_L0 | Account_L1 | 2018-Q1 | 2018-Q2 | 2018-Q3 | 2018-Q4 | 2019-Q1 | 2019-Q2 | 2019-Q3 | 2019-Q4 | 2020-Q1 | 2020-Q2 | 2020-Q3 | 2020-Q4 | 77 | |---:|:-------------|:-------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| 78 | | 0 | Expenses | Financial | 12 | 12 | 12 | 128.35 | 12 | 56.75 | 83.6 | 20.95 | 12 | 110.45 | 74.65 | 83.6 | 79 | | 1 | Expenses | Food | 1531.15 | 1513 | 1756.22 | 1568.18 | 1463.93 | 1850.14 | 2338.11 | 1705.47 | 1820.74 | 1724.06 | 1699.9 | 1491.02 | 80 | | 2 | Expenses | Health | 678.3 | 581.4 | 678.3 | 581.4 | 678.3 | 581.4 | 678.3 | 581.4 | 678.3 | 581.4 | 678.3 | 678.3 | 81 | | 3 | Expenses | Home | 7803.2 | 7810.58 | 7790.05 | 7806.36 | 7798.26 | 7821.55 | 7820.87 | 7828.41 | 7819.9 | 7819.41 | 7820.04 | 5234.56 | 82 | | 4 | Expenses | Taxes | 13945.4 | 11953.2 | 13945.4 | 11633.2 | 14854.4 | 11953.2 | 13945.4 | 11633.2 | 14882.1 | 11953.2 | 13945.4 | 13343.9 | 83 | 84 | At level 0: 85 | 86 | | | Account_L0 | 2018-Q1 | 2018-Q2 | 2018-Q3 | 2018-Q4 | 2019-Q1 | 2019-Q2 | 2019-Q3 | 2019-Q4 | 2020-Q1 | 2020-Q2 | 2020-Q3 | 2020-Q4 | 87 | |---:|:-------------|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:|----------:| 88 | | 0 | Expenses | 24330 | 22230.2 | 24542 | 22077.5 | 25166.9 | 22623 | 25106.3 | 22129.4 | 25573.1 | 22548.5 | 24578.3 | 21191.3 | 89 | | 1 | Income | -36677.9 | -31438.2 | -33927.9 | -27956 | -36728.8 | -31368.2 | -34178.4 | -27953.5 | -36793.2 | -32343.2 | -34095.5 | -32699 | 90 | 91 | 92 | ### Income and Expenses over Time 93 | ![png](Figures/Income_Expenses_over_Time.png) 94 | 95 | 96 | ### Treemap Plot of Expenses 97 | ![png](Figures/Treemap_Expenses.png) 98 | 99 | -------------------------------------------------------------------------------- /Notebooks/Beancount_Multiperiod_Reports_by_Year.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Multiperiod Reports by Year in Beancount\n", 8 | "If you've ever used [hledger](https://hledger.org/), you might like its ability to produce nice reports. One of the reports' feature is the table structure, where rows are accounts and columns are weeks, months, quarters or years. Looking at earnings and spendings as a function of time can give you more insights about your finances.\n", 9 | "\n", 10 | "However, if you are using [beancount](http://furius.ca/beancount/), this feature is not yet supported in the command line interface. You need to use [fava](https://github.com/beancount/fava), an awesome web-interface for beancount, which has a graph drawing capability as described in this tutorial. fava is not ideal and sometimes you might need more custom reports than the ones available in fava.\n", 11 | "\n", 12 | "This notebook provides methodology and tools to:\n", 13 | "- Process BQL query's output using Pandas library\n", 14 | "- Generate yearly totals (multiperiod reports by year) by pivoting a table\n", 15 | "- Aggregate values at different account levels for the provided account hierarchy\n", 16 | "- Draw treemap plots of expenses for all time period\n", 17 | "\n", 18 | "Details are in [this blog post](https://www.isabekov.pro/multiperiod-hledger-style-reports-in-beancount-pivoting-a-table/)." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "from beancount.loader import load_file\n", 28 | "from beancount.query.query import run_query\n", 29 | "from beancount.query.numberify import numberify_results\n", 30 | "import os\n", 31 | "import numpy as np\n", 32 | "import pandas as pd\n", 33 | "pd.set_option('display.max_column', 15)\n", 34 | "pd.set_option('display.max_rows', 20)\n", 35 | "pd.set_option('display.max_seq_items',None)\n", 36 | "pd.set_option('display.max_colwidth', 50000)\n", 37 | "pd.set_option('display.width', 5000000)\n", 38 | "pd.set_option('display.expand_frame_repr', True)\n", 39 | "pd.set_option('display.notebook_repr_html', True)\n", 40 | "pd.set_option('precision', 2)\n", 41 | "pd.set_option('expand_frame_repr', True)\n", 42 | "import squarify\n", 43 | "from matplotlib import pyplot as plt\n", 44 | "font = {'weight' : 'normal',\n", 45 | " 'size' : 12}\n", 46 | "plt.rc('font', **font)\n", 47 | "plt.rcParams[\"figure.figsize\"] = (15, 5)" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "## Create and Load an Example Beancount Journal File" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 2, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "data": { 64 | "text/plain": [ 65 | "'USD'" 66 | ] 67 | }, 68 | "execution_count": 2, 69 | "metadata": {}, 70 | "output_type": "execute_result" 71 | } 72 | ], 73 | "source": [ 74 | "FileName = \"example.beancount\"\n", 75 | "if not os.path.isfile(FileName):\n", 76 | " os.system(\"bean-example > {}\".format(FileName)) \n", 77 | " \n", 78 | "entries, _, opts = load_file(FileName)\n", 79 | "currency = opts[\"operating_currency\"][0]\n", 80 | "# Main currency\n", 81 | "currency" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "## Investigating a Transaction" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 3, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "data": { 98 | "text/plain": [ 99 | "2245" 100 | ] 101 | }, 102 | "execution_count": 3, 103 | "metadata": {}, 104 | "output_type": "execute_result" 105 | } 106 | ], 107 | "source": [ 108 | "# Not all of the entries are transactions\n", 109 | "len(entries)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 4, 115 | "metadata": {}, 116 | "outputs": [ 117 | { 118 | "data": { 119 | "text/plain": [ 120 | "beancount.core.data.Transaction" 121 | ] 122 | }, 123 | "execution_count": 4, 124 | "metadata": {}, 125 | "output_type": "execute_result" 126 | } 127 | ], 128 | "source": [ 129 | "type(entries[-1])" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 5, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "data": { 139 | "text/plain": [ 140 | "'Assets:US:BofA:Checking'" 141 | ] 142 | }, 143 | "execution_count": 5, 144 | "metadata": {}, 145 | "output_type": "execute_result" 146 | } 147 | ], 148 | "source": [ 149 | "entries[-1].postings[0].account" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 6, 155 | "metadata": {}, 156 | "outputs": [ 157 | { 158 | "data": { 159 | "text/plain": [ 160 | "2832.14 USD" 161 | ] 162 | }, 163 | "execution_count": 6, 164 | "metadata": {}, 165 | "output_type": "execute_result" 166 | } 167 | ], 168 | "source": [ 169 | "entries[-1].postings[0].units" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 7, 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "data": { 179 | "text/plain": [ 180 | "'Hoogle'" 181 | ] 182 | }, 183 | "execution_count": 7, 184 | "metadata": {}, 185 | "output_type": "execute_result" 186 | } 187 | ], 188 | "source": [ 189 | "entries[-1].payee" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 8, 195 | "metadata": {}, 196 | "outputs": [ 197 | { 198 | "data": { 199 | "text/plain": [ 200 | "'Payroll'" 201 | ] 202 | }, 203 | "execution_count": 8, 204 | "metadata": {}, 205 | "output_type": "execute_result" 206 | } 207 | ], 208 | "source": [ 209 | "entries[-1].narration" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "## Executing a BQL Query" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 9, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "data": { 226 | "text/plain": [ 227 | "[('account', str),\n", 228 | " ('year', int),\n", 229 | " ('amount', beancount.core.inventory.Inventory)]" 230 | ] 231 | }, 232 | "execution_count": 9, 233 | "metadata": {}, 234 | "output_type": "execute_result" 235 | } 236 | ], 237 | "source": [ 238 | "cols, rows = run_query(entries, opts, \n", 239 | " \"SELECT account, YEAR(date) AS year, SUM(convert(position, '{}', date)) AS amount\\\n", 240 | " WHERE account ~ 'Expenses'\\\n", 241 | " OR account ~ 'Income'\\\n", 242 | " GROUP BY account, year ORDER BY account, year\".format(currency)\n", 243 | " )\n", 244 | "cols" 245 | ] 246 | }, 247 | { 248 | "cell_type": "code", 249 | "execution_count": 10, 250 | "metadata": {}, 251 | "outputs": [ 252 | { 253 | "data": { 254 | "text/plain": [ 255 | "[ResultRow(account='Expenses:Financial:Commissions', year=2018, amount=(116.35 USD)),\n", 256 | " ResultRow(account='Expenses:Financial:Commissions', year=2019, amount=(125.30 USD)),\n", 257 | " ResultRow(account='Expenses:Financial:Commissions', year=2020, amount=(232.70 USD))]" 258 | ] 259 | }, 260 | "execution_count": 10, 261 | "metadata": {}, 262 | "output_type": "execute_result" 263 | } 264 | ], 265 | "source": [ 266 | "rows[:3]" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": 11, 272 | "metadata": {}, 273 | "outputs": [ 274 | { 275 | "data": { 276 | "text/plain": [ 277 | "[('account', str),\n", 278 | " ('year', int),\n", 279 | " ('amount (USD)', decimal.Decimal),\n", 280 | " ('amount (VACHR)', decimal.Decimal),\n", 281 | " ('amount (IRAUSD)', decimal.Decimal)]" 282 | ] 283 | }, 284 | "execution_count": 11, 285 | "metadata": {}, 286 | "output_type": "execute_result" 287 | } 288 | ], 289 | "source": [ 290 | "cols, rows = numberify_results(cols, rows)\n", 291 | "cols" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 12, 297 | "metadata": {}, 298 | "outputs": [ 299 | { 300 | "data": { 301 | "text/plain": [ 302 | "[['Expenses:Financial:Commissions', 2018, Decimal('116.35'), None, None],\n", 303 | " ['Expenses:Financial:Commissions', 2019, Decimal('125.30'), None, None],\n", 304 | " ['Expenses:Financial:Commissions', 2020, Decimal('232.70'), None, None]]" 305 | ] 306 | }, 307 | "execution_count": 12, 308 | "metadata": {}, 309 | "output_type": "execute_result" 310 | } 311 | ], 312 | "source": [ 313 | "rows[:3]" 314 | ] 315 | }, 316 | { 317 | "cell_type": "markdown", 318 | "metadata": {}, 319 | "source": [ 320 | "## Converting Result Rows to a Pandas Dataframe" 321 | ] 322 | }, 323 | { 324 | "cell_type": "code", 325 | "execution_count": 13, 326 | "metadata": {}, 327 | "outputs": [ 328 | { 329 | "data": { 330 | "text/html": [ 331 | "
\n", 332 | "\n", 345 | "\n", 346 | " \n", 347 | " \n", 348 | " \n", 349 | " \n", 350 | " \n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | "
accountyearamount (USD)amount (VACHR)amount (IRAUSD)
0Expenses:Financial:Commissions2018116.35NoneNone
1Expenses:Financial:Commissions2019125.30NoneNone
2Expenses:Financial:Commissions2020232.70NoneNone
3Expenses:Financial:Fees201848.00NoneNone
4Expenses:Financial:Fees201948.00NoneNone
\n", 399 | "
" 400 | ], 401 | "text/plain": [ 402 | " account year amount (USD) amount (VACHR) amount (IRAUSD)\n", 403 | "0 Expenses:Financial:Commissions 2018 116.35 None None\n", 404 | "1 Expenses:Financial:Commissions 2019 125.30 None None\n", 405 | "2 Expenses:Financial:Commissions 2020 232.70 None None\n", 406 | "3 Expenses:Financial:Fees 2018 48.00 None None\n", 407 | "4 Expenses:Financial:Fees 2019 48.00 None None" 408 | ] 409 | }, 410 | "execution_count": 13, 411 | "metadata": {}, 412 | "output_type": "execute_result" 413 | } 414 | ], 415 | "source": [ 416 | "df = pd.DataFrame(rows, columns=[k[0] for k in cols])\n", 417 | "df.head()" 418 | ] 419 | }, 420 | { 421 | "cell_type": "code", 422 | "execution_count": 14, 423 | "metadata": {}, 424 | "outputs": [ 425 | { 426 | "data": { 427 | "text/plain": [ 428 | "Account object\n", 429 | "Year int64\n", 430 | "Amount (USD) object\n", 431 | "amount (VACHR) object\n", 432 | "amount (IRAUSD) object\n", 433 | "dtype: object" 434 | ] 435 | }, 436 | "execution_count": 14, 437 | "metadata": {}, 438 | "output_type": "execute_result" 439 | } 440 | ], 441 | "source": [ 442 | "df.rename(columns={\"account\": \"Account\", \"year\":\"Year\", \"amount ({})\".format(currency): \"Amount ({})\".format(currency)}, inplace=True)\n", 443 | "df.dtypes" 444 | ] 445 | }, 446 | { 447 | "cell_type": "code", 448 | "execution_count": 15, 449 | "metadata": {}, 450 | "outputs": [ 451 | { 452 | "data": { 453 | "text/html": [ 454 | "
\n", 455 | "\n", 468 | "\n", 469 | " \n", 470 | " \n", 471 | " \n", 472 | " \n", 473 | " \n", 474 | " \n", 475 | " \n", 476 | " \n", 477 | " \n", 478 | " \n", 479 | " \n", 480 | " \n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | "
AccountYearAmount (USD)
0Expenses:Financial:Commissions2018116.35
1Expenses:Financial:Commissions2019125.30
2Expenses:Financial:Commissions2020232.70
3Expenses:Financial:Fees201848.00
4Expenses:Financial:Fees201948.00
\n", 510 | "
" 511 | ], 512 | "text/plain": [ 513 | " Account Year Amount (USD)\n", 514 | "0 Expenses:Financial:Commissions 2018 116.35\n", 515 | "1 Expenses:Financial:Commissions 2019 125.30\n", 516 | "2 Expenses:Financial:Commissions 2020 232.70\n", 517 | "3 Expenses:Financial:Fees 2018 48.00\n", 518 | "4 Expenses:Financial:Fees 2019 48.00" 519 | ] 520 | }, 521 | "execution_count": 15, 522 | "metadata": {}, 523 | "output_type": "execute_result" 524 | } 525 | ], 526 | "source": [ 527 | "df = df.astype({\"Account\": str, \"Year\": int, \"Amount ({})\".format(currency): np.float})\n", 528 | "df = df[[\"Account\", \"Year\", \"Amount ({})\".format(currency)]].fillna(0)\n", 529 | "df.head()" 530 | ] 531 | }, 532 | { 533 | "cell_type": "markdown", 534 | "metadata": {}, 535 | "source": [ 536 | "## Pivoting a Table by Year" 537 | ] 538 | }, 539 | { 540 | "cell_type": "code", 541 | "execution_count": 16, 542 | "metadata": {}, 543 | "outputs": [ 544 | { 545 | "data": { 546 | "text/html": [ 547 | "
\n", 548 | "\n", 561 | "\n", 562 | " \n", 563 | " \n", 564 | " \n", 565 | " \n", 566 | " \n", 567 | " \n", 568 | " \n", 569 | " \n", 570 | " \n", 571 | " \n", 572 | " \n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | "
AccountAmount (USD)
Year201820192020
0Expenses:Financial:Commissions116.35125.30232.70
1Expenses:Financial:Fees48.0048.0048.00
2Expenses:Food:Coffee5.4936.7643.07
3Expenses:Food:Groceries2298.842365.052291.58
4Expenses:Food:Restaurant4064.224955.844401.07
\n", 614 | "
" 615 | ], 616 | "text/plain": [ 617 | " Account Amount (USD) \n", 618 | "Year 2018 2019 2020\n", 619 | "0 Expenses:Financial:Commissions 116.35 125.30 232.70\n", 620 | "1 Expenses:Financial:Fees 48.00 48.00 48.00\n", 621 | "2 Expenses:Food:Coffee 5.49 36.76 43.07\n", 622 | "3 Expenses:Food:Groceries 2298.84 2365.05 2291.58\n", 623 | "4 Expenses:Food:Restaurant 4064.22 4955.84 4401.07" 624 | ] 625 | }, 626 | "execution_count": 16, 627 | "metadata": {}, 628 | "output_type": "execute_result" 629 | } 630 | ], 631 | "source": [ 632 | "df = df.pivot_table(index=\"Account\", columns=['Year']).fillna(0).reset_index()\n", 633 | "df.head()" 634 | ] 635 | }, 636 | { 637 | "cell_type": "markdown", 638 | "metadata": {}, 639 | "source": [ 640 | "## Creating Multi-Level Accounts" 641 | ] 642 | }, 643 | { 644 | "cell_type": "code", 645 | "execution_count": 17, 646 | "metadata": {}, 647 | "outputs": [ 648 | { 649 | "data": { 650 | "text/plain": [ 651 | "6" 652 | ] 653 | }, 654 | "execution_count": 17, 655 | "metadata": {}, 656 | "output_type": "execute_result" 657 | } 658 | ], 659 | "source": [ 660 | "n_levels = df[\"Account\"].str.count(\":\").max() + 1\n", 661 | "n_levels" 662 | ] 663 | }, 664 | { 665 | "cell_type": "code", 666 | "execution_count": 18, 667 | "metadata": {}, 668 | "outputs": [ 669 | { 670 | "data": { 671 | "text/plain": [ 672 | "['Account_L0',\n", 673 | " 'Account_L1',\n", 674 | " 'Account_L2',\n", 675 | " 'Account_L3',\n", 676 | " 'Account_L4',\n", 677 | " 'Account_L5']" 678 | ] 679 | }, 680 | "execution_count": 18, 681 | "metadata": {}, 682 | "output_type": "execute_result" 683 | } 684 | ], 685 | "source": [ 686 | "cols = [\"Account_L{}\".format(k) for k in range(n_levels)]\n", 687 | "cols" 688 | ] 689 | }, 690 | { 691 | "cell_type": "code", 692 | "execution_count": 19, 693 | "metadata": {}, 694 | "outputs": [ 695 | { 696 | "data": { 697 | "text/html": [ 698 | "
\n", 699 | "\n", 716 | "\n", 717 | " \n", 718 | " \n", 719 | " \n", 720 | " \n", 721 | " \n", 722 | " \n", 723 | " \n", 724 | " \n", 725 | " \n", 726 | " \n", 727 | " \n", 728 | " \n", 729 | " \n", 730 | " \n", 731 | " \n", 732 | " \n", 733 | " \n", 734 | " \n", 735 | " \n", 736 | " \n", 737 | " \n", 738 | " \n", 739 | " \n", 740 | " \n", 741 | " \n", 742 | " \n", 743 | " \n", 744 | " \n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | "
Amount (USD)
Year201820192020
Account_L0Account_L1Account_L2Account_L3Account_L4Account_L5
ExpensesFinancialCommissions116.35125.30232.70
Fees48.0048.0048.00
FoodCoffee5.4936.7643.07
Groceries2298.842365.052291.58
Restaurant4064.224955.844401.07
\n", 801 | "
" 802 | ], 803 | "text/plain": [ 804 | " Amount (USD) \n", 805 | "Year 2018 2019 2020\n", 806 | "Account_L0 Account_L1 Account_L2 Account_L3 Account_L4 Account_L5 \n", 807 | "Expenses Financial Commissions 116.35 125.30 232.70\n", 808 | " Fees 48.00 48.00 48.00\n", 809 | " Food Coffee 5.49 36.76 43.07\n", 810 | " Groceries 2298.84 2365.05 2291.58\n", 811 | " Restaurant 4064.22 4955.84 4401.07" 812 | ] 813 | }, 814 | "execution_count": 19, 815 | "metadata": {}, 816 | "output_type": "execute_result" 817 | } 818 | ], 819 | "source": [ 820 | "df[cols] = df[\"Account\"].str.split(':', n_levels - 1, expand=True)\n", 821 | "df = df.fillna('').drop(columns=\"Account\", level=0).set_index(cols)\n", 822 | "df.head()" 823 | ] 824 | }, 825 | { 826 | "cell_type": "markdown", 827 | "metadata": {}, 828 | "source": [ 829 | "## Aggregation at Different Account Levels" 830 | ] 831 | }, 832 | { 833 | "cell_type": "code", 834 | "execution_count": 20, 835 | "metadata": {}, 836 | "outputs": [ 837 | { 838 | "data": { 839 | "text/html": [ 840 | "\n", 844 | " \n", 845 | " \n", 846 | " \n", 847 | " \n", 848 | " \n", 849 | " \n", 850 | " \n", 851 | " \n", 852 | " \n", 853 | " \n", 854 | " \n", 855 | " \n", 856 | " \n", 857 | " \n", 858 | " \n", 859 | " \n", 860 | " \n", 861 | " \n", 862 | " \n", 863 | " \n", 864 | " \n", 865 | " \n", 866 | " \n", 867 | " \n", 868 | " \n", 869 | " \n", 870 | " \n", 871 | " \n", 872 | " \n", 873 | " \n", 874 | " \n", 875 | " \n", 876 | " \n", 877 | " \n", 878 | " \n", 879 | " \n", 880 | " \n", 881 | " \n", 882 | " \n", 883 | " \n", 884 | " \n", 885 | " \n", 886 | " \n", 887 | " \n", 888 | " \n", 889 | " \n", 890 | " \n", 891 | " \n", 892 | " \n", 893 | " \n", 894 | "
Amount (USD)
Year 2018 2019 2020
Account_L0 Account_L1
ExpensesFinancial164.35173.30280.70
Food6368.557357.656735.72
Health2519.402519.402616.30
Home31210.1931269.0928693.91
Taxes51477.2052386.1954124.59
Transport1440.001320.001440.00
Vacation0.000.000.00
IncomeUS-129999.98-130228.84-135930.89
" 895 | ], 896 | "text/plain": [ 897 | "" 898 | ] 899 | }, 900 | "execution_count": 20, 901 | "metadata": {}, 902 | "output_type": "execute_result" 903 | } 904 | ], 905 | "source": [ 906 | "df_L1 = df.groupby([\"Account_L0\", \"Account_L1\"]).sum()\n", 907 | "df_L1.style.set_table_styles(\n", 908 | "[{'selector': 'tr:hover',\n", 909 | " 'props': [('background-color', 'yellow')]}]\n", 910 | ")" 911 | ] 912 | }, 913 | { 914 | "cell_type": "code", 915 | "execution_count": 21, 916 | "metadata": {}, 917 | "outputs": [ 918 | { 919 | "data": { 920 | "text/html": [ 921 | "\n", 925 | " \n", 926 | " \n", 927 | " \n", 928 | " \n", 929 | " \n", 930 | " \n", 931 | " \n", 932 | " \n", 933 | " \n", 934 | " \n", 935 | " \n", 936 | " \n", 937 | "
Amount (USD)
Year 2018 2019 2020
Account_L0
Expenses93179.6995025.6393891.22
Income-129999.98-130228.84-135930.89
" 938 | ], 939 | "text/plain": [ 940 | "" 941 | ] 942 | }, 943 | "execution_count": 21, 944 | "metadata": {}, 945 | "output_type": "execute_result" 946 | } 947 | ], 948 | "source": [ 949 | "df_L0 = df.groupby([\"Account_L0\"]).sum()\n", 950 | "df_L0.style.set_table_styles(\n", 951 | "[{'selector': 'tr:hover',\n", 952 | " 'props': [('background-color', 'yellow')]}]\n", 953 | ")" 954 | ] 955 | }, 956 | { 957 | "cell_type": "code", 958 | "execution_count": 22, 959 | "metadata": {}, 960 | "outputs": [ 961 | { 962 | "data": { 963 | "text/html": [ 964 | "
\n", 965 | "\n", 978 | "\n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | "
Amount (USD)
Year201820192020
Net income36820.2935203.2142039.67
\n", 1000 | "
" 1001 | ], 1002 | "text/plain": [ 1003 | " Amount (USD) \n", 1004 | "Year 2018 2019 2020\n", 1005 | "Net income 36820.29 35203.21 42039.67" 1006 | ] 1007 | }, 1008 | "execution_count": 22, 1009 | "metadata": {}, 1010 | "output_type": "execute_result" 1011 | } 1012 | ], 1013 | "source": [ 1014 | "# Net income\n", 1015 | "-df_L0.sum(axis=0).to_frame().rename(columns={0: \"Net income\"}).transpose()" 1016 | ] 1017 | }, 1018 | { 1019 | "cell_type": "code", 1020 | "execution_count": 23, 1021 | "metadata": {}, 1022 | "outputs": [ 1023 | { 1024 | "data": { 1025 | "text/html": [ 1026 | "
\n", 1027 | "\n", 1040 | "\n", 1041 | " \n", 1042 | " \n", 1043 | " \n", 1044 | " \n", 1045 | " \n", 1046 | " \n", 1047 | " \n", 1048 | " \n", 1049 | " \n", 1050 | " \n", 1051 | " \n", 1052 | " \n", 1053 | " \n", 1054 | " \n", 1055 | " \n", 1056 | " \n", 1057 | " \n", 1058 | " \n", 1059 | " \n", 1060 | " \n", 1061 | " \n", 1062 | " \n", 1063 | " \n", 1064 | " \n", 1065 | " \n", 1066 | " \n", 1067 | " \n", 1068 | " \n", 1069 | " \n", 1070 | " \n", 1071 | " \n", 1072 | " \n", 1073 | "
Account_L0level_0YearExpensesIncome
0Amount (USD)201893179.69129999.98
1Amount (USD)201995025.63130228.84
2Amount (USD)202093891.22135930.89
\n", 1074 | "
" 1075 | ], 1076 | "text/plain": [ 1077 | "Account_L0 level_0 Year Expenses Income\n", 1078 | "0 Amount (USD) 2018 93179.69 129999.98\n", 1079 | "1 Amount (USD) 2019 95025.63 130228.84\n", 1080 | "2 Amount (USD) 2020 93891.22 135930.89" 1081 | ] 1082 | }, 1083 | "execution_count": 23, 1084 | "metadata": {}, 1085 | "output_type": "execute_result" 1086 | } 1087 | ], 1088 | "source": [ 1089 | "# Invert the sign of the \"Income\" account. Negative income is counter-intuitive.\n", 1090 | "df_L0.loc[\"Income\"] = -df_L0.loc[\"Income\"]\n", 1091 | "df_L0 = df_L0.transpose().reset_index()\n", 1092 | "df_L0" 1093 | ] 1094 | }, 1095 | { 1096 | "cell_type": "code", 1097 | "execution_count": 24, 1098 | "metadata": {}, 1099 | "outputs": [ 1100 | { 1101 | "data": { 1102 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAA5kAAAFUCAYAAACuv/hlAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAAsTAAALEwEAmpwYAAA0IklEQVR4nO3dfZxXdZ3//8dLRK6JS1FQIUzFqDSjtfx6VZpZqZmYS5jrRYFa7u5X86tuXmDaVlrstr+sBdEUFSkLs7zKzS1X3VULDS1ESU1EEEFEZEAB9fX743Nm9sM4AzN4hs8M87jfbp9bc96vz/vM64x7O+vTc877RGYiSZIkSVIZtql1A5IkSZKkrYchU5IkSZJUGkOmJEmSJKk0hkxJkiRJUmkMmZIkSZKk0hgyJUmSJEml2bbWDXREgwYNyhEjRtS6DUmSJEmqiYcffvilzBzcVG2LhcyIOAM4CXg/MDMzT2riO5OAi4FPZObdxVgA3wG+XHztauDcLF7wGREjgGuAfYHngDPq5xb18cC3gUHAb4BTMvPlotYN+HfgWGANcHlm/sumjmXEiBHMnj27VccvSZIkSVuLiFjQXG1L3i67GPgm8OOmihGxK5Ww90Kj0kTgaGAv4APAEcCpVfWZwB+BgcD5wM8jYnCxz9HAVOAEYAiVIPmjqrkXA7sBw4GPAedExOGbeXySJEmS1OltsZCZmTdn5i3A8ma+cgVwLrCu0fiJwOTMfD4zFwGTqVwRJSJ2B/YBJmXma5k5C/gTMLaYezxwa2bem5l1wIXAMRHRp6j/HXBpZq7IzHnAtPp9S5IkSZJar10s/BMRnwfWZeYdTZRHA49WbT9ajNXXnsnMVRupN8zNzKephNjdI6I/MHQj+5YkSZIktVLNF/6JiN7At4DDmvlKb2Bl1fZKoHfxrGbjWn19WDNz6+t9ihq8fd99aEJETKRy6y677LJLM61KkiRJeideffVVli5dyvr162vdSqfXq1cvdtppJ7bZpnXXJmseMoFvANdn5l+bqdcBfau2+wJ1mZkR0bhWX1/VzNzqel3V9utNzN1AZl4JXAkwZsyY3NgBSZIkSWq9V199lRdffJFhw4bRo0cPKteVVAtvvfUWixYt4qWXXmL77bdv1dz2cLvsIcA/RMSSiFgC7AzcFBHnFvW5VBb9qbdXMVZfG1n1jGVT9Ya5ETES6AbMz8wVVBYZam7fkiRJkragpUuXMmzYMHr27GnArLFtttmGIUOGsHJl4xtDWzC3DfppUkRsGxHdgS5Al4joHhHbUgmZ7wP2Lj6Lqawe+8Ni6nXAWRExLCKGAl8DrgXIzPnAHGBSsb/PUVmBdlYxdwZwZEQcEBG9gEuAm6ue4bwOuCAi+kfEKGBC/b4lSZIkbVnr16+nR48etW5Dha5du/LGG2+0et6WvF32AmBS1fYXgW9k5sXVX4qIN4EVxWqwUHkFyUgqq8YCXFWM1RtHJRiuoPKezGMzcxlAZs6NiNOohM2BwN3AyVVzJ1F5T+YC4DXgssz89Ts6SkmSJEmbzSuY7cfm/rOITB8vbK0xY8bk7Nmza92GJEmStFWZN28ee+65Z63bUJXm/plExMOZOaapOe3hmUxJkiRJ0laiPawuK0mSJG1RI867vdYttDvPfucztW6hZg4++GAeffRRlixZQrdu3Wrdzttce+21XHXVVdx///21bqVFvJIpSZIkqdN69tlnue+++4gIfvWrX9W6na2CIVOSJElSp3XdddfxkY98hJNOOonp06c3jC9cuJBjjjmGwYMHM3DgQM4444yG2rRp09hzzz3p06cP733ve3nkkUeAyvOLBx98MP369WP06NEbhNaDDz6Yq666qmH72muvZf/992/YjgimTJnCbrvtRv/+/fnqV79KZjJv3jxOO+00HnjgAXr37k2/fv3a8K9RDkOmJEmSpE7ruuuu4/jjj+f444/nrrvu4sUXX+TNN9/kiCOOYPjw4Tz77LMsWrSIcePGAfCzn/2Miy++mOuuu45XX32VX/3qVwwcOJD169dz5JFHcthhh7F06VJ+8IMfcPzxx/Pkk0+2uJfbbruNP/zhDzz66KPcdNNN3HXXXey5555MmTKFj370o9TV1fHKK6+00V+iPIZMSZIkSZ3S/fffz4IFCzjuuOP40Ic+xK677sqNN97I73//exYvXsx3v/tdevXqRffu3RuuOl511VWcc845fPjDHyYieM973sPw4cN58MEHqaur47zzzmO77bbj4x//OEcccQQzZ85scT/nnXce/fr1Y5ddduFjH/sYc+bMaaMjb1su/CNJkiSpU5o+fTqHHXYYgwYNAmD8+PFMnz6dYcOGMXz4cLbd9u1xaeHChey6665vG1+8eDE777wz22zzv9fxhg8fzqJFi1rc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9VX69+/PPvvsA8C0adM49dRT2XfffenSpQsnnngi3bp148EHH6RLly6sXbuWxx9/nPXr1zNixAh23XXXUv9GhkxJkiRJaqFbbrmFV155peEzYcIEAP7mb/6GkSNHkpkcd9xxG8zp378/vXr1atgePnw4ixcvZtmyZaxZs4YPfehDDaH18MMPZ9myZQ3fHThwINtu+79L6fTs2ZO6ujoAZs2axR133MHw4cM56KCDeOCBBwBYsGABkydP3iAML1y4kMWLF/Oe97yH73//+1x88cVsv/32jBs3jsWLF5f6NzJkSpIkSdI79MMf/pC1a9cydOhQLr/88g1qK1asYPXq1Q3bzz33HEOHDmXQoEH06NGDuXPnNoTWlStXNoTITfnwhz/ML3/5S5YuXcrRRx/dEG533nlnzj///A3C8Jo1a/jCF74AwPjx47n//vtZsGABEcG5555b0l+hwpApSZIkSe/A/PnzueCCC7jhhhu4/vrrufzyy5kzZ84G35k0aRLr1q3jvvvu47bbbuPzn/8822yzDRMmTODMM89k6dKlACxatIi77rprk79z3bp1zJgxg5UrV9K1a1f69u3b8GqVCRMmMGXKFB566CEyk9WrV3P77bezatUqnnzySX7729+ydu1aunfvTo8ePTZ4JUsZfIWJJEmSpParFe+x3BKOPPLIDULZJz7xCRYtWsS5557LXnvtBcC3vvUtTjjhBGbPng3ADjvsQP/+/Rk6dCg9e/ZkypQpjBo1Cqis9HrJJZfwkY98hJdeeolhw4Zx+umn88lPfnKTvVx//fWcccYZvPnmm+yxxx7ccMMNAIwZM4Zp06Zxxhln8Je//IUePXqw//77c+CBB7J27VrOO+885s2bR9euXdlvv/248sorS/0bRWaWusPOYMyYMVn/fzD6XyPOu73WLbQ7z37nM7VuQerwPLe8necW6Z3z3PJ27eHcMm/ePPbcc89at1Gqe+65hy9+8Ys8//zztW5lszT3zyQiHs7MMU3N8XZZSZIkSVJpDJmSJEmSpNIYMiVJkiSpjRx88MEd9lbZzWXIlCRJkiSVxpApSZIkqd146623at2CCpu7SKwhU5IkSVK70KtXLxYtWsS6des2O+CoHJnJ8uXL6d69e6vn+p5MSZIkSe3CTjvtxEsvvcSCBQt44403at1Op9e9e3d22mmnVs8zZEqSJElq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\n", 1103 | "text/plain": [ 1104 | "
" 1105 | ] 1106 | }, 1107 | "metadata": { 1108 | "needs_background": "light" 1109 | }, 1110 | "output_type": "display_data" 1111 | } 1112 | ], 1113 | "source": [ 1114 | "df_L0.columns.name = \"Account\"\n", 1115 | "df_L0.plot.bar(x=\"Year\", y=[\"Income\", \"Expenses\"], xlabel=\"Year\", ylabel=df_L0[\"level_0\"][0], rot=90)\n", 1116 | "plt.show()" 1117 | ] 1118 | }, 1119 | { 1120 | "cell_type": "markdown", 1121 | "metadata": {}, 1122 | "source": [ 1123 | "## Treemap Plot of Expenses" 1124 | ] 1125 | }, 1126 | { 1127 | "cell_type": "code", 1128 | "execution_count": 25, 1129 | "metadata": {}, 1130 | "outputs": [ 1131 | { 1132 | "data": { 1133 | "text/html": [ 1134 | "
\n", 1135 | "\n", 1148 | "\n", 1149 | " \n", 1150 | " \n", 1151 | " \n", 1152 | " \n", 1153 | " \n", 1154 | " \n", 1155 | " \n", 1156 | " \n", 1157 | " \n", 1158 | " \n", 1159 | " \n", 1160 | " \n", 1161 | " \n", 1162 | " \n", 1163 | " \n", 1164 | " \n", 1165 | " \n", 1166 | " \n", 1167 | " \n", 1168 | " \n", 1169 | " \n", 1170 | " \n", 1171 | " \n", 1172 | " \n", 1173 | " \n", 1174 | " \n", 1175 | " \n", 1176 | " \n", 1177 | " \n", 1178 | " \n", 1179 | " \n", 1180 | " \n", 1181 | " \n", 1182 | " \n", 1183 | " \n", 1184 | " \n", 1185 | " \n", 1186 | " \n", 1187 | " \n", 1188 | " \n", 1189 | " \n", 1190 | " \n", 1191 | " \n", 1192 | " \n", 1193 | " \n", 1194 | " \n", 1195 | " \n", 1196 | " \n", 1197 | "
0
Account_L0Account_L1
ExpensesTaxes157987.98
Home91173.19
Food20461.92
Health7655.10
Transport4200.00
Financial618.35
Vacation0.00
IncomeUS-396159.71
\n", 1198 | "
" 1199 | ], 1200 | "text/plain": [ 1201 | " 0\n", 1202 | "Account_L0 Account_L1 \n", 1203 | "Expenses Taxes 157987.98\n", 1204 | " Home 91173.19\n", 1205 | " Food 20461.92\n", 1206 | " Health 7655.10\n", 1207 | " Transport 4200.00\n", 1208 | " Financial 618.35\n", 1209 | " Vacation 0.00\n", 1210 | "Income US -396159.71" 1211 | ] 1212 | }, 1213 | "execution_count": 25, 1214 | "metadata": {}, 1215 | "output_type": "execute_result" 1216 | } 1217 | ], 1218 | "source": [ 1219 | "tot = df_L1.sum(axis=1).to_frame().sort_values(by=0, ascending=False)\n", 1220 | "tot" 1221 | ] 1222 | }, 1223 | { 1224 | "cell_type": "code", 1225 | "execution_count": 26, 1226 | "metadata": {}, 1227 | "outputs": [ 1228 | { 1229 | "data": { 1230 | "image/png": "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\n", 1231 | "text/plain": [ 1232 | "
" 1233 | ] 1234 | }, 1235 | "metadata": {}, 1236 | "output_type": "display_data" 1237 | } 1238 | ], 1239 | "source": [ 1240 | "data = tot.loc[\"Expenses\"].sort_values(by=0, ascending=False)\n", 1241 | "idx = [k[0] != 0 for k in data.values]\n", 1242 | "values = data.values[idx]\n", 1243 | "labels = data.index[idx]\n", 1244 | "\n", 1245 | "width = 1\n", 1246 | "height = 0.5\n", 1247 | "values_norm = squarify.normalize_sizes(values, width, height)\n", 1248 | "rects = squarify.squarify(values_norm, 0, 0, width, height)\n", 1249 | "\n", 1250 | "fig = plt.figure(figsize=(10, 10))\n", 1251 | "fig.suptitle('Expenses', x=0.5, y=0.55, fontsize=16)\n", 1252 | "axes = [fig.add_axes([rect['x'], rect['y'], rect['dx'], rect['dy'], ]) for rect in rects]\n", 1253 | "\n", 1254 | "for ax, txt, color in zip(axes, labels, plt.cm.Pastel1.colors):\n", 1255 | " ax.text(0.5, 0.5, txt, horizontalalignment='center', verticalalignment='center')\n", 1256 | " ax.set_yticks([])\n", 1257 | " ax.set_xticks([])\n", 1258 | " ax.set_facecolor(color) \n", 1259 | "plt.show()" 1260 | ] 1261 | } 1262 | ], 1263 | "metadata": { 1264 | "kernelspec": { 1265 | "display_name": "Python 3", 1266 | "language": "python", 1267 | "name": "python3" 1268 | }, 1269 | "language_info": { 1270 | "codemirror_mode": { 1271 | "name": "ipython", 1272 | "version": 3 1273 | }, 1274 | "file_extension": ".py", 1275 | "mimetype": "text/x-python", 1276 | "name": "python", 1277 | "nbconvert_exporter": "python", 1278 | "pygments_lexer": "ipython3", 1279 | "version": "3.9.1" 1280 | } 1281 | }, 1282 | "nbformat": 4, 1283 | "nbformat_minor": 4 1284 | } 1285 | -------------------------------------------------------------------------------- /Notebooks/Beancount_Multiperiod_Reports_by_Quarter.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Multiperiod Reports by Quarter in Beancount\n", 8 | "If you've ever used [hledger](https://hledger.org/), you might like its ability to produce nice reports. One of the reports' feature is the table structure, where rows are accounts and columns are weeks, months, quarters or years. Looking at earnings and spendings as a function of time can give you more insights about your finances.\n", 9 | "\n", 10 | "However, if you are using [beancount](http://furius.ca/beancount/), this feature is not yet supported in the command line interface. You need to use [fava](https://github.com/beancount/fava), an awesome web-interface for beancount, which has a graph drawing capability as described in this tutorial. fava is not ideal and sometimes you might need more custom reports than the ones available in fava.\n", 11 | "\n", 12 | "This notebook provides methodology and tools to:\n", 13 | "- Process BQL query's output using Pandas library\n", 14 | "- Generate quarterly totals (multiperiod reports by quarter) by pivoting a table\n", 15 | "- Aggregate values at different account levels for the provided account hierarchy\n", 16 | "- Draw treemap plots of expenses for all time period\n", 17 | "\n", 18 | "Details are in [this blog post](https://www.isabekov.pro/multiperiod-hledger-style-reports-in-beancount-pivoting-a-table/)." 19 | ] 20 | }, 21 | { 22 | "cell_type": "code", 23 | "execution_count": 1, 24 | "metadata": {}, 25 | "outputs": [], 26 | "source": [ 27 | "from beancount.loader import load_file\n", 28 | "from beancount.query.query import run_query\n", 29 | "from beancount.query.numberify import numberify_results\n", 30 | "import os\n", 31 | "import numpy as np\n", 32 | "import pandas as pd\n", 33 | "pd.set_option('display.max_column', 15)\n", 34 | "pd.set_option('display.max_rows', 20)\n", 35 | "pd.set_option('display.max_seq_items',None)\n", 36 | "pd.set_option('display.max_colwidth', 50000)\n", 37 | "pd.set_option('display.width', 5000000)\n", 38 | "pd.set_option('display.expand_frame_repr', True)\n", 39 | "pd.set_option('display.notebook_repr_html', True)\n", 40 | "pd.set_option('precision', 2)\n", 41 | "pd.set_option('expand_frame_repr', True)\n", 42 | "import squarify\n", 43 | "from matplotlib import pyplot as plt\n", 44 | "font = {'weight' : 'normal',\n", 45 | " 'size' : 12}\n", 46 | "plt.rc('font', **font)\n", 47 | "plt.rcParams[\"figure.figsize\"] = (15, 5)" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "## Create and Load an Example Beancount Journal File" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 2, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "data": { 64 | "text/plain": [ 65 | "'USD'" 66 | ] 67 | }, 68 | "execution_count": 2, 69 | "metadata": {}, 70 | "output_type": "execute_result" 71 | } 72 | ], 73 | "source": [ 74 | "FileName = \"example.beancount\"\n", 75 | "if not os.path.isfile(FileName):\n", 76 | " os.system(\"bean-example > {}\".format(FileName)) \n", 77 | " \n", 78 | "entries, _, opts = load_file(FileName)\n", 79 | "currency = opts[\"operating_currency\"][0]\n", 80 | "# Main currency\n", 81 | "currency" 82 | ] 83 | }, 84 | { 85 | "cell_type": "markdown", 86 | "metadata": {}, 87 | "source": [ 88 | "## Investigating a Transaction" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": 3, 94 | "metadata": {}, 95 | "outputs": [ 96 | { 97 | "data": { 98 | "text/plain": [ 99 | "2245" 100 | ] 101 | }, 102 | "execution_count": 3, 103 | "metadata": {}, 104 | "output_type": "execute_result" 105 | } 106 | ], 107 | "source": [ 108 | "# Not all of the entries are transactions\n", 109 | "len(entries)" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 4, 115 | "metadata": {}, 116 | "outputs": [ 117 | { 118 | "data": { 119 | "text/plain": [ 120 | "beancount.core.data.Transaction" 121 | ] 122 | }, 123 | "execution_count": 4, 124 | "metadata": {}, 125 | "output_type": "execute_result" 126 | } 127 | ], 128 | "source": [ 129 | "type(entries[-1])" 130 | ] 131 | }, 132 | { 133 | "cell_type": "code", 134 | "execution_count": 5, 135 | "metadata": {}, 136 | "outputs": [ 137 | { 138 | "data": { 139 | "text/plain": [ 140 | "'Assets:US:BofA:Checking'" 141 | ] 142 | }, 143 | "execution_count": 5, 144 | "metadata": {}, 145 | "output_type": "execute_result" 146 | } 147 | ], 148 | "source": [ 149 | "entries[-1].postings[0].account" 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "execution_count": 6, 155 | "metadata": {}, 156 | "outputs": [ 157 | { 158 | "data": { 159 | "text/plain": [ 160 | "2832.14 USD" 161 | ] 162 | }, 163 | "execution_count": 6, 164 | "metadata": {}, 165 | "output_type": "execute_result" 166 | } 167 | ], 168 | "source": [ 169 | "entries[-1].postings[0].units" 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 7, 175 | "metadata": {}, 176 | "outputs": [ 177 | { 178 | "data": { 179 | "text/plain": [ 180 | "'Hoogle'" 181 | ] 182 | }, 183 | "execution_count": 7, 184 | "metadata": {}, 185 | "output_type": "execute_result" 186 | } 187 | ], 188 | "source": [ 189 | "entries[-1].payee" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": 8, 195 | "metadata": {}, 196 | "outputs": [ 197 | { 198 | "data": { 199 | "text/plain": [ 200 | "'Payroll'" 201 | ] 202 | }, 203 | "execution_count": 8, 204 | "metadata": {}, 205 | "output_type": "execute_result" 206 | } 207 | ], 208 | "source": [ 209 | "entries[-1].narration" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "## Executing a BQL Query" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 9, 222 | "metadata": {}, 223 | "outputs": [ 224 | { 225 | "data": { 226 | "text/plain": [ 227 | "[('account', str),\n", 228 | " ('year', int),\n", 229 | " ('month', int),\n", 230 | " ('amount', beancount.core.inventory.Inventory)]" 231 | ] 232 | }, 233 | "execution_count": 9, 234 | "metadata": {}, 235 | "output_type": "execute_result" 236 | } 237 | ], 238 | "source": [ 239 | "cols, rows = run_query(entries, opts, \n", 240 | " \"SELECT account, YEAR(date) AS year,\\\n", 241 | " MONTH(date) as month,\\\n", 242 | " SUM(convert(position, '{}', date)) AS amount\\\n", 243 | " WHERE account ~ 'Expenses'\\\n", 244 | " OR account ~ 'Income'\\\n", 245 | " GROUP BY account, year, month\\\n", 246 | " ORDER BY account, year, month\".format(currency)\n", 247 | " )\n", 248 | "cols" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": 10, 254 | "metadata": {}, 255 | "outputs": [ 256 | { 257 | "data": { 258 | "text/plain": [ 259 | "[ResultRow(account='Expenses:Financial:Commissions', year=2018, month=10, amount=(44.75 USD)),\n", 260 | " ResultRow(account='Expenses:Financial:Commissions', year=2018, month=11, amount=(35.80 USD)),\n", 261 | " ResultRow(account='Expenses:Financial:Commissions', year=2018, month=12, amount=(35.80 USD))]" 262 | ] 263 | }, 264 | "execution_count": 10, 265 | "metadata": {}, 266 | "output_type": "execute_result" 267 | } 268 | ], 269 | "source": [ 270 | "rows[:3]" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 11, 276 | "metadata": {}, 277 | "outputs": [ 278 | { 279 | "data": { 280 | "text/plain": [ 281 | "[('account', str),\n", 282 | " ('year', int),\n", 283 | " ('month', int),\n", 284 | " ('amount (USD)', decimal.Decimal),\n", 285 | " ('amount (VACHR)', decimal.Decimal),\n", 286 | " ('amount (IRAUSD)', decimal.Decimal)]" 287 | ] 288 | }, 289 | "execution_count": 11, 290 | "metadata": {}, 291 | "output_type": "execute_result" 292 | } 293 | ], 294 | "source": [ 295 | "cols, rows = numberify_results(cols, rows)\n", 296 | "cols" 297 | ] 298 | }, 299 | { 300 | "cell_type": "code", 301 | "execution_count": 12, 302 | "metadata": {}, 303 | "outputs": [ 304 | { 305 | "data": { 306 | "text/plain": [ 307 | "[['Expenses:Financial:Commissions', 2018, 10, Decimal('44.75'), None, None],\n", 308 | " ['Expenses:Financial:Commissions', 2018, 11, Decimal('35.80'), None, None],\n", 309 | " ['Expenses:Financial:Commissions', 2018, 12, Decimal('35.80'), None, None]]" 310 | ] 311 | }, 312 | "execution_count": 12, 313 | "metadata": {}, 314 | "output_type": "execute_result" 315 | } 316 | ], 317 | "source": [ 318 | "rows[:3]" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "## Converting Result Rows to a Pandas Dataframe" 326 | ] 327 | }, 328 | { 329 | "cell_type": "code", 330 | "execution_count": 13, 331 | "metadata": {}, 332 | "outputs": [ 333 | { 334 | "data": { 335 | "text/html": [ 336 | "
\n", 337 | "\n", 350 | "\n", 351 | " \n", 352 | " \n", 353 | " \n", 354 | " \n", 355 | " \n", 356 | " \n", 357 | " \n", 358 | " \n", 359 | " \n", 360 | " \n", 361 | " \n", 362 | " \n", 363 | " \n", 364 | " \n", 365 | " \n", 366 | " \n", 367 | " \n", 368 | " \n", 369 | " \n", 370 | " \n", 371 | " \n", 372 | " \n", 373 | " \n", 374 | " \n", 375 | " \n", 376 | " \n", 377 | " \n", 378 | " \n", 379 | " \n", 380 | " \n", 381 | " \n", 382 | " \n", 383 | " \n", 384 | " \n", 385 | " \n", 386 | " \n", 387 | " \n", 388 | " \n", 389 | " \n", 390 | " \n", 391 | " \n", 392 | " \n", 393 | " \n", 394 | " \n", 395 | " \n", 396 | " \n", 397 | " \n", 398 | " \n", 399 | " \n", 400 | " \n", 401 | " \n", 402 | " \n", 403 | " \n", 404 | " \n", 405 | " \n", 406 | " \n", 407 | " \n", 408 | " \n", 409 | "
accountyearmonthamount (USD)amount (VACHR)amount (IRAUSD)
0Expenses:Financial:Commissions20181044.75NoneNone
1Expenses:Financial:Commissions20181135.80NoneNone
2Expenses:Financial:Commissions20181235.80NoneNone
3Expenses:Financial:Commissions2019535.80NoneNone
4Expenses:Financial:Commissions201968.95NoneNone
\n", 410 | "
" 411 | ], 412 | "text/plain": [ 413 | " account year month amount (USD) amount (VACHR) amount (IRAUSD)\n", 414 | "0 Expenses:Financial:Commissions 2018 10 44.75 None None\n", 415 | "1 Expenses:Financial:Commissions 2018 11 35.80 None None\n", 416 | "2 Expenses:Financial:Commissions 2018 12 35.80 None None\n", 417 | "3 Expenses:Financial:Commissions 2019 5 35.80 None None\n", 418 | "4 Expenses:Financial:Commissions 2019 6 8.95 None None" 419 | ] 420 | }, 421 | "execution_count": 13, 422 | "metadata": {}, 423 | "output_type": "execute_result" 424 | } 425 | ], 426 | "source": [ 427 | "df = pd.DataFrame(rows, columns=[k[0] for k in cols])\n", 428 | "df.head()" 429 | ] 430 | }, 431 | { 432 | "cell_type": "code", 433 | "execution_count": 14, 434 | "metadata": {}, 435 | "outputs": [ 436 | { 437 | "data": { 438 | "text/plain": [ 439 | "Account object\n", 440 | "Year int64\n", 441 | "Month int64\n", 442 | "Amount (USD) object\n", 443 | "amount (VACHR) object\n", 444 | "amount (IRAUSD) object\n", 445 | "dtype: object" 446 | ] 447 | }, 448 | "execution_count": 14, 449 | "metadata": {}, 450 | "output_type": "execute_result" 451 | } 452 | ], 453 | "source": [ 454 | "df.rename(columns={\"account\": \"Account\", \"year\":\"Year\", \"month\": \"Month\", \"amount ({})\".format(currency): \"Amount ({})\".format(currency)}, inplace=True)\n", 455 | "df.dtypes" 456 | ] 457 | }, 458 | { 459 | "cell_type": "code", 460 | "execution_count": 15, 461 | "metadata": {}, 462 | "outputs": [ 463 | { 464 | "data": { 465 | "text/html": [ 466 | "
\n", 467 | "\n", 480 | "\n", 481 | " \n", 482 | " \n", 483 | " \n", 484 | " \n", 485 | " \n", 486 | " \n", 487 | " \n", 488 | " \n", 489 | " \n", 490 | " \n", 491 | " \n", 492 | " \n", 493 | " \n", 494 | " \n", 495 | " \n", 496 | " \n", 497 | " \n", 498 | " \n", 499 | " \n", 500 | " \n", 501 | " \n", 502 | " \n", 503 | " \n", 504 | " \n", 505 | " \n", 506 | " \n", 507 | " \n", 508 | " \n", 509 | " \n", 510 | " \n", 511 | " \n", 512 | " \n", 513 | " \n", 514 | " \n", 515 | " \n", 516 | " \n", 517 | " \n", 518 | " \n", 519 | " \n", 520 | " \n", 521 | " \n", 522 | " \n", 523 | " \n", 524 | " \n", 525 | " \n", 526 | " \n", 527 | "
AccountYearMonthAmount (USD)
0Expenses:Financial:Commissions20181044.75
1Expenses:Financial:Commissions20181135.80
2Expenses:Financial:Commissions20181235.80
3Expenses:Financial:Commissions2019535.80
4Expenses:Financial:Commissions201968.95
\n", 528 | "
" 529 | ], 530 | "text/plain": [ 531 | " Account Year Month Amount (USD)\n", 532 | "0 Expenses:Financial:Commissions 2018 10 44.75\n", 533 | "1 Expenses:Financial:Commissions 2018 11 35.80\n", 534 | "2 Expenses:Financial:Commissions 2018 12 35.80\n", 535 | "3 Expenses:Financial:Commissions 2019 5 35.80\n", 536 | "4 Expenses:Financial:Commissions 2019 6 8.95" 537 | ] 538 | }, 539 | "execution_count": 15, 540 | "metadata": {}, 541 | "output_type": "execute_result" 542 | } 543 | ], 544 | "source": [ 545 | "df = df.astype({\"Account\": str, \"Year\": int, \"Month\": int, \"Amount ({})\".format(currency): np.float})\n", 546 | "df = df[[\"Account\", \"Year\", \"Month\", \"Amount ({})\".format(currency)]].fillna(0)\n", 547 | "df.head()" 548 | ] 549 | }, 550 | { 551 | "cell_type": "code", 552 | "execution_count": 16, 553 | "metadata": {}, 554 | "outputs": [ 555 | { 556 | "data": { 557 | "text/html": [ 558 | "
\n", 559 | "\n", 572 | "\n", 573 | " \n", 574 | " \n", 575 | " \n", 576 | " \n", 577 | " \n", 578 | " \n", 579 | " \n", 580 | " \n", 581 | " \n", 582 | " \n", 583 | " \n", 584 | " \n", 585 | " \n", 586 | " \n", 587 | " \n", 588 | " \n", 589 | " \n", 590 | " \n", 591 | " \n", 592 | " \n", 593 | " \n", 594 | " \n", 595 | " \n", 596 | " \n", 597 | " \n", 598 | " \n", 599 | " \n", 600 | " \n", 601 | " \n", 602 | " \n", 603 | " \n", 604 | " \n", 605 | " \n", 606 | " \n", 607 | " \n", 608 | " \n", 609 | " \n", 610 | " \n", 611 | " \n", 612 | " \n", 613 | "
AccountYearMonthAmount (USD)
0Expenses:Financial:Commissions2018-1044.75
1Expenses:Financial:Commissions2018-1135.80
2Expenses:Financial:Commissions2018-1235.80
3Expenses:Financial:Commissions2019-0535.80
4Expenses:Financial:Commissions2019-068.95
\n", 614 | "
" 615 | ], 616 | "text/plain": [ 617 | " Account YearMonth Amount (USD)\n", 618 | "0 Expenses:Financial:Commissions 2018-10 44.75\n", 619 | "1 Expenses:Financial:Commissions 2018-11 35.80\n", 620 | "2 Expenses:Financial:Commissions 2018-12 35.80\n", 621 | "3 Expenses:Financial:Commissions 2019-05 35.80\n", 622 | "4 Expenses:Financial:Commissions 2019-06 8.95" 623 | ] 624 | }, 625 | "execution_count": 16, 626 | "metadata": {}, 627 | "output_type": "execute_result" 628 | } 629 | ], 630 | "source": [ 631 | "df[\"YearMonth\"] = df.apply(lambda x: \"{}-{:0>2d}\".format(x[\"Year\"], x[\"Month\"]),axis=1)\n", 632 | "df = df[[\"Account\", \"YearMonth\", \"Amount ({})\".format(currency)]]\n", 633 | "df.head()" 634 | ] 635 | }, 636 | { 637 | "cell_type": "code", 638 | "execution_count": 17, 639 | "metadata": {}, 640 | "outputs": [ 641 | { 642 | "data": { 643 | "text/html": [ 644 | "
\n", 645 | "\n", 658 | "\n", 659 | " \n", 660 | " \n", 661 | " \n", 662 | " \n", 663 | " \n", 664 | " \n", 665 | " \n", 666 | " \n", 667 | " \n", 668 | " \n", 669 | " \n", 670 | " \n", 671 | " \n", 672 | " \n", 673 | " \n", 674 | " \n", 675 | " \n", 676 | " \n", 677 | " \n", 678 | " \n", 679 | " \n", 680 | " \n", 681 | " \n", 682 | " \n", 683 | " \n", 684 | " \n", 685 | " \n", 686 | " \n", 687 | " \n", 688 | " \n", 689 | " \n", 690 | " \n", 691 | " \n", 692 | " \n", 693 | " \n", 694 | " \n", 695 | " \n", 696 | " \n", 697 | " \n", 698 | "
AccountAmount (USD)
YearMonth
2018-10-31Expenses:Financial:Commissions44.75
2018-11-30Expenses:Financial:Commissions35.80
2018-12-31Expenses:Financial:Commissions35.80
2019-05-31Expenses:Financial:Commissions35.80
2019-06-30Expenses:Financial:Commissions8.95
\n", 699 | "
" 700 | ], 701 | "text/plain": [ 702 | " Account Amount (USD)\n", 703 | "YearMonth \n", 704 | "2018-10-31 Expenses:Financial:Commissions 44.75\n", 705 | "2018-11-30 Expenses:Financial:Commissions 35.80\n", 706 | "2018-12-31 Expenses:Financial:Commissions 35.80\n", 707 | "2019-05-31 Expenses:Financial:Commissions 35.80\n", 708 | "2019-06-30 Expenses:Financial:Commissions 8.95" 709 | ] 710 | }, 711 | "execution_count": 17, 712 | "metadata": {}, 713 | "output_type": "execute_result" 714 | } 715 | ], 716 | "source": [ 717 | "df.index = pd.to_datetime(df[\"YearMonth\"], format='%Y-%m') + pd.offsets.MonthEnd(0)\n", 718 | "df.drop(columns=\"YearMonth\", inplace=True)\n", 719 | "df.head()" 720 | ] 721 | }, 722 | { 723 | "cell_type": "code", 724 | "execution_count": 18, 725 | "metadata": {}, 726 | "outputs": [ 727 | { 728 | "data": { 729 | "text/html": [ 730 | "
\n", 731 | "\n", 744 | "\n", 745 | " \n", 746 | " \n", 747 | " \n", 748 | " \n", 749 | " \n", 750 | " \n", 751 | " \n", 752 | " \n", 753 | " \n", 754 | " \n", 755 | " \n", 756 | " \n", 757 | " \n", 758 | " \n", 759 | " \n", 760 | " \n", 761 | " \n", 762 | " \n", 763 | " \n", 764 | " \n", 765 | " \n", 766 | " \n", 767 | " \n", 768 | " \n", 769 | " \n", 770 | " \n", 771 | " \n", 772 | " \n", 773 | " \n", 774 | " \n", 775 | " \n", 776 | " \n", 777 | " \n", 778 | " \n", 779 | " \n", 780 | " \n", 781 | " \n", 782 | " \n", 783 | " \n", 784 | " \n", 785 | " \n", 786 | " \n", 787 | " \n", 788 | " \n", 789 | " \n", 790 | " \n", 791 | " \n", 792 | " \n", 793 | " \n", 794 | " \n", 795 | " \n", 796 | " \n", 797 | " \n", 798 | " \n", 799 | " \n", 800 | " \n", 801 | " \n", 802 | " \n", 803 | " \n", 804 | " \n", 805 | " \n", 806 | " \n", 807 | " \n", 808 | " \n", 809 | " \n", 810 | " \n", 811 | " \n", 812 | " \n", 813 | " \n", 814 | " \n", 815 | " \n", 816 | " \n", 817 | " \n", 818 | " \n", 819 | " \n", 820 | " \n", 821 | " \n", 822 | " \n", 823 | " \n", 824 | " \n", 825 | " \n", 826 | " \n", 827 | " \n", 828 | " \n", 829 | " \n", 830 | " \n", 831 | " \n", 832 | " \n", 833 | " \n", 834 | " \n", 835 | " \n", 836 | " \n", 837 | " \n", 838 | " \n", 839 | " \n", 840 | " \n", 841 | " \n", 842 | "
Amount (USD)
AccountQuarter
Expenses:Financial:Commissions2018-12-31116.35
2019-06-3044.75
2019-09-3071.60
2019-12-318.95
2020-06-3098.45
2020-09-3062.65
2020-12-3171.60
Expenses:Financial:Fees2018-03-3112.00
2018-06-3012.00
2018-09-3012.00
2018-12-3112.00
2019-03-3112.00
2019-06-3012.00
2019-09-3012.00
2019-12-3112.00
2020-03-3112.00
2020-06-3012.00
2020-09-3012.00
2020-12-3112.00
Expenses:Food:Coffee2018-06-305.49
\n", 843 | "
" 844 | ], 845 | "text/plain": [ 846 | " Amount (USD)\n", 847 | "Account Quarter \n", 848 | "Expenses:Financial:Commissions 2018-12-31 116.35\n", 849 | " 2019-06-30 44.75\n", 850 | " 2019-09-30 71.60\n", 851 | " 2019-12-31 8.95\n", 852 | " 2020-06-30 98.45\n", 853 | " 2020-09-30 62.65\n", 854 | " 2020-12-31 71.60\n", 855 | "Expenses:Financial:Fees 2018-03-31 12.00\n", 856 | " 2018-06-30 12.00\n", 857 | " 2018-09-30 12.00\n", 858 | " 2018-12-31 12.00\n", 859 | " 2019-03-31 12.00\n", 860 | " 2019-06-30 12.00\n", 861 | " 2019-09-30 12.00\n", 862 | " 2019-12-31 12.00\n", 863 | " 2020-03-31 12.00\n", 864 | " 2020-06-30 12.00\n", 865 | " 2020-09-30 12.00\n", 866 | " 2020-12-31 12.00\n", 867 | "Expenses:Food:Coffee 2018-06-30 5.49" 868 | ] 869 | }, 870 | "execution_count": 18, 871 | "metadata": {}, 872 | "output_type": "execute_result" 873 | } 874 | ], 875 | "source": [ 876 | "# Aggregate at quarter level\n", 877 | "df = df.groupby([\"Account\", pd.Grouper(freq='Q')]).sum()\n", 878 | "df.index.rename(names = [\"Account\", \"Quarter\"], inplace=True)\n", 879 | "df.head(20)" 880 | ] 881 | }, 882 | { 883 | "cell_type": "markdown", 884 | "metadata": {}, 885 | "source": [ 886 | "## Pivoting a Table by Quarter" 887 | ] 888 | }, 889 | { 890 | "cell_type": "code", 891 | "execution_count": 19, 892 | "metadata": {}, 893 | "outputs": [ 894 | { 895 | "data": { 896 | "text/html": [ 897 | "
\n", 898 | "\n", 911 | "\n", 912 | " \n", 913 | " \n", 914 | " \n", 915 | " \n", 916 | " \n", 917 | " \n", 918 | " \n", 919 | " \n", 920 | " \n", 921 | " \n", 922 | " \n", 923 | " \n", 924 | " \n", 925 | " \n", 926 | " \n", 927 | " \n", 928 | " \n", 929 | " \n", 930 | " \n", 931 | " \n", 932 | " \n", 933 | " \n", 934 | " \n", 935 | " \n", 936 | " \n", 937 | " \n", 938 | " \n", 939 | " \n", 940 | " \n", 941 | " \n", 942 | " \n", 943 | " \n", 944 | " \n", 945 | " \n", 946 | " \n", 947 | " \n", 948 | " \n", 949 | " \n", 950 | " \n", 951 | " \n", 952 | " \n", 953 | " \n", 954 | " \n", 955 | " \n", 956 | " \n", 957 | " \n", 958 | " \n", 959 | " \n", 960 | " \n", 961 | " \n", 962 | " \n", 963 | " \n", 964 | " \n", 965 | " \n", 966 | " \n", 967 | " \n", 968 | " \n", 969 | " \n", 970 | " \n", 971 | " \n", 972 | " \n", 973 | " \n", 974 | " \n", 975 | " \n", 976 | " \n", 977 | " \n", 978 | " \n", 979 | " \n", 980 | " \n", 981 | " \n", 982 | " \n", 983 | " \n", 984 | " \n", 985 | " \n", 986 | " \n", 987 | " \n", 988 | " \n", 989 | " \n", 990 | " \n", 991 | " \n", 992 | " \n", 993 | " \n", 994 | " \n", 995 | " \n", 996 | " \n", 997 | " \n", 998 | " \n", 999 | " \n", 1000 | " \n", 1001 | " \n", 1002 | " \n", 1003 | " \n", 1004 | " \n", 1005 | " \n", 1006 | " \n", 1007 | " \n", 1008 | " \n", 1009 | " \n", 1010 | " \n", 1011 | " \n", 1012 | " \n", 1013 | " \n", 1014 | " \n", 1015 | " \n", 1016 | " \n", 1017 | "
AccountAmount (USD)
Quarter2018-Q12018-Q22018-Q32018-Q42019-Q12019-Q22019-Q32019-Q42020-Q12020-Q22020-Q32020-Q4
0Expenses:Financial:Commissions0.000.000.00116.350.0044.7571.608.950.0098.4562.6571.60
1Expenses:Financial:Fees12.0012.0012.0012.0012.0012.0012.0012.0012.0012.0012.0012.00
2Expenses:Food:Coffee0.005.490.000.000.000.0036.760.000.000.0043.070.00
3Expenses:Food:Groceries582.97559.27616.30540.30480.78722.67520.40641.20711.49581.02442.03557.04
4Expenses:Food:Restaurant948.18948.241139.921027.88983.151127.471780.951064.271109.251143.041214.80933.98
\n", 1018 | "
" 1019 | ], 1020 | "text/plain": [ 1021 | " Account Amount (USD) \n", 1022 | "Quarter 2018-Q1 2018-Q2 2018-Q3 2018-Q4 2019-Q1 2019-Q2 2019-Q3 2019-Q4 2020-Q1 2020-Q2 2020-Q3 2020-Q4\n", 1023 | "0 Expenses:Financial:Commissions 0.00 0.00 0.00 116.35 0.00 44.75 71.60 8.95 0.00 98.45 62.65 71.60\n", 1024 | "1 Expenses:Financial:Fees 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00\n", 1025 | "2 Expenses:Food:Coffee 0.00 5.49 0.00 0.00 0.00 0.00 36.76 0.00 0.00 0.00 43.07 0.00\n", 1026 | "3 Expenses:Food:Groceries 582.97 559.27 616.30 540.30 480.78 722.67 520.40 641.20 711.49 581.02 442.03 557.04\n", 1027 | "4 Expenses:Food:Restaurant 948.18 948.24 1139.92 1027.88 983.15 1127.47 1780.95 1064.27 1109.25 1143.04 1214.80 933.98" 1028 | ] 1029 | }, 1030 | "execution_count": 19, 1031 | "metadata": {}, 1032 | "output_type": "execute_result" 1033 | } 1034 | ], 1035 | "source": [ 1036 | "df = df.pivot_table(index=\"Account\", columns=df.index.get_level_values(1).map(lambda s: s.strftime('%Y-%m')).astype('period[Q]').strftime('%F-Q%q')).fillna(0).reset_index()\n", 1037 | "df.head()" 1038 | ] 1039 | }, 1040 | { 1041 | "cell_type": "markdown", 1042 | "metadata": {}, 1043 | "source": [ 1044 | "## Creating Multi-Level Accounts" 1045 | ] 1046 | }, 1047 | { 1048 | "cell_type": "code", 1049 | "execution_count": 20, 1050 | "metadata": {}, 1051 | "outputs": [ 1052 | { 1053 | "data": { 1054 | "text/plain": [ 1055 | "6" 1056 | ] 1057 | }, 1058 | "execution_count": 20, 1059 | "metadata": {}, 1060 | "output_type": "execute_result" 1061 | } 1062 | ], 1063 | "source": [ 1064 | "n_levels = df[\"Account\"].str.count(\":\").max() + 1\n", 1065 | "n_levels" 1066 | ] 1067 | }, 1068 | { 1069 | "cell_type": "code", 1070 | "execution_count": 21, 1071 | "metadata": {}, 1072 | "outputs": [ 1073 | { 1074 | "data": { 1075 | "text/plain": [ 1076 | "['Account_L0',\n", 1077 | " 'Account_L1',\n", 1078 | " 'Account_L2',\n", 1079 | " 'Account_L3',\n", 1080 | " 'Account_L4',\n", 1081 | " 'Account_L5']" 1082 | ] 1083 | }, 1084 | "execution_count": 21, 1085 | "metadata": {}, 1086 | "output_type": "execute_result" 1087 | } 1088 | ], 1089 | "source": [ 1090 | "cols = [\"Account_L{}\".format(k) for k in range(n_levels)]\n", 1091 | "cols" 1092 | ] 1093 | }, 1094 | { 1095 | "cell_type": "code", 1096 | "execution_count": 22, 1097 | "metadata": {}, 1098 | "outputs": [ 1099 | { 1100 | "data": { 1101 | "text/html": [ 1102 | "
\n", 1103 | "\n", 1120 | "\n", 1121 | " \n", 1122 | " \n", 1123 | " \n", 1124 | " \n", 1125 | " \n", 1126 | " \n", 1127 | " \n", 1128 | " \n", 1129 | " \n", 1130 | " \n", 1131 | " \n", 1132 | " \n", 1133 | " \n", 1134 | " \n", 1135 | " \n", 1136 | " \n", 1137 | " \n", 1138 | " \n", 1139 | " \n", 1140 | " \n", 1141 | " \n", 1142 | " \n", 1143 | " \n", 1144 | " \n", 1145 | " \n", 1146 | " \n", 1147 | " \n", 1148 | " \n", 1149 | " \n", 1150 | " \n", 1151 | " \n", 1152 | " \n", 1153 | " \n", 1154 | " \n", 1155 | " \n", 1156 | " \n", 1157 | " \n", 1158 | " \n", 1159 | " \n", 1160 | " \n", 1161 | " \n", 1162 | " \n", 1163 | " \n", 1164 | " \n", 1165 | " \n", 1166 | " \n", 1167 | " \n", 1168 | " \n", 1169 | " \n", 1170 | " \n", 1171 | " \n", 1172 | " \n", 1173 | " \n", 1174 | " \n", 1175 | " \n", 1176 | " \n", 1177 | " \n", 1178 | " \n", 1179 | " \n", 1180 | " \n", 1181 | " \n", 1182 | " \n", 1183 | " \n", 1184 | " \n", 1185 | " \n", 1186 | " \n", 1187 | " \n", 1188 | " \n", 1189 | " \n", 1190 | " \n", 1191 | " \n", 1192 | " \n", 1193 | " \n", 1194 | " \n", 1195 | " \n", 1196 | " \n", 1197 | " \n", 1198 | " \n", 1199 | " \n", 1200 | " \n", 1201 | " \n", 1202 | " \n", 1203 | " \n", 1204 | " \n", 1205 | " \n", 1206 | " \n", 1207 | " \n", 1208 | " \n", 1209 | " \n", 1210 | " \n", 1211 | " \n", 1212 | " \n", 1213 | " \n", 1214 | " \n", 1215 | " \n", 1216 | " \n", 1217 | " \n", 1218 | " \n", 1219 | " \n", 1220 | " \n", 1221 | " \n", 1222 | " \n", 1223 | " \n", 1224 | " \n", 1225 | " \n", 1226 | " \n", 1227 | " \n", 1228 | " \n", 1229 | " \n", 1230 | " \n", 1231 | " \n", 1232 | " \n", 1233 | " \n", 1234 | " \n", 1235 | " \n", 1236 | " \n", 1237 | " \n", 1238 | " \n", 1239 | " \n", 1240 | " \n", 1241 | " \n", 1242 | " \n", 1243 | " \n", 1244 | " \n", 1245 | " \n", 1246 | " \n", 1247 | " \n", 1248 | " \n", 1249 | " \n", 1250 | " \n", 1251 | " \n", 1252 | " \n", 1253 | " \n", 1254 | " \n", 1255 | " \n", 1256 | " \n", 1257 | " \n", 1258 | " \n", 1259 | " \n", 1260 | " \n", 1261 | " \n", 1262 | " \n", 1263 | " \n", 1264 | " \n", 1265 | " \n", 1266 | " \n", 1267 | "
Amount (USD)
Quarter2018-Q12018-Q22018-Q32018-Q42019-Q12019-Q22019-Q32019-Q42020-Q12020-Q22020-Q32020-Q4
Account_L0Account_L1Account_L2Account_L3Account_L4Account_L5
ExpensesFinancialCommissions0.000.000.00116.350.0044.7571.608.950.0098.4562.6571.60
Fees12.0012.0012.0012.0012.0012.0012.0012.0012.0012.0012.0012.00
FoodCoffee0.005.490.000.000.000.0036.760.000.000.0043.070.00
Groceries582.97559.27616.30540.30480.78722.67520.40641.20711.49581.02442.03557.04
Restaurant948.18948.241139.921027.88983.151127.471780.951064.271109.251143.041214.80933.98
\n", 1268 | "
" 1269 | ], 1270 | "text/plain": [ 1271 | " Amount (USD) \n", 1272 | "Quarter 2018-Q1 2018-Q2 2018-Q3 2018-Q4 2019-Q1 2019-Q2 2019-Q3 2019-Q4 2020-Q1 2020-Q2 2020-Q3 2020-Q4\n", 1273 | "Account_L0 Account_L1 Account_L2 Account_L3 Account_L4 Account_L5 \n", 1274 | "Expenses Financial Commissions 0.00 0.00 0.00 116.35 0.00 44.75 71.60 8.95 0.00 98.45 62.65 71.60\n", 1275 | " Fees 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00 12.00\n", 1276 | " Food Coffee 0.00 5.49 0.00 0.00 0.00 0.00 36.76 0.00 0.00 0.00 43.07 0.00\n", 1277 | " Groceries 582.97 559.27 616.30 540.30 480.78 722.67 520.40 641.20 711.49 581.02 442.03 557.04\n", 1278 | " Restaurant 948.18 948.24 1139.92 1027.88 983.15 1127.47 1780.95 1064.27 1109.25 1143.04 1214.80 933.98" 1279 | ] 1280 | }, 1281 | "execution_count": 22, 1282 | "metadata": {}, 1283 | "output_type": "execute_result" 1284 | } 1285 | ], 1286 | "source": [ 1287 | "df[cols] = df[\"Account\"].str.split(':', n_levels - 1, expand=True)\n", 1288 | "df = df.fillna('').drop(columns=\"Account\", level=0).set_index(cols)\n", 1289 | "df.head()" 1290 | ] 1291 | }, 1292 | { 1293 | "cell_type": "markdown", 1294 | "metadata": {}, 1295 | "source": [ 1296 | "## Aggregation at Different Account Levels" 1297 | ] 1298 | }, 1299 | { 1300 | "cell_type": "code", 1301 | "execution_count": 23, 1302 | "metadata": {}, 1303 | "outputs": [ 1304 | { 1305 | "data": { 1306 | "text/html": [ 1307 | "\n", 1311 | " \n", 1312 | " \n", 1313 | " \n", 1314 | " \n", 1315 | " \n", 1316 | " \n", 1317 | " \n", 1318 | " \n", 1319 | " \n", 1320 | " \n", 1321 | " \n", 1322 | " \n", 1323 | " \n", 1324 | " \n", 1325 | " \n", 1326 | " \n", 1327 | " \n", 1328 | " \n", 1329 | " \n", 1330 | " \n", 1331 | " \n", 1332 | " \n", 1333 | " \n", 1334 | " \n", 1335 | " \n", 1336 | " \n", 1337 | " \n", 1338 | " \n", 1339 | " \n", 1340 | " \n", 1341 | " \n", 1342 | " \n", 1343 | " \n", 1344 | " \n", 1345 | " \n", 1346 | " \n", 1347 | " \n", 1348 | " \n", 1349 | " \n", 1350 | " \n", 1351 | " \n", 1352 | " \n", 1353 | " \n", 1354 | " \n", 1355 | " \n", 1356 | " \n", 1357 | " \n", 1358 | " \n", 1359 | " \n", 1360 | " \n", 1361 | " \n", 1362 | " \n", 1363 | " \n", 1364 | " \n", 1365 | " \n", 1366 | " \n", 1367 | " \n", 1368 | " \n", 1369 | " \n", 1370 | " \n", 1371 | " \n", 1372 | " \n", 1373 | " \n", 1374 | " \n", 1375 | " \n", 1376 | " \n", 1377 | " \n", 1378 | " \n", 1379 | " \n", 1380 | " \n", 1381 | " \n", 1382 | " \n", 1383 | " \n", 1384 | " \n", 1385 | " \n", 1386 | " \n", 1387 | " \n", 1388 | " \n", 1389 | " \n", 1390 | " \n", 1391 | " \n", 1392 | " \n", 1393 | " \n", 1394 | " \n", 1395 | " \n", 1396 | " \n", 1397 | " \n", 1398 | " \n", 1399 | " \n", 1400 | " \n", 1401 | " \n", 1402 | " \n", 1403 | " \n", 1404 | " \n", 1405 | " \n", 1406 | " \n", 1407 | " \n", 1408 | " \n", 1409 | " \n", 1410 | " \n", 1411 | " \n", 1412 | " \n", 1413 | " \n", 1414 | " \n", 1415 | " \n", 1416 | " \n", 1417 | " \n", 1418 | " \n", 1419 | " \n", 1420 | " \n", 1421 | " \n", 1422 | " \n", 1423 | " \n", 1424 | " \n", 1425 | " \n", 1426 | " \n", 1427 | " \n", 1428 | " \n", 1429 | " \n", 1430 | " \n", 1431 | " \n", 1432 | " \n", 1433 | "
Amount (USD)
Quarter 2018-Q1 2018-Q2 2018-Q3 2018-Q4 2019-Q1 2019-Q2 2019-Q3 2019-Q4 2020-Q1 2020-Q2 2020-Q3 2020-Q4
Account_L0 Account_L1
ExpensesFinancial12.0012.0012.00128.3512.0056.7583.6020.9512.00110.4574.6583.60
Food1531.151513.001756.221568.181463.931850.142338.111705.471820.741724.061699.901491.02
Health678.30581.40678.30581.40678.30581.40678.30581.40678.30581.40678.30678.30
Home7803.207810.587790.057806.367798.267821.557820.877828.417819.907819.417820.045234.56
Taxes13945.4011953.2013945.4011633.2014854.3911953.2013945.4011633.2014882.1311953.2013945.4013343.86
Transport360.00360.00360.00360.00360.00360.00240.00360.00360.00360.00360.00360.00
Vacation0.000.000.000.000.000.000.000.000.000.000.000.00
IncomeUS-36677.90-31438.20-33927.90-27955.98-36728.80-31368.23-34178.35-27953.46-36793.16-32343.17-34095.51-32699.05
" 1434 | ], 1435 | "text/plain": [ 1436 | "" 1437 | ] 1438 | }, 1439 | "execution_count": 23, 1440 | "metadata": {}, 1441 | "output_type": "execute_result" 1442 | } 1443 | ], 1444 | "source": [ 1445 | "df_L1 = df.groupby([\"Account_L0\", \"Account_L1\"]).sum()\n", 1446 | "df_L1.style.set_table_styles(\n", 1447 | "[{'selector': 'tr:hover',\n", 1448 | " 'props': [('background-color', 'yellow')]}]\n", 1449 | ")" 1450 | ] 1451 | }, 1452 | { 1453 | "cell_type": "code", 1454 | "execution_count": 24, 1455 | "metadata": {}, 1456 | "outputs": [ 1457 | { 1458 | "data": { 1459 | "text/html": [ 1460 | "\n", 1464 | " \n", 1465 | " \n", 1466 | " \n", 1467 | " \n", 1468 | " \n", 1469 | " \n", 1470 | " \n", 1471 | " \n", 1472 | " \n", 1473 | " \n", 1474 | " \n", 1475 | " \n", 1476 | " \n", 1477 | " \n", 1478 | " \n", 1479 | " \n", 1480 | " \n", 1481 | " \n", 1482 | " \n", 1483 | " \n", 1484 | " \n", 1485 | " \n", 1486 | " \n", 1487 | " \n", 1488 | " \n", 1489 | " \n", 1490 | " \n", 1491 | " \n", 1492 | " \n", 1493 | " \n", 1494 | "
Amount (USD)
Quarter 2018-Q1 2018-Q2 2018-Q3 2018-Q4 2019-Q1 2019-Q2 2019-Q3 2019-Q4 2020-Q1 2020-Q2 2020-Q3 2020-Q4
Account_L0
Expenses24330.0522230.1824541.9722077.4925166.8822623.0425106.2822129.4325573.0722548.5224578.2921191.34
Income-36677.90-31438.20-33927.90-27955.98-36728.80-31368.23-34178.35-27953.46-36793.16-32343.17-34095.51-32699.05
" 1495 | ], 1496 | "text/plain": [ 1497 | "" 1498 | ] 1499 | }, 1500 | "execution_count": 24, 1501 | "metadata": {}, 1502 | "output_type": "execute_result" 1503 | } 1504 | ], 1505 | "source": [ 1506 | "df_L0 = df.groupby([\"Account_L0\"]).sum()\n", 1507 | "df_L0.style.set_table_styles(\n", 1508 | "[{'selector': 'tr:hover',\n", 1509 | " 'props': [('background-color', 'yellow')]}]\n", 1510 | ")" 1511 | ] 1512 | }, 1513 | { 1514 | "cell_type": "code", 1515 | "execution_count": 25, 1516 | "metadata": {}, 1517 | "outputs": [ 1518 | { 1519 | "data": { 1520 | "text/html": [ 1521 | "
\n", 1522 | "\n", 1535 | "\n", 1536 | " \n", 1537 | " \n", 1538 | " \n", 1539 | " \n", 1540 | " \n", 1541 | " \n", 1542 | " \n", 1543 | " \n", 1544 | " \n", 1545 | " \n", 1546 | " \n", 1547 | " \n", 1548 | " \n", 1549 | " \n", 1550 | " \n", 1551 | " \n", 1552 | " \n", 1553 | " \n", 1554 | " \n", 1555 | " \n", 1556 | " \n", 1557 | " \n", 1558 | " \n", 1559 | " \n", 1560 | " \n", 1561 | " \n", 1562 | " \n", 1563 | " \n", 1564 | " \n", 1565 | " \n", 1566 | " \n", 1567 | " \n", 1568 | " \n", 1569 | " \n", 1570 | " \n", 1571 | " \n", 1572 | " \n", 1573 | " \n", 1574 | "
Amount (USD)
Quarter2018-Q12018-Q22018-Q32018-Q42019-Q12019-Q22019-Q32019-Q42020-Q12020-Q22020-Q32020-Q4
Net income12347.859208.029385.935878.4911561.928745.199072.075824.0311220.099794.659517.2211507.71
\n", 1575 | "
" 1576 | ], 1577 | "text/plain": [ 1578 | " Amount (USD) \n", 1579 | "Quarter 2018-Q1 2018-Q2 2018-Q3 2018-Q4 2019-Q1 2019-Q2 2019-Q3 2019-Q4 2020-Q1 2020-Q2 2020-Q3 2020-Q4\n", 1580 | "Net income 12347.85 9208.02 9385.93 5878.49 11561.92 8745.19 9072.07 5824.03 11220.09 9794.65 9517.22 11507.71" 1581 | ] 1582 | }, 1583 | "execution_count": 25, 1584 | "metadata": {}, 1585 | "output_type": "execute_result" 1586 | } 1587 | ], 1588 | "source": [ 1589 | "# Net income\n", 1590 | "-df_L0.sum(axis=0).to_frame().rename(columns={0: \"Net income\"}).transpose()" 1591 | ] 1592 | }, 1593 | { 1594 | "cell_type": "code", 1595 | "execution_count": 26, 1596 | "metadata": {}, 1597 | "outputs": [ 1598 | { 1599 | "data": { 1600 | "text/html": [ 1601 | "
\n", 1602 | "\n", 1615 | "\n", 1616 | " \n", 1617 | " \n", 1618 | " \n", 1619 | " \n", 1620 | " \n", 1621 | " \n", 1622 | " \n", 1623 | " \n", 1624 | " \n", 1625 | " \n", 1626 | " \n", 1627 | " \n", 1628 | " \n", 1629 | " \n", 1630 | " \n", 1631 | " \n", 1632 | " \n", 1633 | " \n", 1634 | " \n", 1635 | " \n", 1636 | " \n", 1637 | " \n", 1638 | " \n", 1639 | " \n", 1640 | " \n", 1641 | " \n", 1642 | " \n", 1643 | " \n", 1644 | " \n", 1645 | " \n", 1646 | " \n", 1647 | " \n", 1648 | " \n", 1649 | " \n", 1650 | " \n", 1651 | " \n", 1652 | " \n", 1653 | " \n", 1654 | " \n", 1655 | " \n", 1656 | " \n", 1657 | " \n", 1658 | " \n", 1659 | " \n", 1660 | " \n", 1661 | " \n", 1662 | " \n", 1663 | " \n", 1664 | " \n", 1665 | " \n", 1666 | " \n", 1667 | " \n", 1668 | " \n", 1669 | " \n", 1670 | " \n", 1671 | " \n", 1672 | " \n", 1673 | " \n", 1674 | " \n", 1675 | " \n", 1676 | " \n", 1677 | " \n", 1678 | " \n", 1679 | " \n", 1680 | " \n", 1681 | " \n", 1682 | " \n", 1683 | " \n", 1684 | " \n", 1685 | " \n", 1686 | " \n", 1687 | " \n", 1688 | " \n", 1689 | " \n", 1690 | " \n", 1691 | " \n", 1692 | " \n", 1693 | " \n", 1694 | " \n", 1695 | " \n", 1696 | " \n", 1697 | " \n", 1698 | " \n", 1699 | " \n", 1700 | " \n", 1701 | " \n", 1702 | " \n", 1703 | " \n", 1704 | " \n", 1705 | " \n", 1706 | " \n", 1707 | " \n", 1708 | " \n", 1709 | " \n", 1710 | " \n", 1711 | "
Account_L0level_0QuarterExpensesIncome
0Amount (USD)2018-Q124330.0536677.90
1Amount (USD)2018-Q222230.1831438.20
2Amount (USD)2018-Q324541.9733927.90
3Amount (USD)2018-Q422077.4927955.98
4Amount (USD)2019-Q125166.8836728.80
5Amount (USD)2019-Q222623.0431368.23
6Amount (USD)2019-Q325106.2834178.35
7Amount (USD)2019-Q422129.4327953.46
8Amount (USD)2020-Q125573.0736793.16
9Amount (USD)2020-Q222548.5232343.17
10Amount (USD)2020-Q324578.2934095.51
11Amount (USD)2020-Q421191.3432699.05
\n", 1712 | "
" 1713 | ], 1714 | "text/plain": [ 1715 | "Account_L0 level_0 Quarter Expenses Income\n", 1716 | "0 Amount (USD) 2018-Q1 24330.05 36677.90\n", 1717 | "1 Amount (USD) 2018-Q2 22230.18 31438.20\n", 1718 | "2 Amount (USD) 2018-Q3 24541.97 33927.90\n", 1719 | "3 Amount (USD) 2018-Q4 22077.49 27955.98\n", 1720 | "4 Amount (USD) 2019-Q1 25166.88 36728.80\n", 1721 | "5 Amount (USD) 2019-Q2 22623.04 31368.23\n", 1722 | "6 Amount (USD) 2019-Q3 25106.28 34178.35\n", 1723 | "7 Amount (USD) 2019-Q4 22129.43 27953.46\n", 1724 | "8 Amount (USD) 2020-Q1 25573.07 36793.16\n", 1725 | "9 Amount (USD) 2020-Q2 22548.52 32343.17\n", 1726 | "10 Amount (USD) 2020-Q3 24578.29 34095.51\n", 1727 | "11 Amount (USD) 2020-Q4 21191.34 32699.05" 1728 | ] 1729 | }, 1730 | "execution_count": 26, 1731 | "metadata": {}, 1732 | "output_type": "execute_result" 1733 | } 1734 | ], 1735 | "source": [ 1736 | "# Invert the sign of the \"Income\" account. Negative income is counter-intuitive.\n", 1737 | "df_L0.loc[\"Income\"] = -df_L0.loc[\"Income\"]\n", 1738 | "df_L0 = df_L0.transpose().reset_index()\n", 1739 | "df_L0" 1740 | ] 1741 | }, 1742 | { 1743 | "cell_type": "code", 1744 | "execution_count": 27, 1745 | "metadata": {}, 1746 | "outputs": [ 1747 | { 1748 | "data": { 1749 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAA5EAAAFqCAYAAACDGN5MAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAAsTAAALEwEAmpwYAAA7/UlEQVR4nO3de7xVdZ3/8dcnQFABQSTxCmFeKXEM0xwvlGZq2phYg5o/zfLW0ExZo05mWjaVTkzNZA7eJu+WpZmo5eSYY5qaaGjhLTUQxAsCIgcQUT+/P/Y+tDl7HVjAOWfvw3k9H4/14Oz13Wvtz36LSz9nrfVdkZlIkiRJklTGOxpdgCRJkiSp+7CJlCRJkiSVZhMpSZIkSSrNJlKSJEmSVJpNpCRJkiSpNJtISZIkSVJpvRtdQDPaZJNNcsSIEY0uQ5IkSZIa4qGHHnolM4cWjdlEFhgxYgRTpkxpdBmSJEmS1BARMaO9MS9nlSRJkiSVZhMpSZIkSSrNJlKSJEmSVJpNpCRJkiSpNCfWkSRJktRlli1bxqxZs3j99dcbXUqP169fP7bcckv69OmzWtvZREqSJEnqMrNmzWLAgAGMGDGCiGh0OT1WZjJ37lxmzZrFu971rtXa1stZJUmSJHWZ119/nSFDhthANlhEMGTIkDU6I2wTKUmSJKlL2UA2hzX952ATKUmSJEkqzSZSkiRJklSaTaQkSZKkddrYsWMZPHgwS5cubXQphS6//HL22muvRpdRmrOzdqIRZ9y61vuY/p2PdkAlktYlHlskdQaPLVpXTZ8+nd/+9rdstNFG3HzzzXziE59odEndnmciJUmSJK2zrrzySvbYYw+OO+44rrjiiuXrZ86cyeGHH87QoUMZMmQIEyZMWD52ySWXsOOOOzJgwAB22mknHn74YQAef/xxxo4dy6BBgxg1ahQ333zz8m3Gjh3LpZdeuvx127OLEcGkSZPYdtttGTx4MP/wD/9AZvL4449z8sknc99999G/f38GDRrUiWl0DJtISZIkSeusK6+8kqOPPpqjjz6a22+/nZdeeom33nqLQw45hOHDhzN9+nSef/55xo8fD8BPf/pTzjnnHK688kpee+01br75ZoYMGcKyZcs49NBDOeCAA3j55Zf5wQ9+wNFHH82TTz5ZupZbbrmFBx98kEceeYTrr7+e22+/nR133JFJkybxgQ98gJaWFl599dVOSqLj2ERKkiRJWifdc889zJgxg09+8pO8733vY5tttuHaa6/l97//PbNnz+bf/u3f2HDDDenXr9/ys4aXXnopp512GrvtthsRwbvf/W6GDx/O/fffT0tLC2eccQbrrbceH/rQhzjkkEO47rrrStdzxhlnMGjQILbeems++MEPMnXq1E765p3LeyIlSZIkrZOuuOIKDjjgADbZZBMAjjrqKK644gq22GILhg8fTu/e9e3QzJkz2WabberWz549m6222op3vOOv5+GGDx/O888/X7qeYcOGLf95gw02oKWlZXW+TtOwiZQkSZK0zlmyZAnXX389b7311vLmbenSpbz66qtsuummPPfcc7z55pt1jeRWW23FM888U7e/zTffnJkzZ/L2228vbySfe+45tttuOwA23HBDFi9evPz9L774YulaI2K1v18jeTmrJEmSpHXOTTfdRK9evXjssceYOnUqU6dO5fHHH2fvvffmpptuYrPNNuOMM85g0aJFvP7669x7770AfPazn+W73/0uDz30EJnJ008/zYwZM9h9993ZcMMNOf/881m2bBl33XUXkydPXn4v5S677MKNN97I4sWLefrpp7nssstK17rpppsya9Ys3njjjU7JoqPZREqSJEla51xxxRV8+tOfZuutt2bYsGHLlwkTJnDdddcxefJknn76abbeemu23HJLfvKTnwDwiU98gjPPPJOjjjqKAQMGcNhhhzFv3jzWW289br75Zn75y1+yySab8LnPfY4rr7ySHXbYAYAvfvGLrLfeemy66aYce+yxHH300aVr/dCHPsSoUaMYNmzY8ktvm1lkZqNraDpjxozJKVOmrPV+fN6SpM7gsUVSZ/DYoq7y+OOPs+OOOza6DFW1988jIh7KzDFF23TZmciIuDoiXoiI1yLiqYj4bHX9iIjIiGipWc6q2S4i4ryImFtdzo+ai4ar2/8mIhZHxBMRsX+bzz0qImZExKKIuCkiNu6q7yxJkiRJ65quvJz128CIzBwIfAz4ZkS8r2Z8UGb2ry7n1qw/ETgMGA3sDBwCnFQzfh3wB2AIcCbws4gYChARo4CLgGOATYHFwIWd8N0kSZIkqUfosiYyM6dl5tLWl9Wlfu7cescCEzNzVmY+D0wEjgOIiO2AXYGzM3NJZt4A/BEYV932aGByZt6dmS3AWcDhETGgo76XJEmSJPUkXTqxTkRcGBGLgSeAF4DbaoZnRMSsiPhRRNTeTToKeKTm9SPVda1jz2bmwpWML982M58B3gC2K6jtxIiYEhFT5syZs2ZfUJIkSZLWcV3aRGbm54ABwN7AjcBS4BVgN2A48L7q+DU1m/UHFtS8XgD0r94X2XasdXxAO9u2Ha+t7eLMHJOZY4YOHbr6X06SJEmSeoAuf8RHZr6VmfcAWwKnZGZLZk7JzDcz8yVgAnBARAysbtICDKzZxUCgJSvTyrYdax1f2M62bcclSZIkSauhkc+J7E3xPZGtzxxpnYF1GpVJdVqNrq5rHRvZ5h7HtuPLt42IkUBf4Km1qlySJEmSeqguaSIj4p0RMT4i+kdEr4j4CHAkcGdE7B4R20fEOyJiCPCfwF2Z2XoZ6pXAqRGxRURsDnwJuBwgM58CpgJnR0S/iPg4lRlcb6huew1waETsHREbAt8AbmxzD6UkSZIkqaSuOhOZwCnALGA+8F3gC5n5C2Ak8Csql5j+icp9kkfWbHsRMJnKrKt/Am6trms1HhhT3e93gCMycw5UZoQFTqbSTL5M5V7Iz3XKN5QkSZKkHqB3V3xItanbt52x66g867G9bRM4rboUjU8Hxq5k+2uBa8tXK0nSumHEGbd2yH6mf+ejHbIfSWpPRx2v2lP2ODZixAguvfRS9t9//06tp7vrkiZSkiRJ0rphbRu+Sz62GctmvcrOWw7qmILU5WwipSbg2QJJkqTmcfnll3PppZeyxx57cNlllzFo0CAuvPBCDjroIADmzZvHl770JW6//XaWLFnCvvvuy0033QTAJZdcwnnnnce8efPYa6+9mDRpEptvvjkAEcEPf/hDvve97/Hiiy/yhS98geOOO45PfepTTJs2jQMPPJCrr76a9dZbD4BbbrmFr371q0yfPp2ddtqJSZMmsfPOOzckk1qNnJ1VkiRJkprSAw88wPbbb88rr7zCaaedxmc+8xkqd9rBMcccw+LFi5k2bRovv/wyX/ziFwG48847+Zd/+Reuv/56XnjhBYYPH8748eNX2O+vfvUrHnroIe6//37OP/98TjzxRK655hpmzpzJn/70J667rnKn38MPP8zxxx/PRRddxNy5cznppJP42Mc+xtKlS7s2iAI2kZIkSZLUxvDhwznhhBPo1asXxx57LC+88AIvvfQSL7zwAr/85S+ZNGkSgwcPpk+fPuy7b2X6l2uuuYbjjz+eXXfdlb59+/Ltb3+b++67j+nTpy/f7+mnn87AgQMZNWoU73nPezjggAMYOXIkG220EQcddBB/+MMfgMoZzZNOOondd999eQ19+/bl/vvvb0QcK7CJlCRJkqQ2hg0btvznDTbYAICWlhZmzpzJxhtvzODBg+u2mT17NsOHD1/+un///gwZMoTnn39++bpNN910+c/rr79+3euWlhYAZsyYwcSJExk0aNDyZebMmcyePbvjvuQa8p5ISZIkSSppq622Yt68ebz66qsMGjRohbHNN9+cGTNmLH+9aNEi5s6dyxZbbLFGn3PmmWdy5plnrm3JHc4zkZIkSZJU0mabbcZBBx3E5z73OebPn8+yZcu4++67ATjqqKP40Y9+xNSpU1m6dClf+cpX2H333RkxYsRqf84JJ5zApEmTeOCBB8hMFi1axK233srChQs7+ButPs9ESpIkSWqo7jbD/FVXXcUXv/hFdthhB9544w0++MEPss8++7Dffvtx7rnnMm7cOObPn8+ee+7Jj3/84zX6jDFjxnDJJZcwYcIE/vznP7P++uuz1157sc8++3Twt1l9NpGSJEmSBCtMgHPcccetMNY6MyvAxhtvzBVXXFG4j5NPPpmTTz65cKx2HwD33HPPCq+/+c1vrvD6wAMP5MADD1xV2V3OJlKSJEmS1tKjs15d633svOWgtd5HV/CeSEmSJElSaTaRkiRJkqTSvJxVkiRJaseIM25d6310t0ljpFXxTKQkSZIkqTSbSEmSJElSaTaRkiRJkqTSbCIlSZIkSaXZREqSJEmSSnN2VnU5ZzmTJEnSCs7ZqJP3v6DU20aMGMFLL71Er169lq877rjjuOCCCzqrsm7JJlKStE7wF1SSpI4wefJk9t9//0aX0dS8nFWSJEmSVuKUU07hiCOOWP769NNPZ7/99iMzueuuu9hyyy259AcT2XfnbTjoAztz68+vX/7eN5YuZeK5Z/GR3d/DB/9mO879ly/y+pIlADx43z18eLdRXHHRBYzdZVs222wzfvSjHy3f9rbbbmOnnXZiwIABbLHFFnz3u99dPnbLLbewyy67MGjQIPbcc08effTR5WPnnXceW2yxBQMGDGD77bfnf//3fzs0D89ESpIkSdJKTJw4kV122YXLL7+cbbbZhssuu4ypU6cSEQC8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\n", 1750 | "text/plain": [ 1751 | "
" 1752 | ] 1753 | }, 1754 | "metadata": { 1755 | "needs_background": "light" 1756 | }, 1757 | "output_type": "display_data" 1758 | } 1759 | ], 1760 | "source": [ 1761 | "df_L0.columns.name = \"Account\"\n", 1762 | "df_L0.plot.bar(x=\"Quarter\", y=[\"Income\", \"Expenses\"], xlabel=\"Quarter\", ylabel=df_L0[\"level_0\"][0], rot=90)\n", 1763 | "plt.show()" 1764 | ] 1765 | }, 1766 | { 1767 | "cell_type": "markdown", 1768 | "metadata": {}, 1769 | "source": [ 1770 | "## Treemap Plot of Expenses" 1771 | ] 1772 | }, 1773 | { 1774 | "cell_type": "code", 1775 | "execution_count": 28, 1776 | "metadata": {}, 1777 | "outputs": [ 1778 | { 1779 | "data": { 1780 | "text/html": [ 1781 | "
\n", 1782 | "\n", 1795 | "\n", 1796 | " \n", 1797 | " \n", 1798 | " \n", 1799 | " \n", 1800 | " \n", 1801 | " \n", 1802 | " \n", 1803 | " \n", 1804 | " \n", 1805 | " \n", 1806 | " \n", 1807 | " \n", 1808 | " \n", 1809 | " \n", 1810 | " \n", 1811 | " \n", 1812 | " \n", 1813 | " \n", 1814 | " \n", 1815 | " \n", 1816 | " \n", 1817 | " \n", 1818 | " \n", 1819 | " \n", 1820 | " \n", 1821 | " \n", 1822 | " \n", 1823 | " \n", 1824 | " \n", 1825 | " \n", 1826 | " \n", 1827 | " \n", 1828 | " \n", 1829 | " \n", 1830 | " \n", 1831 | " \n", 1832 | " \n", 1833 | " \n", 1834 | " \n", 1835 | " \n", 1836 | " \n", 1837 | " \n", 1838 | " \n", 1839 | " \n", 1840 | " \n", 1841 | " \n", 1842 | " \n", 1843 | " \n", 1844 | "
0
Account_L0Account_L1
ExpensesTaxes157987.98
Home91173.19
Food20461.92
Health7655.10
Transport4200.00
Financial618.35
Vacation0.00
IncomeUS-396159.71
\n", 1845 | "
" 1846 | ], 1847 | "text/plain": [ 1848 | " 0\n", 1849 | "Account_L0 Account_L1 \n", 1850 | "Expenses Taxes 157987.98\n", 1851 | " Home 91173.19\n", 1852 | " Food 20461.92\n", 1853 | " Health 7655.10\n", 1854 | " Transport 4200.00\n", 1855 | " Financial 618.35\n", 1856 | " Vacation 0.00\n", 1857 | "Income US -396159.71" 1858 | ] 1859 | }, 1860 | "execution_count": 28, 1861 | "metadata": {}, 1862 | "output_type": "execute_result" 1863 | } 1864 | ], 1865 | "source": [ 1866 | "tot = df_L1.sum(axis=1).to_frame().sort_values(by=0, ascending=False)\n", 1867 | "tot" 1868 | ] 1869 | }, 1870 | { 1871 | "cell_type": "code", 1872 | "execution_count": 29, 1873 | "metadata": {}, 1874 | "outputs": [ 1875 | { 1876 | "data": { 1877 | "image/png": "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\n", 1878 | "text/plain": [ 1879 | "
" 1880 | ] 1881 | }, 1882 | "metadata": {}, 1883 | "output_type": "display_data" 1884 | } 1885 | ], 1886 | "source": [ 1887 | "data = tot.loc[\"Expenses\"].sort_values(by=0, ascending=False)\n", 1888 | "idx = [k[0] != 0 for k in data.values]\n", 1889 | "values = data.values[idx]\n", 1890 | "labels = data.index[idx]\n", 1891 | "\n", 1892 | "width = 1\n", 1893 | "height = 0.5\n", 1894 | "values_norm = squarify.normalize_sizes(values, width, height)\n", 1895 | "rects = squarify.squarify(values_norm, 0, 0, width, height)\n", 1896 | "\n", 1897 | "fig = plt.figure(figsize=(10, 10))\n", 1898 | "fig.suptitle('Expenses', x=0.5, y=0.55, fontsize=16)\n", 1899 | "axes = [fig.add_axes([rect['x'], rect['y'], rect['dx'], rect['dy'], ]) for rect in rects]\n", 1900 | "\n", 1901 | "for ax, txt, color in zip(axes, labels, plt.cm.Pastel1.colors):\n", 1902 | " ax.text(0.5, 0.5, txt, horizontalalignment='center', verticalalignment='center')\n", 1903 | " ax.set_yticks([])\n", 1904 | " ax.set_xticks([])\n", 1905 | " ax.set_facecolor(color) \n", 1906 | "plt.show()" 1907 | ] 1908 | } 1909 | ], 1910 | "metadata": { 1911 | "kernelspec": { 1912 | "display_name": "Python 3", 1913 | "language": "python", 1914 | "name": "python3" 1915 | }, 1916 | "language_info": { 1917 | "codemirror_mode": { 1918 | "name": "ipython", 1919 | "version": 3 1920 | }, 1921 | "file_extension": ".py", 1922 | "mimetype": "text/x-python", 1923 | "name": "python", 1924 | "nbconvert_exporter": "python", 1925 | "pygments_lexer": "ipython3", 1926 | "version": "3.9.1" 1927 | } 1928 | }, 1929 | "nbformat": 4, 1930 | "nbformat_minor": 4 1931 | } 1932 | --------------------------------------------------------------------------------