見出し画像

MoneytreeでエクスポートしたCSVを集計!【python】【家計簿】

私はMoneytreeで家計簿をつけているのですが、月次の入出金や総資産の推移を1年程度しか表示できない(スクロールする必要がある)という点が不便に感じていました。
長期スパンで収入支出額の推移を一目で確認したいと思い、Moneytreeの有料プランでcsv出力し、pythonで集計・可視化しました。


Moneytreeの有料プランについて

Moneytreeで個人向けの有料プランは二つあります。
CSV出力だけが目的なら1か月だけ契約すれば十分です。

https://getmoneytree.com/jp/app/plans-and-pricing

  • Moneytree Grow

    • 月額300円

    • CSV出力可能期間:昨年の1月以降

  • Moneytree Work

    • 月額500円

    • CSV出力可能期間:全期間

準備

ライブラリのインポート

集計にpandas、可視化にplotly.expressを使用します。

import pandas as pd
import numpy as np
import plotly.express as px

CSVの読み込み

# 複数の期間のファイルを重複期間の重複レコードを除外しつつ読み込み
# 重複期間においてカテゴリ情報など編集していた場合、重複は削除されず二重に計上されるため注意
path_list = ["全ての口座-2018-01-01-2022-09-10.csv","全ての口座-2022-01-01-2023-09-05.csv"]
df = pd.concat([pd.read_csv(path) for path in path_list]).drop_duplicates(keep='last').reset_index(drop=True)

Workプランであれば、全期間一つのcsvファイルで取得できるため、単純に出力された1つのcsvをread_csvするだけでよいです。
私は去年1か月だけWorkプランに加入してその時点の全期間をcsv出力、今年は全期間取得する必要がないためGrowプランで約1年分をcsv出力しました。するとファイルが2つになるので、それぞれ読み込んだあと結合し、重複期間があるため、重複レコードを除外する処理をしています。

前処理

クレカ等の記録からMoneyTree利用開始時点より前のデータが入っている場合があります。今回それは除外したかったので、利用開始日を指定して、それ以降のデータだけ見るようにしています。

また、収入は正の値、支出は負の値になっているのですが、可視化の際収入か支出かわかりやすくするためそれを示す列を追加します。

start_date = '2018-09-01'

df["DATE"]=pd.to_datetime(df["日付"])
df = df[df["DATE"]>start_date].copy() # 使用する期間の指定 
df["収支"]=df["金額"].map(lambda x:"収入" if x>=0 else "支出")

口座間移動による二重計上の回避

銀行口座における「カード返済」の記録は、対応するカードをMoneyTreeに登録している場合そのカード側の記録も存在するので、そのまま利用すると二重に計上されていしまいます。したがって、「カード返済」の記録を除外するようにします。「振替」も同様です。
しかしながら、例外的にペイペイの「振替」は除外しないようにしています。なぜならば、ペイペイの記録はMoneytreeに登録できないため、これが二重に計上されることはないためです。

df_p = df[(~df["カテゴリ"].isin(['カード返済','振替'])|(df["メモ"].str.contains('ペイペイ')))].copy() 

なお、「振替」に関してはすべて自分の口座間の移動という前提の処理になります。私の場合そのパターンが多かったためです。(そうでない場合もたまにありましたが今回は無視)
無視できないほど自分以外への口座間の移動があれば、それは勘定に加える必要があります。

収入と支出

# 月ごとの集計を行う
# 行が「月」、列が「収入」「支出」のテーブルを作る
inout_df = df_p.pivot_table(index=pd.Grouper(key='DATE',freq='MS'),columns='収支',values='金額',aggfunc='sum',fill_value=0)
inout_df["差額"]=inout_df["収入"]+inout_df["支出"]

これでMoneytreeの「現金入出金」のグラフをある程度再現しつつ、長期間まとめて表示できるグラフが作成できました。

# 可視化
fig = px.bar(inout_df[["収入","支出"]])
fig2 = px.line(inout_df[["差額"]],markers=True)
fig2.update_traces(line=dict(color='lightgreen'),marker=dict(color='lightgreen',size=10))
fig.add_traces(fig2.data)
月ごとの収入・支出・差額

