取得データからデータマートを作ろうとしてみた
※この記事は R Advent Calendar 2014 - Qiita の 19日目の記事です。
前編RでXBRLデータを取得してみた - horioの雑記帳では、XBRL形式での有価証券報告書を、zip形式でローカルに保存しました。
後編の本ポストでは、保存したzipから、表形式での財務諸表データを作成してゆきます。
前・後編全体を通した手順
前編の再掲ですが、このような流れです。
(前編)RでXBRLデータを取得してみた → ここRでXBRLデータを取得してみた - horioの雑記帳
(後編)取得データからデータマートを作ってみた → 本ポスト
プログラム本体
今回も、著者より献本頂いた本のソースが下敷きになっております。下記リンクより、書籍に掲載されいるサンプルコードのDLも可能です。
1. 書籍のサンプルコードからの改変点
- サンプルコードでの「サンプルインスタンス」を、解凍したzip下の"*.xbrl"に置き換える
- 日本語名称は、横持ちさせてから最後に紐付ければ良いので省略
- 連結・単体の両方の要素を取得するように変更。理由は以下の通り
- 単体=連結の企業の場合、連結の要素が全部NULLの場合があるから
- 両方でも一方でも計算負荷はほぼ一緒、かつ後で削除するのは簡単なので
- 処理のボトルネックはCPUなので、福島氏の著書や大仏様のお告げを参考に、並列処理に変更
2. 使い方
以下条件を満たせば、コピペでそのまま動きます。なお今度は、外部サービスへの迷惑はかからないので一安心です。
- 前編でDLしたzipが、プログラムを置いたディレクトリ直下の"data"フォルダー下に全てあること
3. プログラム
INPUT
- 前編でDLしたzip
OUTPUT
- "definfo.txt" -> ID、名称、連結・単体の区分、会計期間。中身はこんな感じ
"ED2013062600002" "株式会社トランスジェニック" NA "CurrentYearNonConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600002" "株式会社トランスジェニック" NA "CurrentYearConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600003" "コムテック株式会社" NA "CurrentYearNonConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600003" "コムテック株式会社" NA "CurrentYearConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600004" "株式会社DTS" NA "CurrentYearNonConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600004" "株式会社DTS" NA "CurrentYearConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600005" "株式会社フライングガーデン" NA "CurrentYearNonConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600006" "㈱寺岡製作所" "TERAOKA SEISAKUSHO CO., LTD" "CurrentYearConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600006" "㈱寺岡製作所" "TERAOKA SEISAKUSHO CO., LTD" "CurrentYearNonConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600007" "(株)大戸屋ホールディングス" "OOTOYA Holdings Co., Ltd." "CurrentYearConsolidatedDuration" "2012-04-01" "2013-03-31" "ED2013062600007" "(株)大戸屋ホールディングス" "OOTOYA Holdings Co., Ltd." "CurrentYearNonConsolidatedDuration" "2012-04-01" "2013-03-31"
- "FI.CurrentYear.txt" -> DLした縦持ち財務諸表。中身はこんな感じ
"ED2013062600002" "jpfr-t-cte_IssuanceOfNewSharesExerciseOfSubscriptionRightsToSharesNA" "CurrentYearNonConsolidatedDuration" "JPY" "2186000" "ED2013062600002" "jpfr-t-cte_NetIncomeNA" "CurrentYearNonConsolidatedDuration" "JPY" "18877000" "ED2013062600002" "jpfr-t-cte_NetChangesOfItemsOtherThanShareholdersEquityNA" "CurrentYearNonConsolidatedDuration" "JPY" "-4391000" "ED2013062600002" "jpfr-t-cte_TotalChangesOfItemsDuringThePeriodNA" "CurrentYearNonConsolidatedDuration" "JPY" "16672000" "ED2013062600002" "jpfr-t-cte_IssuanceOfNewSharesExerciseOfSubscriptionRightsToSharesCAP" "CurrentYearNonConsolidatedDuration" "JPY" "1093000" "ED2013062600002" "jpfr-t-cte_TotalChangesOfItemsDuringThePeriodCAP" "CurrentYearNonConsolidatedDuration" "JPY" "1093000"
ソースコード:"02.perseXBRL.R"
rm(list=ls()) invisible(gc()); invisible(gc()) ##### library ##### library("XBRL") library("foreach") library("doParallel") ##### prerequirement ##### setwd("./data/") # unzip対象リスト取得 str.filelist <- dir(pattern = "*zip") # 並列処理準備 n.cores <- detectCores() cl <- makeCluster(n.cores, type = "PSOCK") registerDoParallel(cl) ##### main ##### rm(f.export.persed) f.export.persed <- function(str.filelist, i){ ### zip2xbrl # zip内ファイル名取得 wk.file.list <- unzip(zipfile = str.filelist[i] ,list = TRUE) # zipファイル解凍 unzip(zipfile = str.filelist[i]) unlink("XbrlDlInfo.csv") # 不要なので即消去 # 拡張子が"xbrl"のファイル名を取得し整形 wk.file.xbrl <- wk.file.list[grep("xbrl", wk.file.list[,1]), 1] wk.file.xbrl <- paste0("./", wk.file.xbrl) ### xbrlをparse ### この一行が非常に重い! ### xbrl.parsed <- xbrlDoAll(wk.file.xbrl) # cleanup # 解凍フォルダを削除 wk.dirname <- unlist(strsplit(wk.file.xbrl,"/"))[2] unlink(wk.dirname, recursive = TRUE) # 一時変数を全削除 rm(list = ls(pattern = "wk*")) ### 文書情報、財務諸表情報を抽出 dat.facts <- xbrl.parsed$fact # 内容(=facts) の文字コードを,UTF-8 からShift-JIS に修正 dat.facts$fact <- iconv(x=dat.facts$fact,from="UTF-8",to="Shift-JIS") ### 文章情報 # 開示者名称を抽出(日英両方) target <- grepl(pattern="^DocumentInfo",x=dat.facts$contextId) dat.Presenter.Info <- dat.facts[target,] dat.Presenter.Info <- dat.Presenter.Info[grepl(pattern="^jpfr-di_EntityName", x=dat.Presenter.Info$elementId), "fact"] # 期首・期末日を抽出(連結・単体) dat.contexts <- xbrl.parsed$context target <- grepl(pattern="^CurrentYear",x=dat.contexts$contextId) dat.contexts <- na.omit(dat.contexts[target, c("contextId","startDate","endDate")]) # 解凍元ファイル名をKeyとして付与 dat.definfo <- cbind(sub(".zip","",str.filelist[i]), dat.Presenter.Info[1], dat.Presenter.Info[2], dat.contexts) # ファイルに追記出力 write.table(dat.definfo, "../definfo.txt" , sep = "\t", col.name = FALSE, row.names = FALSE, append = TRUE) ### 財務諸表データ # 当期要素を抽出(連結・単体) target <- grepl(pattern="^CurrentYear",x=dat.facts$contextId) dat.FI.CurrentYear <- dat.facts[target,] # 解凍元ファイル名をKeyとして付与 dat.FI.CurrentYear <- cbind(sub(".zip","",str.filelist[i]), dat.FI.CurrentYear[,c(1:4)]) # ファイルに追記出力 write.table(dat.FI.CurrentYear, "../FI.CurrentYear.txt" , sep = "\t", row.names = FALSE, col.name = FALSE, append = TRUE) ### cleanup(これをしないとメモリーが逼迫) rm(list = ls(pattern = "^dat|target|^xbrl")) invisible(gc()); invisible(gc()) ### 経過出力 print( c(i, format(Sys.time(), "%m/%d %T")) ) } ##### main ##### foreach(i = 1:length(str.filelist) , .export = ls(envir=parent.frame()) , .packages = c("XBRL") ) %dopar% {f.export.persed(str.filelist, i)} stopCluster(cl)
上記プログラムの問題点
以下4点、個人的な対応コスト順に挙げます。一部の問題は、XBRLの仕様書を読めば解決しそうな気がします*1。
1. 今年以降に提出される、次世代EDINETタクソノミに未対応
次世代EDINETタクソノミは何か?は、金融庁HPをご参照下さい。
重要な点は、上記プログラムは過去分のみにしか通用しないことです。ですので、次世代EDINETタクソノミ用のプログラムを用意する必要があります。なんか悪い予感はしており…。
また、今年の提出物を眺めると、過去形式で提出された模様なのが散見されます。なので、zipを解凍した結果を見てから、適用するのは 上記ソース/新規のプログラム を判定するロジックも考えないといけません。ただ、数ファイルを実際に眺める限りだと、ロジックと実装は簡単そうな気がしております。
3. 会計基準が分からない
現在日本では、日本/米国/IFRSの3基準から選択可能ですが、XBRLのみの情報で、会計基準を判断する情報がまだ分かっておりません。
なお、どの会計基準なのか?の情報は非常に重要です。例えば売上高については*2、以下リンクの住友商事やJTを見ると、会計基準だけで数倍のオーダーで数字が変わります*3。よって、考慮しない訳にはいきません。
4. xbrl::xbrlDoAllが激重
上記プログラムの処理時間は、9割程度がxbrl::xbrlDoAllになります。CPU勝負なので、AWSのc3.largeで2並列処理をかけてみると、1ファイル平均で20秒程度になります*4。東証1/2部、JASDAQ、マザーズで上場企業は約3400社あるので、全企業1年分ならば3/4日ですか。
解決策としては、次の2点を考えてます。
- AWSにお金を積む
高級インスタンス借りれば?と思い実験もしましたが、vCPU/ECUが上がるほど処理は早くなるものの、やはり時間短縮は収穫逓減でした。よってお財布との天秤の結果、コンピューティング最適化シリーズで最安のc3.largeにしました。
ついでに無料枠のt2.microも実験しましたが、1日の処理数が100との計算でした…。精神衛生上、やらないことにしました。
- xbrlDoAllを使わず、XMLで実装する
取得したい項目が決まっているのだから、この筋でも行けるかも?とも思っています。次世代EDINETタクソノミ対応の際に、気が向けば挑戦するかもしれません。
感想
- Elasticsearchの記事で、心がぽっきり折れかかっております
- こういうときは、オレ何やりたかったのだっけ?を今一度考え直すことでしょうか。年末、旅に出ます。。。
Disclaimer
本記事は個人的な勉強のために作成したものです。本情報の内容については万全を期しておりますが、その内容を保証するものではありません。これらの情報によって生じたいかなる損害についても、筆者は一切の責任を負いません。
なお、本資料における意見、見解等はすべて筆者の個人的なものであり、筆者の属する組織の意見、見解等ではないことをご了承ください。