🐙

『Python』EDINET上の財務情報を取得・アプリで可視化③

2023/03/01に公開

はじめに

大まかな流れは下の図の通りです。

ここでは、➁で保存したCSVを使用してアプリを作成・可視化するまでの工程を取り扱います。
アプリ作成のライブラリにPythonのStreamlit、可視化のライブラリにPlotlyを使います。
また、Streamlitのマルチページという機能を使って作成しています。
マルチページの説明は以下のサイトが分かりやすいのでそちらを載せておきます。
https://qiita.com/nockn/items/f40a80cc79fcb358083c
https://kajiblo.com/streamlit-maltipage/

ファイル構成
top.py
pages
 ├─ 01_rakus.py
 └─ 02_sony.py
csv
 ├─ rakus_fs.csv
 ├─ sony_fs.csv
 ├─ sony_product.csv
 └─ sony_segment.csv

実行したコード

コードエディタ:Visual Studio Code
Pythonの仮想環境を構築し、その環境下で実行しています。

top.py
import streamlit as st
st.title('トップページ')
st.text('これは各企業の有価証券報告書から財務諸表を取得し、その数値を使って財務指標を可視化するアプリです。\n今回、可視化する指標は以下の通りです。')

st.subheader('損益計算書 項目')
st.text('・売上高  (Net Sales)\n・売上総利益(Gross Profit)\n・営業利益 (Operating Income)\n・経常利益 (Ordinary Income)\n・当期純利益(Net Income)')
st.text('・売上原価      (Cost Of Sales)\n・販売費及び一般管理費(Selling, General And Administrative Expenses)\n・営業外費用     (NonOperating Expenses)\n・特別損失      (Extraordinary Loss)\n・法人税等合計    (Income Taxes)')

st.subheader('収益性分析')
st.text('・売上高総利益率   (Gross Margin)\n・売上高営業利益率  (Operating Margin)\n・総資本当期純利益率 (Return on Assets:ROA)\n・自己資本当期純利益率(Return on Equity:ROE)\n・総資本回転率    (Total Asset Turnover)')

st.subheader('安全性分析')
st.text('・流動比率    (Current Ratio)\n・固定長期適合比率(Fixed Long Term Conformity Ratio)\n・自己資本比率  (Equity Ratio)\n・営業活動によるキャッシュフロー(Cash Flows From Operating Activities)\n・投資活動によるキャッシュフロー(Cash Flows From Investing Activities)\n・財務活動によるキャッシュフロー(Cash Flows From Financing Activities)')

st.subheader('成長性分析')
st.text('・売上高増加率 (Net Sales Growth Ratio)\n・営業利益増加率(Operating Income Growth Ratio)\n・経常利益増加率(Ordinary Income Growth Ratio)\n・総資本増加率 (Liabilities And Net Assets Growth Ratio)\n・純資本増加率 (Net Assets Growth Ratio)')
rakus.py
import os
import pandas as pd
import plotly.graph_objects as go
import streamlit as st

rakus_df = pd.read_csv('./csv/rakus_fs.csv', index_col=['財務諸表','科目'])

bs, pl, cf                      = '連結B/S', '連結P/L', '連結C/F'
# Current Assets, Current Liabilities, Noncurrent Assets, Noncurrent Liabilities
CA, CL, NCA, NCL                = '流動資産合計', '流動負債合計', '固定資産合計', '固定負債合計'
# Shareholders Equity, Valuation And Translation Adjustments, Liabilities And Net Assets, Net Assets
SE, VA, LNA, NA                 = '株主資本合計', 'その他の包括利益累計額合計', '負債純資産合計', '純資産合計'
NS, GP, OPI, ORI, NI            = '売上高', '売上総利益', '営業利益', '経常利益', '当期純利益'
COS, SGA, NOE, EL, IT           = '売上原価', '販売費及び一般管理費', '営業外費用', '特別損失', '法人税等合計'
CFFOA, CFFIA, CFFFA             = '営業活動によるキャッシュフロー', '投資活動によるキャッシュフロー', '財務活動によるキャッシュフロー'
GM, OPM, ORM, ROA, ROE, TAT     = '売上高総利益率', '売上高営業利益率', '売上高経常利益率', 'ROA(総資本利益率)', 'ROE(自己資本利益率)', '総資本回転率'
NSGR, OPIGR, ORIGR, LNAGR, NAGR =  '売上高増加率', '営業利益増加率', '経常利益増加率', '総資本増加率', '純資本増加率'
CR, FLTCR, ER                   = '流動比率', '固定長期適合比率', '自己資本比率'