箱ひげ図にすると、支出額等がどう変化していったか傾向がわかりやすいかと思います。

inout_df["year"]=inout_df.index.to_series().dt.year
inout_df["支出_inv"] = inout_df["支出"]*-1 # 支出を正の値に変換した列を作る
px.box(inout_df,y="支出_inv",x='year')
月ごとの支出の箱ひげ図

総資産

エクスポートされたcsvに記録されているのはあくまで収入と支出の記録であり、資産がいくらあるか直接知ることができません。
したがって、Moneytreeアプリの「全資産の推移」グラフから、最初の資産を確認し、それに収入と支出の差額を累積していき総資産を推定します。
ただし、証券等の金額の変動はわからないため、Moneytreeで確認できる総資産のグラフを完全には再現できないです。

initial_assets = XXXXX # 「全資産の推移」グラフの最初の金額を入れる(start_date時点の総資産)
assets_df = inout_df["差額"].cumsum().rename("総資産")+initial_assets
px.bar(assets_df)
総資産の推移

長期間確認できると貯金のモチベが上がります!

支出の内訳

支出の内訳も確認できます。

outcat_df = df_p[df_p["収支"]=='支出'].pivot_table(index=pd.Grouper(key='DATE',freq='MS'),
                                               columns='カテゴリ',values='金額',aggfunc='sum',fill_value=0)
outcat_df *= -1
px.bar(outcat_df)
支出の内訳

カテゴリの粒度が細かったので、私はこのあと複数のカテゴリをまとめて、新たなカテゴリ群をつくり再度集計しました。

まとめ

最後にコードをまとめて記載しました。ご参考になれば幸いです。

import pandas as pd
import numpy as np
import plotly.express as px

start_date = '2018-09-01'
initial_assets = XXXXX # 「全資産の推移」グラフの最初の金額を入れる(start_date時点の総資産)

# 複数の期間のファイルを重複期間の重複レコードを除外しつつ読み込み
path_list = ["全ての口座-2018-01-01-2022-09-10.csv","全ての口座-2022-01-01-2023-09-05.csv"]
df = (pd.concat([pd.read_csv(path) for path in path_list])
      .drop_duplicates(keep='last').reset_index(drop=True))

df["DATE"]=pd.to_datetime(df["日付"])
df = df[df["DATE"]>start_date].copy() # 使用する期間の指定 
df["収支"]=df["金額"].map(lambda x:"収入" if x>=0 else "支出")

# 二重計上の回避
condition = (~df["カテゴリ"].isin(['カード返済','振替'])|(df["メモ"].str.contains('ペイペイ')))
df_p = df[condition].copy() 

# 月ごとの収入支出を求める
inout_df = df_p.pivot_table(index=pd.Grouper(key='DATE',freq='MS'),
                            columns='収支',values='金額',aggfunc='sum',fill_value=0)
inout_df["差額"]=inout_df["収入"]+inout_df["支出"]
inout_df["year"]=inout_df.index.to_series().dt.year
inout_df["支出_inv"] = inout_df["支出"]*-1 # 支出を正の値に変換した列を作る

# 総資産
assets_df = inout_df["差額"].cumsum().rename("総資産")+initial_assets

# 支出の内訳を求める
outcat_df = (df_p[df_p["収支"]=='支出']
             .pivot_table(index=pd.Grouper(key='DATE',freq='MS'),
                          columns='カテゴリ',values='金額',
                          aggfunc='sum',fill_value=0)*(-1)


# 可視化
# 月ごとの収入・支出・差額
fig = px.bar(inout_df[["収入","支出"]])
fig2 = px.line(inout_df[["差額"]],markers=True)
fig2.update_traces(line=dict(color='lightgreen'),marker=dict(color='lightgreen',size=10))
fig.add_traces(fig2.data)
fig.show()

# 支出の箱ひげ図
fig = px.box(inout_df,y="支出_inv",x='year')
fig.show()

# 総資産の推移
fig = px.bar(assets_df)
fig.show()

# 支出の内訳
fig = px.bar(outcat_df)
fig.show()



この記事が気に入ったらサポートをしてみませんか?