端の知識の備忘録

技術メモになりきれない、なにものか達の供養先

【R】 三井住友・楽天のカード明細とUFJの口座明細をRで読み込んで社会人1年目の収支を見てみる

背景

早いもので働き始めて一年も経ってしまったようだ。

学生の時と違うことといえば、とにもかくにもお金が入ってくること。

生まれてきてから溜め込んできた分の欲望を無計画に吐き出すかのように、この1年はとにかく何も考えずにお金を使ったものである。グラボを買った月などはカードの引き落しがその月の給料より高かった気がする。

高い買い物は記憶に残るものの、あまり出費として気に留めない細かい買い物も明らかに増えたし、その「細かい買い物」に該当する金額の幅も広がった。飲むお酒もストロング系からちゃんとした生ビールに、ウイスキーもトリスから酒屋で適当なスコッチを選り取り見取り好きに買い、味の違いを楽しむなんて具合に。酒を趣味にするだなんて少し洒落た話以外、もっと卑近でナードなサムシングの話をするならば、DLSiteの買い物がマジで増えた

いくら使ったか怖くてちゃんと計算してなかったけれども、流石に2年目以降無計画というのは人生楽しけりゃどうにでもなれ思想の私としてもいずれ底しれぬ不安を感じ夜もおちおち寝てられない状態になるのは目に見えているので、Rを使って収支をしっかり可視化してみようというお話。

まとめ

  • 三井住友のアマゾンカード、楽天カード、あと三菱UFJの口座明細のRでの処理方法を書いています

  • 少なくとも私の使っている金融機関で出力できるCSVは基本的にクソUTF-8かと思ったらBOM付きだったり、ひとつのCSVに2つのカードの情報が入っていてヘッダー的な行が途中唐突に挿入されていたり。

    • あと金融機関ごとで全く列に格納される情報もフォーマットも異なるので、それぞれ別々に処理するコードを書かなければならない。標準化とかしないんですかね……。
  • パソコン&ゲームオタクは意外に安上がりな人種。新卒1年目で欲望のままに買い物をしてもちゃんと貯金ができるようだ。

    • なにより会社の寮にいるので光熱費食費住居費が合わせて5万以内というのがでかい。これがなければ貯金額は半分くらいになっただろう
    • 趣味に生きたい人間は、会社を選ぶときにきちんと住宅補助や寮の有無を確認したほうが良いだろうと思いました

CSVをとってくる

それぞれの会員ページからCSVが出力できる。UFJは期間指定で一括で落とせたが、三井住友のVpassと楽天カードはそれぞれ月ごと。

下記のようなフォルダ構成にして置いておきます。

---- rmdファイル
    |---- vpass
    |    L 202004.csv, 202005.csv... ..., 202104.csv
    |---- rakuten
    |    L 202004.csv, 202005.csv... ..., 202104.csv
    |---- UFJ
         L xxxxxxx.csv

CSVの読み込み・前処理

ここが一番大事。楽天カード三井住友カードは12ヶ月分を順繰りに読んでマージ、一つのdfにします。

使う libraryはtidyverseだけで良いので、library(tidyverse)しておく。

三井住友の場合

エンコーディングshift-jisなのでread.csvではfileEncoding="cp932"を指定します。

多分3つの中で一番イケてないcsv。ヘッダーがなく(!)各列になんの情報が入っているのかわからない上、idでの支払いもこんな感じで唐突に挟まる。

支払い者情報のある行だけカンマの数が違うので、そもそも列数が違うエラーが出て読み込みすらままならない。極めてクソである

xx xx 様,xxxx-xxx-xxxx-x***,Amazonマスタークラシック
2020/03/26,xxxx,1628,1,1,1628,
2020/03/30,xxxx,2192,1,1,2192,
xx xx 様,xxxx-xxx-xxxx-x***,三井住友カードiD
2020/03/19,xxxx/iD,267,1,1,267,
2020/03/23,xxxx /iD,3448,1,1,3448,

そのため、読み込みのときは利用日の列が数字で始まるか否かで判定して、filterで唐突に現れる支払い者情報行を弾くことにする。

てなわけで、こんな感じで読み込んでみた。いらない列はselectで省きます。

df_smbc <- data.frame()

file_list <- list.files("smbc/", pattern="csv", full.names=T)

for( i in file_list){
  df_tmp <- read.csv(i,col.names = c("利用日","利用名","利用金額", "item1", "item2","支払総額","note"),skip=1,sep=',', fileEncoding = "cp932") %>% 
            filter(str_detect(利用日, "^[0-9]" )) %>% 
            select(-item1, -item2, -note)

  df_smbc <- rbind(df_smbc, df_tmp)
}

このdf_smbcから

#総額
sum(as.integer(df_smbc$利用金額))
#アマゾンだけ
sum(as.integer(filter(df_smbc, 利用名=="AMAZON.CO.JP")$利用金額))

とかすれば使った金額がわかる。

ちなみに手取り給料6.5ヶ月分くらい使っててビビったのは内緒。やはりちゃんと可視化するのは大事

楽天カードの場合

こちらもなかなかクソなCSVエンコーディングは何ということでしょうUTF-8 with BOM。使いにくいからやめてほしい。

でもまだヘッダーがあるぶんSMBCよりも随分マシである。まあ他のdfと列名の整合性を取るため、自分で列名はつけますが。

