{"id":14066740,"url":"https://github.com/trevorld/r-ledger","last_synced_at":"2025-04-30T22:25:09.314Z","repository":{"id":56934396,"uuid":"108694000","full_name":"trevorld/r-ledger","owner":"trevorld","description":"Imports data from plain text accounting files","archived":false,"fork":false,"pushed_at":"2024-05-20T16:46:12.000Z","size":371,"stargazers_count":40,"open_issues_count":5,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-30T20:33:45.974Z","etag":null,"topics":["beancount","hledger","ledger","plaintext-accounting"],"latest_commit_sha":null,"homepage":"","language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trevorld.png","metadata":{"files":{"readme":"README.Rrst","changelog":"NEWS.md","contributing":"CONTRIBUTING","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-29T00:30:10.000Z","updated_at":"2025-01-12T17:57:19.000Z","dependencies_parsed_at":"2024-08-13T07:11:25.274Z","dependency_job_id":"79eb5daa-021e-4359-89da-c49271fb737e","html_url":"https://github.com/trevorld/r-ledger","commit_stats":{"total_commits":141,"total_committers":2,"mean_commits":70.5,"dds":0.007092198581560294,"last_synced_commit":"bb4f377b280292e0877e1d1ec0032eaa07fdcb4a"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorld%2Fr-ledger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorld%2Fr-ledger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorld%2Fr-ledger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trevorld%2Fr-ledger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trevorld","download_url":"https://codeload.github.com/trevorld/r-ledger/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251790632,"owners_count":21644253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["beancount","hledger","ledger","plaintext-accounting"],"created_at":"2024-08-13T07:05:14.337Z","updated_at":"2025-04-30T22:25:09.287Z","avatar_url":"https://github.com/trevorld.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"ledger\n======\n\n.. {r setup, echo = FALSE}\nknitr::opts_chunk$set(fig.path = \"man/figures/README-\")\noptions(width=80)\n.. ..\n\n.. |CRAN-status| image:: https://www.r-pkg.org/badges/version/ledger\n    :target: https://cran.r-project.org/package=ledger\n    :alt: CRAN Status Badge\n.. |R-CMD-check| image:: https://github.com/trevorld/r-ledger/workflows/R-CMD-check/badge.svg\n    :target: https://github.com/trevorld/r-ledger/actions\n    :alt: R-CMD-check\n.. |codecov| image:: https://codecov.io/github/trevorld/r-ledger/branch/master/graph/badge.svg\n    :target: https://app.codecov.io/github/trevorld/r-ledger?branch=master\n    :alt: Coverage Status\n.. |downloads| image:: https://cranlogs.r-pkg.org/badges/ledger\n    :target: https://cran.r-project.org/package=ledger\n    :alt: RStudio CRAN mirror downloads\n\n|CRAN-status| |R-CMD-check| |codecov| |downloads|\n\n``ledger`` is an R package to import data from `plain text accounting \u003chttps://plaintextaccounting.org/\u003e`_ software like `Ledger \u003chttps://ledger-cli.org/\u003e`_, `HLedger \u003chttps://hledger.org/\u003e`_, and `Beancount \u003chttps://github.com/beancount/beancount\u003e`_ into an R data frame for convenient analysis, plotting, and export.\n\nRight now it supports reading in the register from ``ledger``, ``hledger``, and ``beancount`` files.  \n\n.. contents::\n\nInstallation\n------------\n\nTo install the last version released to CRAN use the following command in R:\n\n.. code:: r\n\n    install.packages(\"ledger\")\n\nTo install the development version of the ``ledger`` package (and its R package dependencies) use the ``install_github`` function from the ``remotes`` package in R:\n\n.. code:: r\n    \n    install.packages(\"remotes\")\n    remotes::install_github(\"trevorld/r-ledger\")\n\nThis package also has some system dependencies that need to be installed depending on which plaintext accounting files you wish to read to be able to read in:\n\nledger\n    `ledger \u003chttps://ledger-cli.org/\u003e`_ (\u003e= 3.1)\n\nhledger\n    `hledger \u003chttps://hledger.org/\u003e`_ (\u003e= 1.4)\n\nbeancount\n    `beancount \u003chttps://github.com/beancount/beancount\u003e`_ (\u003e= 2.0)\n\nTo install hledger run the following in your shell:\n\n.. code:: bash\n\n    stack update \u0026\u0026 stack install --resolver=lts-14.3 hledger-lib-1.15.2 hledger-1.15.2 hledger-web-1.15 hledger-ui-1.15 --verbosity=error \n\nTo install beancount run the following in your shell:\n\n.. code:: bash\n\n    pip3 install beancount\n\n`Several pre-compiled Ledger binaries are available \u003chttps://ledger-cli.org/download.html\u003e`_ (often found in several open source repos).\n\nTo run the unit tests you'll also need the suggested R package ``testthat``.\n\nExamples\n--------\n\nAPI\n+++\n\nThe main function of this package is ``register`` which reads in the register of a plaintext accounting file.  This package also registers S3 methods so one can use ``rio::import`` to read in a register, a ``net_worth`` convenience function, and a ``prune_coa`` convenience function.\n\n``register()``\n~~~~~~~~~~~~~~\n\nHere are some examples of very basic files stored within the package:\n\n.. {r register}\nlibrary(\"ledger\")\nledger_file \u003c- system.file(\"extdata\", \"example.ledger\", package = \"ledger\") \nregister(ledger_file)\nhledger_file \u003c- system.file(\"extdata\", \"example.hledger\", package = \"ledger\") \nregister(hledger_file)\nbeancount_file \u003c- system.file(\"extdata\", \"example.beancount\", package = \"ledger\") \nregister(beancount_file)\n.. ..\n\nHere is an example reading in a beancount file generated by ``bean-example``:\n\n.. {r register2}\nbean_example_file \u003c- tempfile(fileext = \".beancount\")\nsystem(paste(\"bean-example -o\", bean_example_file), ignore.stderr=TRUE)\ndf \u003c- register(bean_example_file)\nprint(df)\nsuppressPackageStartupMessages(library(\"dplyr\"))\ndplyr::filter(df, grepl(\"Expenses\", account), grepl(\"trip\", tags)) %\u003e% \n    group_by(trip = tags, account) %\u003e% \n    summarize(trip_total = sum(amount), .groups = \"drop\")\n.. ..\n\nUsing ``rio::import()`` and ``rio::convert()``\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nIf one has loaded in the ``ledger`` package one can also use ``rio::import`` to read in the register:\n\n.. {r rio}\ndf2 \u003c- rio::import(bean_example_file)\nall.equal(df, tibble::as_tibble(df2))\n.. ..\n\nThe main advantage of this is that it allows one to use ``rio::convert`` to easily convert plaintext accounting files to several other file formats such as a csv file.  Here is a shell example:\n\n.. code:: bash\n\n    bean-example -o example.beancount\n    Rscript --default-packages=ledger,rio -e 'convert(\"example.beancount\", \"example.csv\")'\n\n``net_worth()``\n~~~~~~~~~~~~~~~\n\nSome examples of using the ``net_worth`` function using the example files from the ``register`` examples:\n\n.. {r net_worth}\ndates \u003c- seq(as.Date(\"2016-01-01\"), as.Date(\"2018-01-01\"), by=\"years\")\nnet_worth(ledger_file, dates)\nnet_worth(hledger_file, dates)\nnet_worth(beancount_file, dates)\ndates \u003c- seq(min(as.Date(df$date)), max(as.Date(df$date)), by=\"years\")\nnet_worth(bean_example_file, dates)\n.. ..\n\n``prune_coa()``\n~~~~~~~~~~~~~~~\n\nSome examples using the ``prune_coa`` function to simplify the \"Chart of Account\" names to a given maximum depth:\n\n.. {r prune_coa}\nsuppressPackageStartupMessages(library(\"dplyr\"))\ndf \u003c- register(bean_example_file) %\u003e% dplyr::filter(!is.na(commodity))\ndf %\u003e% prune_coa() %\u003e% \n    group_by(account, mv_commodity) %\u003e% \n    summarize(market_value = sum(market_value), .groups = \"drop\")\ndf %\u003e% prune_coa(2) %\u003e% \n    group_by(account, mv_commodity) %\u003e%\n    summarize(market_value = sum(market_value), .groups = \"drop\")\n.. ..\n    \nBasic personal accounting reports\n+++++++++++++++++++++++++++++++++\n\nHere is some examples using the functions in the package to help generate\nvarious personal accounting reports of the \nbeancount example generated by ``bean-example``.\n\nFirst we load the (mainly tidyverse) libraries we'll be using and adjusting terminal output:\n\n.. {r setup_things, message=FALSE}\nlibrary(\"ledger\")\nlibrary(\"dplyr\")\nfilter \u003c- dplyr::filter\nlibrary(\"ggplot2\")\nlibrary(\"scales\")\nlibrary(\"tidyr\")\nlibrary(\"zoo\")\nfilename \u003c- tempfile(fileext = \".beancount\")\nsystem(paste(\"bean-example -o\", filename), ignore.stderr=TRUE)\ndf \u003c- register(filename) %\u003e% mutate(yearmon = zoo::as.yearmon(date)) %\u003e%\n      filter(commodity==\"USD\")\nnw \u003c- net_worth(filename)\n.. ..\n\nThen we'll write some convenience functions we'll use over and over again:\n\n.. {r setup2}\nprint_tibble_rows \u003c- function(df) {\n    print(df, n=nrow(df))\n}\ncount_beans \u003c- function(df, filter_str = \"\", ..., \n                        amount = \"amount\",\n                        commodity=\"commodity\", \n                        cutoff=1e-3) {\n    commodity \u003c- sym(commodity)\n    amount_var \u003c- sym(amount)\n    filter(df, grepl(filter_str, account)) %\u003e% \n        group_by(account, !!commodity, ...) %\u003e%\n        summarize(!!amount := sum(!!amount_var), .groups = \"drop\") %\u003e% \n        filter(abs(!!amount_var) \u003e cutoff \u0026 !is.na(!!amount_var)) %\u003e%\n        arrange(desc(abs(!!amount_var)))\n}\n.. ..\n    \nBasic balance sheets\n~~~~~~~~~~~~~~~~~~~~\n\nHere is some basic balance sheets (using the market value of our assets):\n\n.. {r balance}\nprint_balance_sheet \u003c- function(df) {\n    assets \u003c- count_beans(df, \"^Assets\", \n                 amount=\"market_value\", commodity=\"mv_commodity\")\n    print_tibble_rows(assets)\n    liabilities \u003c- count_beans(df, \"^Liabilities\", \n                       amount=\"market_value\", commodity=\"mv_commodity\")\n    print_tibble_rows(liabilities)\n}\nprint(nw)\nprint_balance_sheet(prune_coa(df, 2))\nprint_balance_sheet(df)\n.. ..\n\nBasic net worth chart\n~~~~~~~~~~~~~~~~~~~~~\n\nHere is a basic chart of one's net worth from the beginning of the plaintext accounting file to today by month:\n\n.. {r net_worth_chart, fig.width=5, fig.height=5, fig.cap=\"Basic net worth chart\"}\nnext_month \u003c- function(date) {\n    zoo::as.Date(zoo::as.yearmon(date) + 1/12)\n}\nnw_dates \u003c- seq(next_month(min(df$date)), next_month(Sys.Date()), by=\"months\")\ndf_nw \u003c- net_worth(filename, nw_dates) %\u003e% filter(commodity==\"USD\")\nggplot(df_nw, aes(x=date, y=net_worth, colour=commodity, group=commodity)) + \n  geom_line() + scale_y_continuous(labels=scales::dollar)\n.. ..\n\nBasic income sheets\n~~~~~~~~~~~~~~~~~~~\n\n.. {r income}\n\nmonth_cutoff \u003c- zoo::as.yearmon(Sys.Date()) - 2/12\ncompute_income \u003c- function(df) {\n    count_beans(df, \"^Income\", yearmon) %\u003e% \n        mutate(income = -amount) %\u003e%\n        select(-amount) %\u003e% ungroup()\n}\nprint_income \u003c- function(df) {\n    compute_income(df) %\u003e% \n        filter(yearmon \u003e= month_cutoff) %\u003e%\n        spread(yearmon, income, fill=0) %\u003e%\n        print_tibble_rows()\n}\ncompute_expenses \u003c- function(df) {\n    count_beans(df, \"^Expenses\", yearmon) %\u003e% \n        mutate(expenses = amount) %\u003e%\n        select(-amount) %\u003e% ungroup()\n}\nprint_expenses \u003c- function(df) {\n    compute_expenses(df) %\u003e%\n        filter(yearmon \u003e= month_cutoff) %\u003e%\n        spread(yearmon, expenses, fill=0) %\u003e%\n        print_tibble_rows()\n}\ncompute_total \u003c- function(df) {\nfull_join(compute_income(prune_coa(df)) %\u003e% select(-account),\n          compute_expenses(prune_coa(df)) %\u003e% select(-account), \n          by=c(\"yearmon\", \"commodity\")) %\u003e%\n    mutate(income = ifelse(is.na(income), 0, income),\n           expenses = ifelse(is.na(expenses), 0, expenses),\n           net = income - expenses) %\u003e%\n    gather(type, amount, -yearmon, -commodity)\n}\nprint_total \u003c- function(df) {\n    compute_total(df) %\u003e%\n        filter(yearmon \u003e= month_cutoff) %\u003e%\n        spread(yearmon, amount, fill=0) %\u003e%\n        print_tibble_rows()\n}\nprint_total(df)\nprint_income(prune_coa(df, 2))\nprint_expenses(prune_coa(df, 2))\nprint_income(df)\nprint_expenses(df)\n.. ..\n\nAnd here is a plot of income, expenses, and net income over time:\n\n.. {r income_chart, fig.width=5, fig.height=5, fig.cap=\"Monthly income chart\"}\n    ggplot(compute_total(df), aes(x=yearmon, y=amount, group=commodity, colour=commodity)) +\n      facet_grid(type ~ .) +\n      geom_line() + geom_hline(yintercept=0, linetype=\"dashed\") +\n      scale_x_continuous() + scale_y_continuous(labels=scales::comma) \n.. ..\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrevorld%2Fr-ledger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrevorld%2Fr-ledger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrevorld%2Fr-ledger/lists"}