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

記事一覧

はじめに

本記事は前処理編2/3の続きであり、6番目の記事にあたる。本記事で行う作業は、前2記事の内容を統合した関数を定義し、実際にzipファイルを分析可能なデータへと整頓することである。

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

考慮事項

詳細な流れはRによる実装の手順に回し、ここでは分析にあたって考慮すべき事項、説明すべき事項を挙げる。

labelStringについて

前記事で、xbrlDoAll()の処理は1分程度かかるとした。筆者の試行錯誤の結果、処理時間のボトルネックxbrlProcessLabels()だと特定した。この関数は前処理編1/3で実行済みのため、処理速度の改善が行えた。

会計基準について

導入編で挙げた通り、本ブログでは日本基準を採用する上場企業のみを抽出することを考える。IFRSとUSGAAPに従う企業のデータを抽出はしないが、どの企業がそれらに従っているかをデータとして保持しておくことは有用と考えられるから、その点をカバーする。

連結財務諸表について

連結財務諸表を作成する上場企業は、有価証券報告書の中で個別と連結2つの財務諸表を報告している。よってxbrlファイル内にも、その両方の情報が混在している。一方で、上場企業には子会社を持たず、連結財務諸表を作成していない企業も存在する。それらの企業にはxbrlファイル内の個別財務諸表の情報のみしかない。

そこで、連結財務諸表を作成する企業は連結財務諸表を、そうではない企業は個別財務諸表を抽出する必要があり、その判断をしなければならない。具体的には、「個別財務諸表にあたらない行」を含む(nrow() > 0)なら連結、そうでないなら(nrow() == 0)個別を取得するようにする。

Rによる実装

手順

詳細な流れは以下の通りである。

  1. for文で使いまわす情報である財務諸表名、提出日の年、labelStringを含むdata.frame(JGAAP_label_YYYY.csv)を用意する。

  2. zipファイルを解凍し、xbrlファイルへのpathを取得する。

  3. fact、context、採用する会計基準を取得する。

  4. 会計基準で場合分けする。メインの処理は日本基準の採用企業である。

  5. fact、context、labelStringをjoinし、連結財務諸表作成企業であるか判断する。その後、判断に応じて適切なdefinitionとjoined_fclを統合したjoined_dataを作成する。

  6. joined_dataに最後の調整を加える。

  7. joined_tidy_dataのlabelStringに欠損があるか調べる*1。欠損がない場合は"docID.csv"としてtidyupフォルダに保存する。ある場合はエラーを出す。

  8. 日本基準を採用していない場合は、そのdocIDと採用する会計基準のみをもつ"**_docID.txt"として保存する。

  9. 解凍後のXBRLファイル群の削除、for文内の変数のクリーンアップを行い、進捗状況をconsoleに表示する。

  10. 2~9の内容を繰り返す。

コード

上記の手順に倣った関数を以下に与える。