ただ分割払いに対応するためなのか、"X月支払金額","X月繰越残高"という列があり、これが月によって変わるのがめんどい。

"利用日","利用店名・商品名","利用者","支払方法","利用金額","支払手数料","支払総額","X月支払金額","X月繰越残高","新規サイン"
"2020/05/27","xxxx","本人","1回払い","40000","0","40000","40000","0","*"
"2020/05/13","xxxx","本人","1回払い","5200","0","5200","5200","0","*"

また、ebayの買い物とかで為替が発生すると、下記のような列が挿入されることがあり邪魔なので、先程同様 利用日の列が数字で始まるか否かのfilterを行います。

"2020/11/20","xxxx利用国USA","本人","1回払い","1000","0","1000","1000","0","*"
"","現地利用額      1000.000変換レート   1.000円","","","","","","","",""

てなわけでエンコーディングと削る列以外はほぼSMBCとおんなじ感じ。

df_rakuten <- data.frame()

file_list <- list.files("rakuten/", pattern="csv", full.names=T)

for( i in file_list){
  df_tmp <- read.csv(i,col.names = c("利用日","利用名","利用者","支払方法","利用金額","支払手数料","支払総額","支払金額","繰越残高","新規サイン"),sep=',', fileEncoding = "UTF-8-BOM") %>% 
            filter(str_detect(利用日, "^[0-9]" )) %>% 
            select(-支払方法, -利用者, -支払手数料, -支払金額, -繰越残高, -新規サイン)  

  df_rakuten <- rbind(df_rakuten, df_tmp)
}
sum(as.integer(df_rakuten$利用金額))

で、楽天カードの利用額もわかる。

こちらが給料2.5ヶ月分でしたので、カードで合わせて給料9ヶ月分も使っていたらしい。怖いね!

三菱UFJの場合

これは12ヶ月分まとめて出せるので、for使わなくて良くて楽ちん。エンコーディングshift-jis。ちゃんとヘッダーもある。

しかし、こいつのめんどくさいところは金額に3桁ごとにカンマが入っているところである。仕方がないのでgsubでカンマを空白に置換し、as.numericで数値化した新規の列を作成することにした。

また、給料や賞与の摘要内容が空になってしまっていたので、if_elseを使って空のときは摘要の内容を入力することにしてみた。

さらに、時系列で残高を折れ線で書いてみたかったので、 gsubで /-にした後as.DateでRで日付として扱えるようにした日付列をdate列として追加した。

df_ufj <- read.csv("ufj/0208339_20210414213549.csv",col.names = c("日付","摘要","摘要内容","支払い金額","預かり金額","差引残高","メモ","未資金化区分","入払区分"), skip=1,sep=',', fileEncoding = "cp932") %>% 
          mutate(payment=as.numeric(sub(",","",支払い金額)), 
                 income=as.numeric(gsub(",","",預かり金額)),
                 balance=as.numeric(gsub(",","",差引残高)),
                 date=as.Date(gsub("/","-",日付)), 
                 item=as.character(if_else(摘要内容=="", 摘要, 摘要内容))
                 )

可視化!

金額わかるのは微妙なので適宜目盛は消していますが、ggplotで可視化。

  • カードの支払いを積み上げ棒グラフで

最初そのままsmbc_dfをグラフにしたら項目数が多すぎて何が何やらわからないグラフになってしまったので、総額のうち占める割合が3%以下の利用名の項目をis_samatsu=Trueとして判定、sonotaという項目に全部ひとまとめにすることに。

ちょっと列名つけるのめんどかったのでperとかperperとかいう適当な列が途中生成されていますが、そこはご愛嬌。

tmp <- df_smbc %>% mutate(per = round(as.integer(利用金額)/sum(as.integer(df_smbc$利用金額))*100, 3)) %>%
                                    group_by(利用名) %>% 
                                    summarise(perper = sum(per))  %>% 
                                    mutate(is_samatsu = if_else(perper<3, TRUE, FALSE))

new_df_smbc <- merge(df_smbc, tmp) %>% filter(is_samatsu==FALSE)

new_df_smbc <- rbind(new_df_smbc, c("others", "9999/99/99", sum(as.integer(filter(merge(df_smbc, tmp), is_samatsu==TRUE)$利用金額)), sum(as.integer(filter(merge(df_smbc, tmp), is_samatsu==TRUE)$利用金額)),"sonota" ))

ggplot(new_df_smbc, aes(x="", y=利用金額, fill=利用名)) + geom_col()

最初めちゃめちゃ使ってたと思っていた例のサイトは、せいぜいヨドバシと同じくらいしか使ってなかったらしい。よかったよかった!

いやでも少なくともRTX3080とRyzen5 5600X買った淀と同じということは、150000近くもいかがわしい何かに使っていたということか?……いや深くは考えないことにしておこう。

f:id:hashicco:20210420222409p:plain

  • 残高の変化を折れ線で

普通にdf_ufjdatebalance列を使って描くだけ。

ggplot(filter(df_ufj, balance!=""), aes(date, balance))  + geom_line() + geom_point()

時々散財の影が見え落ち込みを見せるものの、全体的には右肩上がりの楽しげなグラフである。

f:id:hashicco:20210420221615p:plain

一年頑張った証拠として、皆様もRで収支をグラフ化してみてはいかがでしょうか?