BS, PL, CF= rakus_df.T[bs], rakus_df.T[pl], rakus_df.T[cf]

st.title('株式会社ラクス 財務諸表分析')
col = st.columns(1)
df = col[0].checkbox(label='財務諸表一覧表示')
if df: st.dataframe(rakus_df)
st.caption('・総資本 = 負債純資産合計 ・自己資本 = 株主資本 + その他の包括利益累計額 ・純資本 = 純資産合計')

#-----------------------------------------------------------------------------------
st.subheader('売上・利益の推移')
st.text('・NS :売上高  (Net Sales)\n・GP :売上総利益(Gross Profit)\n・OPI:営業利益 (Operating Income)\n・ORI:経常利益 (Ordinary Income)\n・NI :当期純利益(Net Income)')
fig = go.Figure()
col = st.columns(5)
ns, gp, opi, ori, ni = col[0].checkbox(label='NS'), col[1].checkbox(label='GP'), col[2].checkbox(label='OPI'), col[3].checkbox(label='ORI'), col[4].checkbox(label='NI')
if ns:  fig.add_trace(go.Bar(x=rakus_df.T.index, y=PL[NS], name=f'NS({NS})', marker_color='deeppink'))
if gp:  fig.add_trace(go.Bar(x=rakus_df.T.index, y=PL[GP], name=f'GP({GP})', marker_color='dodgerblue'))
if opi: fig.add_trace(go.Bar(x=rakus_df.T.index, y=PL[OPI], name=f'OPI({OPI})', marker_color='darkcyan'))
if ori: fig.add_trace(go.Bar(x=rakus_df.T.index, y=PL[ORI], name=f'ORI({ORI})', marker_color='darkviolet'))
if ni:  fig.add_trace(go.Bar(x=rakus_df.T.index, y=PL[NI], name=f'NI({NI})', marker_color='orangered'))
fig.update_traces(hovertemplate='(%{x}, %{y}百万円)', texttemplate='%{y}', textposition='outside')
fig.update_layout(
                  xaxis=dict(title_text='年', title_standoff=25),
                  yaxis=dict(tickformat='None', title_text='百万円'),
                  plot_bgcolor='floralwhite',
                  legend=dict(xanchor='center', yanchor='top', x=0.5, y=1.3, orientation='h', entrywidth=150)
                 )
