# Goodbudget
```elixir
Mix.install([
{:explorer, "~> 0.3.1"},
{:vega_lite, "~> 0.1.5"},
{:kino_vega_lite, "~> 0.1.2"}
])
```
## Setup
`LIVEBOOK_HOME=$(pwd) livebook server`
```elixir
alias Explorer.DataFrame
alias Explorer.Series
# You can change the max_rows to vary how far back into history you go.
df =
DataFrame.from_csv!(
System.get_env("HOME") <> "/Downloads" <> "/history.csv",
max_rows: 1500
)
```
```elixir
date_series =
df[:Date]
|> Series.to_list()
|> Enum.map(fn input ->
input
|> String.split("/")
|> tl()
|> Enum.reverse()
|> List.replace_at(2, "15")
|> Enum.join("-")
end)
|> Series.from_list()
amount_series =
df[:Amount]
|> Series.to_list()
|> Enum.map(fn input ->
input
|> String.replace([",", "."], "")
|> String.to_integer()
|> (fn
cents ->
cents / -100
end).()
|> round()
end)
|> Series.from_list()
:ok
```
```elixir
months_moving_average = 3
df =
df
|> DataFrame.mutate(Date: date_series)
|> DataFrame.mutate(Amount: amount_series)
|> DataFrame.filter_with(&Series.equal(&1["Envelope"], "Fuel & Uber"))
# |> DataFrame.group_by(:Envelope)
|> DataFrame.group_by(:Date)
|> Explorer.DataFrame.summarise(Amount: [:sum])
|> DataFrame.mutate_with(fn data ->
moving_average =
data[:Amount_sum]
|> Series.reverse()
|> Series.window_mean(months_moving_average)
|> Series.reverse()
# I am not sure why the above needed to be done backwards
%{MovingAverage: moving_average}
end)
```
```elixir
# The Average for all history
df[:Amount_sum]
|> Series.mean()
```
<!-- livebook:{"attrs":{"chart_title":"Expenses Per Month","height":300,"layers":[{"chart_type":"bar","color_field":null,"color_field_aggregate":null,"color_field_type":null,"data_variable":"df","x_field":"Date","x_field_aggregate":null,"x_field_type":"nominal","y_field":"Amount_sum","y_field_aggregate":null,"y_field_type":"quantitative"},{"chart_type":"line","color_field":null,"color_field_aggregate":null,"color_field_type":null,"data_variable":"df","x_field":"Date","x_field_aggregate":null,"x_field_type":"nominal","y_field":"MovingAverage","y_field_aggregate":null,"y_field_type":"quantitative"}],"vl_alias":"Elixir.VegaLite","width":800},"kind":"Elixir.KinoVegaLite.ChartCell","livebook_object":"smart_cell"} -->
```elixir
VegaLite.new(width: 800, height: 300, title: "Expenses Per Month")
|> VegaLite.data_from_values(df, only: ["Date", "Amount_sum", "MovingAverage"])
|> VegaLite.layers([
VegaLite.new()
|> VegaLite.mark(:bar)
|> VegaLite.encode_field(:x, "Date", type: :nominal)
|> VegaLite.encode_field(:y, "Amount_sum", type: :quantitative),
VegaLite.new()
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "Date", type: :nominal)
|> VegaLite.encode_field(:y, "MovingAverage", type: :quantitative)
])
```