【Python】tabulaで有価証券報告書の概況をデータ利用

プログラム

有価証券報告書内の表をデータとして活用できないか、Pythonで読み込んでみたいと思います。
今回使う有価証券報告書は某夢の国を運営している会社です。
この銘柄を選んだ理由は妻が行きたいと願いつつ、チケットが高額なのであまり
行けていないことから、優待でチケットを入手できれば、と思ったことがきっかけです。

では早速、読み込んでいきたいと思います。
まずはライブラリをimportします。

import pandas as pd
import numpy as np
import tabula

次にtabula-pyを使ってPDFファイルを読み込みます。
ページを指定して、概況、貸借対照表、損益計算書、キャッシュフローをデータにします。

dfs = tabula.read_pdf('test_pdf.pdf', lattice=True, pages=['2', '71-74', '77'])

for df in dfs:
    display(df) # Jupyter notebookの環境で実行可能なコード
#--------------------------------------------------
#	回次						  第 59 期  第 60 期  第 61 期  第 62 期  第 63 期  Unnamed: 0
#0	決算年月					2019年3月	2020年3月	2021年3月	2022年3月	2023年3月	NaN
#1	売上高						百万円				データ割愛
#2	経常利益又は経常損失\r(△)	百万円		
# ⁞

#	Unnamed: 0	Unnamed: 1						  (単位:百万円
#0	NaN			前連結会計年度\r(2022年3月31日)	当連結会計年度\r(2023年3月31日)
#1	資産の部							データ割愛
#2	流動資産	
# ⁞
                             
#	Unnamed: 0	Unnamed: 1						  (単位:百万円
#0	NaN			前連結会計年度\r(2022年3月31日)	当連結会計年度\r(2023年3月31日)
#1	負債の部							データ割愛
#2	流動負債	
# ⁞
                             
#<以下省略>
type(dfs)
#--------------------------------------------------
#list
type(dfs[0])
#--------------------------------------------------
#pandas.core.frame.DataFrame

dfsはist、その中にDataFrameとして格納されている状態であることが確認できます。

まずは企業の概況をデータにしますが、行名などがずれているので気持ちよくないから調整します。

gaikyo = pd.DataFrame(columns=['単位', dfs[0].columns[1], dfs[0].columns[2], dfs[0].columns[3], dfs[0].columns[4], dfs[0].columns[5]],
                     index=[dfs[0].iloc[:, 0]])
gaikyo
#--------------------------------------------------
#							単位	第 59 期	第 60 期	第 61 期	第 62 期	第 63 期
#回次						
#決算年月					  NaN	NaN		NaN		 NaN	   NaN	     NaN
#売上高					  NaN  	NaN		NaN		  NaN		NaN		 NaN
#経常利益又は経常損失\r(△)	NaN	  NaN	  NaN		NaN		  NaN	   NaN
# ⁞

dfs[0].columns[i]みたいな形で楽な記述ができればいいのですが、今の時点では割愛します。
割愛と言いますか、今はアイデアがないので後回しです。
あと、回次のところが不要ですね。

gaikyo.columns
#--------------------------------------------------
#Index(['単位', '第  59  期', '第  60  期', '第  61  期', '第  62  期', '第  63  期'], dtype='object')

columnsにも入っていないようです。
ilocで全行をindexに入れると行名の元々の行名の回次がcolumnsでもないところに入ってしまいます。

回次が気持ち悪いから消していきます。
ついでに\rをreplaceします。

index_name = []
for i in range(0, len(dfs[0])):
    index_name.append(dfs[0].iloc[i, 0].replace('\r', ''))

gaikyo = pd.DataFrame(columns=['単位', dfs[0].columns[1], dfs[0].columns[2], dfs[0].columns[3], dfs[0].columns[4], dfs[0].columns[5]],
                     index=[index_name])
gaikyo
#--------------------------------------------------
#	                           単位   第 59 期	第 60 期	第 61 期	第 62 期	第 63 期
#決算年月                       NaN    NaN	    NaN	      NaN	   NaN	     NaN
#売上高                         NaN    NaN	     NaN	   NaN	    NaN	      NaN
#経常利益又は経常損失(△)	       NaN	  NaN	   NaN	     NaN	  NaN	    NaN
#親会社株主に帰属する…	        NaN    NaN	    NaN	      NaN	   NaN	     NaN
# ⁞
#現金及び現金同等物の期末残高	    NaN	    NaN	     NaN	   NaN	    NaN	      NaN
#従業員数[外、平均臨時雇用者数]	NaN	    NaN	     NaN	   NaN	    NaN	      NaN

使うかどうかもわかりませんが、一番最後の従業員数、平均臨時従業員数も
列を分けれたら収まりがいいので実現させていこうと思います。

index_name = []
for i in range(0, len(dfs[0])-1):
    index_name.append(dfs[0].iloc[i, 0].replace('\r', ''))
    