tidyup_data <- function(path_zipdata, path_label, path_tidyup, start, end){
  
  print(str_c("start!", now(), sep = ", "))
  
  i <- as.integer(start - 1L)
  
  fs_name <- c("BalanceSheet", "StatementOfIncome", "StatementOfCashFlows",
               "StatementOfComprehensiveIncome", "StatementOfChangesInEquity")

  path_zip <- dirname(path_zipdata) %>% unique()

  filing_year <- 
    path_zip %>%
    str_split("/", simplify = TRUE) %>% 
    str_subset("filing$") %>% 
    str_sub(1L, 4L)
  
  ## label
  label <- 
    dir(path_label, filing_year, full.names = TRUE) %>% 
    str_subset("\\.csv$") %>% 
    read_csv()
  
  for(zip in path_zipdata[start:end]) {
    
    unzip(zipfile = zip,
          exdir = path_zip)
    
    i <- i + 1L
    
    docID <- get_docID(zip, path_zip)
    
    path_xbrl <- 
      dir(str_c(path_zip, "/XBRL/Publicdoc"), pattern = "\\.xbrl", full.names = T) %>% 
      str_subset("ifrs", negate = TRUE)
    
    ## fact
    joined_fact <- get_fact(path_xbrl)
    
    ## context
    joined_context <- get_context(path_xbrl)
    
    ## accounting_standard
    ac_standard <- get_ac_standard(joined_fact)
    
    
    if(ac_standard == "Japan GAAP"){
      
      joined_fcl <- join_fcl(joined_fact, joined_context, label)
      
      ## consolidated or NOT
      if(is_consolidated(joined_fcl)){
        
        ## definition
        joined_definition <- 
          get_definition(path_xbrl, fs_name) %>% 
          slice(str_which(.$roleId, "Consolidated"))
        
        
        joined_data <- 
          joined_fcl %>% 
          left_join(joined_definition, by = "elementId") %>% 
          slice(str_which(.$elementId, "jppfs")) %>% 
          slice(str_which(.$contextId, "NonConsolidated", negate = TRUE)) 
        
      } else {
        
        ## definition
        joined_definition <- 
          get_definition(path_xbrl, fs_name) %>% 
          slice(str_which(.$roleId, "Consolidated", negate = TRUE))  # remove consolidated
        
        
        joined_data <- 
          joined_fcl %>% 
          left_join(joined_definition , by = "elementId") %>%   
          slice(str_which(.$elementId, "jppfs"))
        
      }
      
      joined_tidy_data <- tidy_joined_data(joined_data)
      
      NA_flag <-
        joined_tidy_data %>%
        filter(is.na(labelString)) %>% 
        nrow()
      
      if(NA_flag == 0) {
        
        # save data
        joined_tidy_data %>% 
          write_excel_csv(path = str_c(path_tidyup, "/", docID, ".csv"))
        
      } else {
        
        stop(str_c("No.", i, " there are NA rows in labelString!"))
        
      }
      # JGAAP finished
      
    } else if(ac_standard == "IFRS"){
      
      # save as "IFRS_**.txt"
      tibble(
        docID = docID,
        ac_standard = "IFRS"
      ) %>% 
        write_tsv(path = str_c(path_tidyup, "/", "IFRS_", docID, ".txt"))
      
    } else if(ac_standard == "US GAAP"){
      
      # save as "USGAAP_**.txt"
      tibble(
        docID = docID,
        ac_standard = "US GAAP"
      ) %>% 
        write_tsv(path = str_c(path_tidyup, "/", "USGAAP_", docID, ".txt"))
      
    }
    
    ## clean up
    unlink(str_c(path_zip, "/XBRL"), recursive = TRUE)
    unlink("xbrl.Cache", recursive = TRUE)
    rm(list = ls(pattern = "path_xbrl|^joined|^docID$|^ac_standard$|NA_flag"))
    invisible(gc()) ; invisible(gc())
    
    print(str_c(i, now(), sep = ", "))
    
    # repeat
  }
  
}

tidyup_data(path_zipdata, path_label, path_tidyup, start = 1L, end = length(path_zipdata))

tidyupフォルダ内に"「企業のdocID」.csv"が保存されていれば成功である。以下にその例示を与える。

labelString fact startDate endDate elementId contextId roleId
現金及び預金 1093117000 NA 2018-03-31 CashAndDeposits Prior1YearInstant ConsolidatedBalanceSheet
現金及び預金 2184165000 NA 2019-03-31 CashAndDeposits CurrentYearInstant ConsolidatedBalanceSheet
受取手形及び売掛金 4267755000 NA 2018-03-31 NotesAndAccountsReceivableTrade Prior1YearInstant ConsolidatedBalanceSheet
受取手形及び売掛金 5012113000 NA 2019-03-31 NotesAndAccountsReceivableTrade CurrentYearInstant ConsolidatedBalanceSheet
電子記録債権 746345000 NA 2018-03-31 ElectronicallyRecordedMonetaryClaimsOperatingCA Prior1YearInstant ConsolidatedBalanceSheet
電子記録債権 992825000 NA 2019-03-31 ElectronicallyRecordedMonetaryClaimsOperatingCA CurrentYearInstant ConsolidatedBalanceSheet

なお、日本基準以外を採用する場合の例示は以下である。

docID ac_standard
S100GBVE IFRS

おわりに

次回の記事では、本記事までで取得したデータに企業名や業種の情報を加えて分析する。

*1:laberStringはelementIdをkeyとしてjoinしている。ここで、企業が予め用意されていないelementIdを使っていると、joinできずに欠損となる。2020.6.29に提出されたデータに試してみたところ、繰延税金資産のelementIdについて、4番目の記事で入手したlabelのデータに含まれないelementIdを用いている例を発見した。その場合、"there are NA rows in labelString!"というエラーを吐くようにしている。