マネーフォワード MEの家計簿データをCSVダウンロードしてPythonで分析してみる
このnoteでは、プログラミング言語Pythonを書ける方向けに、ご自身のマネーフォワード MEの家計簿データをCSVダウンロードして分析する方法をご紹介します。
マネーフォワード MEのプレミアムサービスを利用されている方は、マネーフォワード MEのWebページのログイン後の「家計簿」タブ→「入出金」タブ→「家計簿データの出力(Excel、CSV形式対応)」→「ダウンロード」ボタンから、入出金の収入・支出詳細をダウンロードできます。
この記事の対象読者
プログラミング言語Pythonを書ける方
マネーフォワード MEのプレミアムサービスを利用されている方
上記いずれも当てはまる方です。
CSVデータを読み込む
ダウンロードしたCSVファイルは「収入・支出詳細_yyyy-mm-01_yyyy-mm-dd.csv」というファイル名になっています(ddは月末日付)。作業ディレクトリの下に「mfmedata」というディレクトリを作り、ダウンロードしたCSVファイルをすべて保存しましょう。
path = './mfmedata/'
すべてのCSVファイルを読み込んで結合する関数を用意します。
import os
import pandas as pd
def union_mfme_csv(path, to_csv=False):
if path[-1] != '/':
path = path + '/'
# ディレクトリに保存してあるCSVファイル名をリストに格納
file_list = []
for file_name in os.listdir(path):
if (file_name[-4:] == '.csv') & (file_name[:7] == '収入・支出詳細'):
file_list += [file_name]
# CSVファイルの読み込み
if len(file_list) != 0:
dfs = []
for file_name in file_list:
# utf-8で読み込み
try:
df_tmp = pd.read_csv(path+file_name)
df_tmp['ファイル名'] = file_name
dfs += [df_tmp]
print(file_name, ': utf-8')
except:
# shif-jisで読み込み
try:
df_tmp = pd.read_csv(path+file_name, encoding='shift-jis')
df_tmp['ファイル名'] = file_name
dfs += [df_tmp]
print(file_name, ': shift-jis')
except:
# cp932で読み込み
try:
df_tmp = pd.read_csv(path+file_name, encoding='cp932')
df_tmp['ファイル名'] = file_name
dfs += [df_tmp]
print(file_name, ': cp932')
except:
print(file_name, ' は utf-8, shift-jis, cp932のいずれでも読み込めません。')
pass
# 読み込んだDataFrameの結合
if len(dfs) == 1:
df = dfs
else:
df = pd.concat(dfs, axis=0)
# 日付をTimeStampに変換し並び替え
df['日付'] = pd.to_datetime(df['日付'])
df = df.sort_values('日付')
# CSVファイルに出力
if to_csv:
df.to_csv(path+'uniondata/mf_収入・支出詳細_'+df['日付'].min().strftime('%Y-%m-%d')+'_'+df['日付'].max().strftime('%Y-%m-%d')+'.csv')
return df
else:
print('収入・支出詳細のCSVファイルが見つかりません。')
pass
df = union_mfme_csv(path, to_csv=True)
utf-8, shift-jis, cp932のいずれでも読み込めないCSVファイルは「内容」列に何らかの特殊文字列が入っている可能性が高いため、その文字列を取り除きましょう。
積み上げ棒グラフで支出の時系列推移を可視化する
毎年の支出を時系列の積み上げ棒グラフで可視化します。まず、各種関数を用意します。
# 計算対象のみに絞る関数
def extract_culc_obj(df):
return df[df['計算対象'] == 1]
# 支出をプラスに変換して抽出する関数
def extract_expenditure(df):
df.loc[:, '金額(円)'] = - df['金額(円)']
return df[df['金額(円)'] > 0]
# 収入を抽出する関数
def extract_income(df):
return df[df['金額(円)'] > 0]
# 日付と大項目・中項目でクロス集計する関数
def crosstab_date_category(df, resample_rule='A'):
cols = ['日付', '大項目', '中項目']
crosstab = df[cols+['金額(円)']].groupby(cols).sum()
output = crosstab.unstack(level=[1, 2]).resample(resample_rule).sum().stack(level=[1, 2]).reset_index()
return output
# 計算対象や収入・支出を絞った上で、日付と大項目・中項目でクロス集計する関数
def crosstab_mfdata(df, resample_rule='A', income_or_expenditure='支出'):
# 日付をTimestampに変換
if df['日付'].dtype is not pd._libs.tslibs.timestamps.Timestamp:
df['日付'] = pd.to_datetime(df['日付'])
# 日付と大項目・中項目でクロス集計
if income_or_expenditure == '収入':
output = crosstab_date_category(extract_income(extract_culc_obj(df)), resample_rule=resample_rule)
else:
output = crosstab_date_category(extract_expenditure(extract_culc_obj(df)), resample_rule=resample_rule)
return output
毎年の各支出の時系列データを作成します。
income_or_expenditure = '支出' # 支出または収入
rr = 'A' # A: 年間, Q: 四半期, M: 月次
crosstab = crosstab_mfdata(df, resample_rule=rr, income_or_expenditure=income_or_expenditure)
crosstab
plotlyで可視化します。
import plotly
import plotly.graph_objects as go
def timeseries_bar(data_frame, cat='中項目', display_axis=True):
traces = []
for i in data_frame[cat].unique():
df_tmp = data_frame[data_frame[cat]==i][['日付', '金額(円)']]
traces += [go.Bar(x=df_tmp['日付'], y=df_tmp['金額(円)'], name=i)]
layout = go.Layout(
barmode='stack',
width=1000,
height=600,
)
fig = go.Figure(
data=traces,
layout=layout
)
# 金額を表示する場合
if display_axis:
fig.update_layout(
xaxis = {'ticksuffix': '年'},
yaxis = {
'tickformat': ',.0f',
'ticksuffix': '円'
}
)
else:
fig.update_layout(
xaxis = {'ticksuffix': '年'},
)
fig.update_yaxes(showticklabels=False)
return fig
fig = timeseries_bar(crosstab, cat='大項目', display_axis=False)
fig.show()
さすがに、可視化した個人情報をお見せすることはできないので悪しからず。。一応、display_axis=Falseで、数値を表示しない設定にできるようにはしています。
ツリーマップで支出の内訳を可視化する
マネーフォワード MEのアプリでは円グラフ(ドーナツチャート)で内訳が可視化されていますが、ここではツリーマップで特定期間の支出の内訳を可視化してみます。まず、特定の期間を抽出する関数を用意します。
# 特定の年を抽出する関数
def extract_year(df, year=None, start_year=None, end_year=None):
cond = df['日付'].notnull()
if year:
cond &= df['日付'].dt.year == year
if start_year:
cond &= df['日付'].dt.year >= start_year
if end_year:
cond &= df['日付'].dt.year <= end_year
return df[cond]
plotly.expressで可視化する関数を用意します。
import plotly.express as px
def expenditure_treemap(data_frame,
textinfo_value=True,
textinfo_percent_root=True,
textinfo_percent_parent=True
):
fig = px.treemap(
data_frame,
path=['大項目', '中項目'],
values='金額(円)'
)
textinfo_lst = ["label"]
if textinfo_value:
textinfo_lst += ["value"]
if textinfo_percent_root:
textinfo_lst += ["percent root"]
if textinfo_percent_parent:
textinfo_lst += ["percent parent"]
fig.data[0].textinfo = "+".join(textinfo_lst)
fig.update_layout(width=1120, height=630)
return fig
可視化用のテーブルを用意します。
treemap_df = crosstab_date_category(extract_expenditure(extract_year(extract_culc_obj(df), start_year=2018,end_year=2022)), resample_rule='A')
treemap_df
ツリーマップを可視化します
fig = expenditure_treemap(treemap_df,
textinfo_value=False,
textinfo_percent_root=False,
textinfo_percent_parent=False)
fig.show()
こちらもtextinfo_value=Falseで、数値を表示しない設定にできるようにしています。
積み上げ棒グラフとツリーマップで可視化しましたが、可視化前のcrosstabを使って将来をシミュレーションしてみるなどご活用ください。
おわりに
今回の記事は、執筆者のただの趣味ですが、一応、執筆者はマネーフォワードに所属しています。
マネーフォワードのデータ組織にご興味のある方は、よろしければ「マネーフォワード・データ&AI」マガジンもご覧ください。