tmp_list = dfs[0].iloc[-1, 0].replace('\r', '').replace(']', '').split('[')

index_name.extend(tmp_list)

gaikyo = pd.DataFrame(columns=['単位', dfs[0].columns[1], dfs[0].columns[2], dfs[0].columns[3], dfs[0].columns[4], dfs[0].columns[5]],
                     index=[index_name])
gaikyo
#--------------------------------------------------
#	                           単位   第 59 期	第 60 期	第 61 期	第 62 期	第 63 期
#決算年月                       NaN    NaN	    NaN	      NaN	   NaN	     NaN
#売上高                         NaN    NaN	     NaN	   NaN	    NaN	      NaN
#経常利益又は経常損失(△)	       NaN	  NaN	   NaN	     NaN	  NaN	    NaN
#親会社株主に帰属する…	        NaN    NaN	    NaN	      NaN	   NaN	     NaN
# ⁞
#従業員数	                    NaN	   NaN	    NaN	      NaN	    NaN	     NaN
#外、平均臨時雇用者数	           NaN	  NaN	   NaN	     NaN	   NaN	    NaN

決算月は単位がいらないから1列ずらして格納。
その他従業員以外のデータはそのまま格納していきます。

gaikyo.iloc[0, 1:] = dfs[0].iloc[0, 1:6]
for i in range(1, len(dfs[0])-1):
    gaikyo.iloc[i, :] = dfs[0].iloc[i, 1:]
gaikyo
#--------------------------------------------------
#	                           単位   第 59 期	第 60 期	第 61 期	第 62 期	第 63 期
#決算年月                       					データ割愛
#売上高                         
#経常利益又は経常損失(△)	       
#親会社株主に帰属する…	        
# ⁞
#現金及び現金同等物の期末残高	    
#従業員数	                    NaN	   NaN	    NaN	      NaN	    NaN	     NaN
#外、平均臨時雇用者数	           NaN	  NaN	   NaN	     NaN	   NaN	    NaN

従業員数なども入れていきます。

for i in range(0, gaikyo.shape[1]):
    for j in range(-1, -3, -1):
        if i == 0:
            gaikyo.iloc[j, i] = dfs[0].iloc[-1, i+1]
        else:
            tmp_list = dfs[0].iloc[-1, i+1].replace('\r', '').replace(']', '').split('[')
            gaikyo.iloc[j, i] = tmp_list[j]

gaikyo
#--------------------------------------------------
#	                           単位   第 59 期	第 60 期	第 61 期	第 62 期	第 63 期
#決算年月                       					データ割愛
#売上高                         
#経常利益又は経常損失(△)	       
#親会社株主に帰属する…	        
# ⁞
#現金及び現金同等物の期末残高	    
#従業員数	                    
#外、平均臨時雇用者数	           

概況の表はindexと要素は対応した形にできましたので、数字として計算できるように
文字列から数値に変換していきます。
その前にマイナスを表す△を-に置き換え、,を消します。

for i in range(0, gaikyo.shape[0]):
    for j in range(0, gaikyo.shape[1]):
        if type(gaikyo.iloc[i, j]) != float:
            gaikyo.iloc[i, j] = gaikyo.iloc[i, j].replace('△', '-')
            gaikyo.iloc[i, j] = gaikyo.iloc[i, j].replace(',', '')
gaikyo
#--------------------------------------------------
#	                           単位   第 59 期	第 60 期	第 61 期	第 62 期	第 63 期
#決算年月                       					データ割愛
#売上高                         
#経常利益又は経常損失(△)	       
#親会社株主に帰属する…	        
# ⁞
#現金及び現金同等物の期末残高	    
#従業員数	                    
#外、平均臨時雇用者数	           

今、この状態ですと要素の型はstrです。
なので、型を変換します。
PERは利益がマイナスだとハイフンになっているので、データなしとしてnanを入れます。

for i in range(1, gaikyo.shape[0]):
    for j in range(1, gaikyo.shape[1]):
        if '.' in gaikyo.iloc[i, j]:
            gaikyo.iloc[i, j] = float(gaikyo.iloc[i, j])
        elif gaikyo.iloc[i, j] == '-':
            gaikyo.iloc[i, j] = np.nan
        else:
            gaikyo.iloc[i, j] = int(gaikyo.iloc[i, j])

type(gaikyo.iloc[-1, -1])
#--------------------------------------------------
#int

一番最後の要素もintに変わっているので有価証券報告書の最初の方にある概況は
数値として扱えるように変換できました。

同じ書き方をしている表であればファイル名を変えるだけでPythonで扱えますが、
恐らく違いが出てくると思いますので完全自動化は難しそうです。
それでも、こういったプログラムは作っていきたいと考えてます。

有価証券報告書のデータをそのまま載せるわけにはいかないのでデータが
どう変わっていくか示せませんが、興味のある方は試してみてください。

タイトルとURLをコピーしました