st.plotly_chart(fig)
#-----------------------------------------------------------------------------------
st.subheader('費用の推移')
st.text('・COS:売上原価      (Cost Of Sales)\n・SGA:販売費及び一般管理費(Selling, General And Administrative Expenses)\n・NOE:営業外費用     (NonOperating Expenses)\n・EL :特別損失      (Extraordinary Loss)\n・IT :法人税等合計    (Income Taxes)')
fig2 = go.Figure()
col = st.columns(5)
cos, sga, noe, el, it = col[0].checkbox(label='COS'), col[1].checkbox(label='SGA'), col[2].checkbox(label='NOE'), col[3].checkbox(label='EL'), col[4].checkbox(label='IT')
if cos: fig2.add_trace(go.Bar(x=rakus_df.T.index, y=PL[COS], name=f'COS({COS})', marker_color='navy'))
if sga: fig2.add_trace(go.Bar(x=rakus_df.T.index, y=PL[SGA], name=f'SGA({SGA})', marker_color='royalblue'))
if noe: fig2.add_trace(go.Bar(x=rakus_df.T.index, y=PL[NOE], name=f'NOE({NOE})', marker_color='deepskyblue'))
if el:  fig2.add_trace(go.Bar(x=rakus_df.T.index, y=PL[EL], name=f'EL({EL})', marker_color='lightblue'))
if it:  fig2.add_trace(go.Bar(x=rakus_df.T.index, y=PL[IT], name=f'IT({IT})', marker_color='darkcyan'))
fig2.update_traces(hovertemplate='(%{x}, %{y}百万円)', texttemplate='%{y}', textposition='outside')
fig2.update_layout(
                   xaxis=dict(title_text='年', title_standoff=25),
                   yaxis=dict(tickformat='None', title_text='百万円'),
                   plot_bgcolor='floralwhite',
                   legend=dict(xanchor='center', yanchor='top', x=0.5, y=1.3, orientation='h')
                  )
st.plotly_chart(fig2)
#-----------------------------------------------------------------------------------
st.subheader('収益性分析')
st.text('・GM :売上高総利益率   (Gross Margin)         = 売上総利益 / 売上高 × 100(%)\n・OPM:売上高営業利益率  (Operating Margin)   = 営業利益  / 売上高 × 100(%)\n・ORM:売上高経常利益率  (Ordinary Margin)    = 経常利益  / 売上高 × 100(%)\n・ROA:総資本当期純利益率 (Return on Assets)   = 当期純利益 / 総資本 × 100(%)\n・ROE:自己資本当期純利益率(Return on Equity)   = 当期純利益 / 自己資本 × 100(%)\n・TAT:総資本回転率    (Total Asset Turnover) = 売上高 / 総資本(回)')
fig3 = go.Figure()
col = st.columns(6)
gm, opm, orm, roa, roe, tat = col[0].checkbox(label='GM'), col[1].checkbox(label='OPM'), col[2].checkbox(label='ORM'), col[3].checkbox(label='ROA'), col[4].checkbox(label='ROE'), col[5].checkbox(label='TAT')
if gm:  fig3.add_trace(go.Scatter(x=rakus_df.T.index, y=((PL[GP] / PL[NS]).values).round(3), name=f'GM({GM})', yaxis='y1', line=dict(color='dodgerblue')))        # 売上総利益 / 売上高
if opm: fig3.add_trace(go.Scatter(x=rakus_df.T.index, y=((PL[OPI] / PL[NS]).values).round(3), name=f'OPM({OPM})', yaxis='y1', line=dict(color='darkcyan'))) # 営業利益  / 売上高
if orm: fig3.add_trace(go.Scatter(x=rakus_df.T.index, y=((PL[ORI] / PL[NS]).values).round(3), name=f'ORM({ORM})', yaxis='y1', line=dict(color='darkviolet')))     # 経常利益  / 売上高
if roa: fig3.add_trace(go.Scatter(x=rakus_df.T.index, y=((PL[NI] / BS[LNA]).values).round(3), name=ROA, yaxis='y1', line=dict(color='orangered'))) # 当期純利益 / 総資本(負債純資産合計)
if roe: fig3.add_trace(go.Scatter(x=rakus_df.T.index, y=((PL[NI] /(BS[SE] + BS[VA])).values).round(3), name=ROE, yaxis='y1', line=dict(color='deeppink'))) # 当期純利益 / 自己資本
if tat: fig3.add_trace(go.Scatter(x=rakus_df.T.index, y=((PL[NS] / BS[LNA]).values).round(2), name=f'TAT({TAT})', yaxis='y2', line=dict(color='black'), hovertemplate='(%{x}, %{y}回)')) # 売上高 / 総資本
fig3.update_layout(
                   yaxis=dict(tickformat='p'),
                   yaxis2=dict(title='回', side='right', overlaying='y'),
                   plot_bgcolor='floralwhite',
                   legend=dict(xanchor='center', yanchor='top', x=0.5, y=1.2, orientation='h')
                  )
