Skip to content

Instantly share code, notes, and snippets.

@adamnew123456
Created September 2, 2017 22:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adamnew123456/fd664f121af9f84868f5ca597a606998 to your computer and use it in GitHub Desktop.
Save adamnew123456/fd664f121af9f84868f5ca597a606998 to your computer and use it in GitHub Desktop.
Basic analysis of ledger files
#!/usr/bin/Rscript
printf <- function(fmt, ...) cat(sprintf(fmt, ...))
# Reads a Ledger file by doing CSV export on the CLI, and then slurping
# it into R's CSV reader.
#
# start and end can be used to limit which entries are allowed in by time,
# and accounts can be used to limit which accounts are read.
ledger.read <- function(filename,
start=NA,
end=NA,
accounts=NULL) {
args <- c('-f', filename)
if (!is.na(start)) args <- append(args, c('-b', start))
if (!is.na(end)) args <- append(args, c('-e', end))
args <- append(args, c('csv', accounts))
raw <- system2('ledger', args, stdout=TRUE)
conn <- textConnection(raw)
data <- read.csv(
conn,
header=FALSE,
col.names=c(
"date", "tag", "description", "account", "currency",
"amount", "X1", "X2"),
stringsAsFactors=FALSE)
close(conn)
data
}
external <- ledger.read('accounts.ledger')
paydate <- NA
income <- 0
spent <- 0
transfers <- list()
# This is designed to track where the money from each paycheck
# ends up, by recording all account credits that occur since
# each payday. A couple of assumptions on the structure
# of the accounts:
#
# - All work income is in a subaccount of Income:Work:
# - All expenses are in a subaccount of Expenses:
# - Virtual transactions (parenthesized) are ignored
invisible(apply(external,
1,
function (txn) {
now <<- txn[[1]]
account <- txn[[4]]
amount <- as.numeric(txn[[6]])
if (grepl('^Income:Work:', account)) {
if (!is.na(paydate)) {
printf('%s\n', strrep('-', 20))
printf("Between %s And %s\n", paydate, now)
printf(" Income: $%f\n", income)
printf(" Spent: $%f\n\n", spent)
print(t(data.frame(transfers)))
printf("\n\n")
}
income <<- -amount
spent <<- 0
transfers <<- list()
paydate <<- now
} else if (!grepl('^\\(', account)) {
if (grepl('^Expenses:', account)) {
spent <<- spent + amount
}
# Not having something like this would show every transaction
# twice (credits and debits), including transfers to accounts
# which are less interesting like Income
if (amount > 0) {
if (is.null(transfers[[account]])) {
transfers[[account]] <<- 0
}
transfers[[account]] <<- transfers[[account]] + amount
}
}
}))
if (!is.na(paydate)) {
printf('%s\n', strrep('-', 20))
printf("Since %s\n", paydate)
printf(" Income: $%f\n", income)
printf(" Spent: $%f\n\n", spent)
print(t(data.frame(transfers)))
printf("\n\n")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment