RでEDINETから財務データを入手・分析する~⑤前処理編2/3~

記事一覧

はじめに

本記事は前処理編1/3の続きであり、5番目の記事にあたる。本記事で行う作業は、xbrlファイルからのデータ抽出のために必要な機能を補助関数として定義することである。

また、本ブログの執筆に際してこちらの記事を参考にさせていただいた。

本記事の概説

実際の整頓の流れの解説は次の記事に回す。本記事では8つの補助関数を定義しているが、それぞれをどうやって使うかを理解する必要はまだない。それぞれの関数がどんな機能を持って、何をアウトプットするのかが分かれば十分である。

参考記事との比較

参考にさせていただいたこちらの記事との大きな差異は、必要な機能を補助関数として分けたことと、xbrlDoAll()を使っていないことである。前者はコードの可読性とメンテナンスの容易さに資する。xbrlDoAll()は、xbrlファイルを指定すればその中にある情報(factやcontextIdなど)をdata.frameのlistとしてまとめて出してくれる便利な関数であるが、参考先にもある通り1企業あたり1分以上の時間がかかる。

本ブログではlow-level functionを用いて、処理のボトルネックである部分を独立させて1回のみ実行することによって、1企業あたりの整理にかかる時間を1~4秒に短縮した。ただし、筆者はxbrlデータの仕組みについて十分熟知しておらず、また本記事の処理は試行錯誤の結果から導いたものであるため、加工されたデータの網羅性、正確性については一切保証できない。

XBRLpackageのリファレンスマニュアルはこちらである。

Rによる実装

概説で述べた通り、以下の関数群をどうやって使うかはまだ理解不要である。それぞれ何をしたいのかが分かれば次回の記事の理解がスムーズとなる。

get_docID()

解凍したzipファイルのdocIDを取得する関数である。抽出後のデータを"docID.csv"としてtidyupフォルダに保存するために必要となる。インプットはzipファイルのpathと、zipフォルダのpathである。アウトプットにはdocIDを返す。

get_docID <- function(zip, path_zip){
  
  zip %>% 
    str_remove(pattern = path_zip) %>% 
    str_remove(pattern = "/") %>% 
    str_remove(pattern = ".zip") %>% 
    return()
}

get_fact()

xbrlファイルからfactを含むdata.frameを抽出する関数である。流れは

  1. xbrlParse()xbrlファイルを"parse"し*1
  2. xbrlProcess**()で**情報をdata.frameとして抽出したあと
  3. xbrlFree()でメモリを開放する

となっている。インプットはxbrlファイルへのpathであり、アウトプットはfact情報を含むdata.frameである。

get_fact <- function(path_xbrl){
  
  parsed <- xbrlParse(path_xbrl)
  
  fact <- xbrlProcessFacts(parsed)
  
  xbrlFree(parsed)
  
  ##
  
  fact %>% 
    as_tibble() %>% 
    mutate_at("fact", str_conv, encoding = "UTF-8") %>% 
    mutate_if(is.factor, as.character) %>% 
    return()
}

get_context()

get_fact()と同様。contextIdの入手のために必要である。

get_context <- function(path_xbrl){
  
  parsed <- xbrlParse(path_xbrl)
  
  context <- xbrlProcessContexts(parsed)
  
  xbrlFree(parsed)
  
  context %>% 
    as_tibble() %>% 
    mutate_if(is.factor, as.character) %>% 
    return()
}

get_definition()

基本的にget_fact()と同様。roleIdの入手のために必要である。definition情報はxbrlファイルではなくxmlファイルに入っているため、その辺りを修正している。ただし統一化のために、インプットはget_fact()と同様、xbrlファイルへのpathとしている。アウトプットはroleIdを含むdata.frameである。

get_definition <- function(path_xbrl, fs_name){
  
  path_pubdoc <- dirname(path_xbrl)
  
  path_def <- 
    dir(path_pubdoc, full.names = TRUE) %>% 
    str_subset(pattern = "def.xml$") %>% 
    str_subset(pattern = "ifrs", negate = TRUE)
  
  parsed_def <- xbrlParse(path_def)
  
  definition <- xbrlProcessArcs(parsed_def, arcType = "definition")
  
  xbrlFree(parsed_def)
  
  definition %>% 
    as_tibble() %>% 
    mutate_all(as.character) %>% 
    rename("elementId" = toElementId) %>% 
    slice(str_which(.$roleId, str_c(fs_name, collapse = "|"))) %>% 
    slice(str_which(.$roleId, "jppfs")) %>% 
    select(elementId, roleId) %>% 
    return()
}

get_ac_standard()

採用している会計基準を返す関数である。インプットにはget_fact()で取得したdata.frameを用いる。アウトプットは

のいずれかを想定している*2

get_ac_standard <- function(joined_fact){
  
  joined_fact %>% 
    slice(str_which(.$elementId, "AccountingStandardsDEI")) %>% 
    purrr::pluck("fact") %>% 
    return()
}

join_fcl()

fact、context, labelStringをそれぞれ含むdata.frameをjoinする関数である。他の関数より定義する必要性は薄いが、次回の記事における統合関数のコードをすっきりさせるために定義した。

join_fcl <- function(joined_fact, joined_context, label){
  
  joined_fact %>% 
    left_join(joined_context, by = "contextId") %>% 
    left_join(label, by = "elementId") %>% 
    return()
}

is_consolidated()

「連結財務諸表の情報をもつ行数」を返す関数である。連結財務諸表作成企業であれば1以上(TRUE)の値をとり、そうでなければ0(FALSE)を返す*3。インプットはjoin_fcl()でjoinしたdata.frameである。

is_consolidated <- function(joined_fcl){
  joined_fcl %>% 
    slice(str_which(.$elementId, "jppfs")) %>%
    slice(str_which(.$contextId, "NonConsolidated", negate = T)) %>%
    nrow() %>% 
    return()
}

tidy_joined_data()

最終的に結合されたdata.frameの調整をする関数である。調整内容は

  1. 不要な列を落とす
  2. ある列に共通して含まれる情報のない文字を削る
  3. 販売費及び一般管理費(SGA)の内訳項目をPL項目扱いとする*4

である。

tidy_joined_data <- function(joined_data){
  
  joined_data %>% 
    select(labelString, fact, startDate, endDate, elementId, contextId, roleId) %>% 
    mutate_at("elementId", str_remove, pattern = "jppfs_cor_") %>% 
    separate(col = roleId, into = c("garbage", "roleId"), sep = "rol_") %>% 
    select(-garbage) %>% 
    mutate(roleId = if_else(is.na(.$roleId) & str_detect(.$elementId, "SGA"), 
                            true = "StatementOfIncome", false = .$roleId)) %>% 
    return()
}

おわりに

次回の記事では、前記事及び本記事の内容を統合した関数を定義し、その実行によって実際にzipファイルを分析可能な形に整頓していく。

*1:C++を呼んでいるため速いらしい。

*2:修正国際会計基準なんて知らない。

*3:子会社を持たず個別財務諸表のみを作成する上場企業も存在する。

*4:これにより人件費や広告宣伝費を分析できる。