st.plotly_chart(fig3)
#-----------------------------------------------------------------------------------
st.subheader('安全性分析')
st.text('・CR   :流動比率     (Current Ratio)            = 流動資産 / 流動負債 × 100(%)\n・FLTCR:固定資産長期適合率(Fixed Long Term Conformity Ratio) = 固定資産 / (自己資本+固定負債) × 100(%)\n・ER   :自己資本比率   (Equity Ratio)             = 自己資本 / 総資本  × 100(%)')
fig4 = go.Figure()
col = st.columns(3)
cr, fltcr, er = col[0].checkbox(label='CR'), col[1].checkbox(label='FLTCR'), col[2].checkbox(label='ER')
if cr:    fig4.add_trace(go.Scatter(x=rakus_df.T.index, y=((BS[CA] / BS[CL]).values).round(3), name=f'CR({CR})', line=dict(color='orangered'))) # 流動資産 / 流動負債
if fltcr: fig4.add_trace(go.Scatter(x=rakus_df.T.index, y=((BS[NCA] / (BS[SE] + BS[VA] + BS[NCL])).values).round(3), name=f'FLTCR({FLTCR})', line=dict(color='darkcyan'))) # 固定資産÷(自己資本+固定負債)
if er:    fig4.add_trace(go.Scatter(x=rakus_df.T.index, y=(((BS[SE] + BS[VA]) / BS[LNA]).values).round(3), name=f'ER({ER})', line=dict(color='slateblue'))) # 自己資本 / 総資本
fig4.update_layout(
                   yaxis=dict(tickformat='p'),
                   plot_bgcolor='floralwhite',
                   legend=dict(xanchor='center', yanchor='top', x=0.5, y=1.2, orientation='h')
                  )
st.plotly_chart(fig4)
st.text('・CFFOA:営業活動によるキャッシュフロー(Cash Flows From Operating Activities)\n・CFFIA:投資活動によるキャッシュフロー(Cash Flows From Investing Activities)\n・CFFFA:財務活動によるキャッシュフロー(Cash Flows From Financing Activities)')
fig5 = go.Figure()
col = st.columns(3)
cffoa, cffia, cfffa = col[0].checkbox(label='CFFOA'), col[1].checkbox(label='CFFIA'), col[2].checkbox(label='CFFFA')
if cffoa: fig5.add_trace(go.Bar(x=rakus_df.T.index, y=CF[CFFOA], name='CFFOA(営業活動によるCF)', marker_color='hotpink'))
if cffia: fig5.add_trace(go.Bar(x=rakus_df.T.index, y=CF[CFFIA], name='CFFIA(投資活動によるCF)', marker_color='darkcyan'))
if cfffa: fig5.add_trace(go.Bar(x=rakus_df.T.index, y=CF[CFFFA], name='CFFFA(財務活動によるCF)', marker_color='royalblue'))
fig5.update_traces(
                   hovertemplate='(%{x}, %{y}百万円)',
                   texttemplate='%{y}',
                   textposition='outside'
                  )
fig5.update_layout(
                   xaxis=dict(title_text='年', title_standoff=25),
                   yaxis=dict(tickformat='None', title_text='百万円'),
                   plot_bgcolor='floralwhite',
                   legend=dict(xanchor='center', yanchor='top', x=0.5, y=1.2, orientation='h')
                  )
st.plotly_chart(fig5)
#-----------------------------------------------------------------------------------
st.subheader('成長性分析')
st.text('・NSGR :売上高増加率 (Net Sales Growth Ratio)           = (当期売上高-前期売上高)   / 前期売上高  × 100(%)\n・OPIGR:営業利益増加率(Operating Income Growth Ratio)      = (当期営業利益-前期営業利益) / 前期営業利益 × 100(%)\n・ORIGR:経常利益増加率(Ordinary Income Growth Ratio)       = (当期経常利益-前期経常利益) / 前期経常利益 × 100(%)\n・LNAGR:総資本増加率 (Liabilities And Net Assets Growth Ratio)= (当期総資本ー前期総資本)   / 前期総資本  × 100(%)\n・NAGR :純資本増加率 (Net Assets Growth Ratio)          = (当期末純資本残高ー前期末純資本残高) / 前期末純資本残高 × 100(%)')
fig6 = go.Figure()
col = st.columns(5)
nsgr, opigr, origr, lnagr, nagr = col[0].checkbox(label='NSGR'), col[1].checkbox(label='OPIGR'), col[2].checkbox(label='ORIGR'), col[3].checkbox(label='LNAGR'), col[4].checkbox(label='NAGR')
if nsgr:
    # 売上高増加率	(当期売上高-前期売上高)÷前期売上高
    l = [(PL[NS][i] - PL[NS][i-1]) / PL[NS][i-1] for i in range(1,5)]
    fig6.add_trace(go.Scatter(x=rakus_df.T.index[1:], y= pd.Series(l).values.round(3), name=f'NSGR({NSGR})', line=dict(color='deeppink')))
if opigr:
    # 営業利益増加率 (当期営業利益-前期営業利益)÷前期営業利益
    l = [(PL[OPI][i] - PL[OPI][i-1]) / PL[OPI][i-1] for i in range(1,5)]
    fig6.add_trace(go.Scatter(x=rakus_df.T.index[1:], y= pd.Series(l).values.round(3), name=f'OPIGR({OPIGR})', line=dict(color='darkcyan')))
if origr:
    # 経常利益増加率 (当期経常利益-前期経常利益)÷前期経常利益
    l = [(PL[ORI][i] - PL[ORI][i-1]) / PL[ORI][i-1] for i in range(1,5)]
    fig6.add_trace(go.Scatter(x=rakus_df.T.index[1:], y= pd.Series(l).values.round(3), name=f'ORIGR({ORIGR})', line=dict(color='darkviolet')))
if lnagr:
    # 総資本増加率 (当期総資本ー前期総資本)÷前期総資本
    l = [(BS[LNA][i] - BS[LNA][i-1]) / BS[LNA][i-1] for i in range(1,5)]
    fig6.add_trace(go.Scatter(x=rakus_df.T.index[1:], y= pd.Series(l).values.round(3), name=f'LNAGR({LNAGR})', line=dict(color='orangered')))
if nagr:
    # 純資本増加率 (当期末純資本残高ー前期末純資本残高)÷前期末純資本残高
    l = [(BS[NA][i] - BS[NA][i-1]) / BS[NA][i-1] for i in range(1,5)]
    fig6.add_trace(go.Scatter(x=rakus_df.T.index[1:], y= pd.Series(l).values.round(3), name=f'NAGR({NAGR})', line=dict(color='dodgerblue')))
#fig6.update_traces(hovertemplate='(%{x}, %{y})')
fig6.update_layout(
                   yaxis=dict(tickformat='p'),
                   plot_bgcolor='floralwhite',
                   legend=dict(xanchor='center', yanchor='top', x=0.5, y=1.2, orientation='h')
                  )
st.plotly_chart(fig6)

以上のコードを記載したのち、ターミナルで仮想環境に入り「streamlit run top.py」と実行するとアプリが起動する。
アプリ画面は以下のような形になるはず。

参考

Pythonを入れて仮想環境を作成する方法ですが、私は以下のサイトを参考にしました。
もしAnaconda以外を使いたい場合は、こちらをご覧ください。
https://prog-8.com/docs/python-env-win
https://obenkyolab.com/?p=2582
https://it-engineer-info.com/language/python/5545/

Discussion