K_DM Book
用語集
対象者 標準
対象者別の入口 →

対象者の設定はこのブラウザの中にだけ保存されます。

言語 JA

企業の財務情報をパース

05.1.9

企業の財務情報をパース

最終更新 2020-05-20 読了時間 9 分
まとめ
財務データの比較で手間が掛かるのは計算ではなく、サイトから保存した CSV を DataFrame に整える前処理です。このページでは Morningstar から保存した損益計算書(Income Statement)と主要指標(Key Ratios)の CSV を、en_US_ja_JP_table の対訳辞書で科目名を日本語に置き換えつつ、桁区切りのカンマを正規表現で除き、年月をヘッダーとして転置する関数 get_preprocessed_df() で読み込みます。海運業の Golden Ocean(GOGL)、ZIM、Star Bulk(SBLK)の 3 社を pd.concat で縦に積み、ticker 列を hue にした seaborn.barplot で期ごとの売上と当期純利益を横並びにします。読み終えると、別の企業の CSV を同じフォルダに置くだけで同じ比較図が作れるようになります。

直感 #

Morningstar の財務ページから保存した CSV は、1 行目にタイトル、途中に「Revenue」のような科目名だけの行、数値は "1,234" のように引用符とカンマ付き、列の見出しは 2017-12 のような年月に TTM(直近 12 か月)が混ざる、という人が読むための形になっています。pd.read_csv にそのまま渡すと列数が行ごとに違ってエラーになるので、1 行ずつ読んで「科目名の行か、数値の行か、年月の見出し行か」を見分けながら組み立て直します。整えた後は「行 = 期(月)、列 = 科目、ticker 列 = 会社」という縦長の表になり、seaborn に渡せば 3 社の棒が期ごとに並びます。

近い業界にある海運 3 社の、期ごとの売上と当期純利益の推移を確認します。

このページの日英訳は以下のサイトのものを参考しつつ作成していますが、正確性に欠ける可能性があるのであくまで参考程度にお願い致します。 TOMAコンサルタンツグループ株式会社 海外決算書の科目 英語→日本語簡易対訳 損益計算書編

1
2
3
4
5
6
7
import matplotlib.pyplot as plt
import numpy as np
import os
import pandas as pd
import re
import seaborn as sns
from IPython.core.display import display
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# 英日辞書
en_US_ja_JP_table = {
    "Revenue": "売上",
    "Cost of revenue": "収益コスト",
    "Gross profit": "粗利益",
    "Sales, General and administrative": "販売費及び一般管理費",
    "Other operating expenses": "その他営業費用",
    "Total operating expenses": "営業費用",
    "Operating income": "営業利益",
    "Interest Expense": "支払利息",
    "Other income (expense)": "Other income (expense)",
    "Income before taxes": "Income before taxes",
    "Provision for income taxes": "Provision for income taxes",
    "Net income from continuing operations": "Net income from continuing operations",
    "Net income available to common shareholders": "普通株式に係る当期純利益",
    "Net income": "当期純利益",
    "Basic": "Basic",
    "Diluted": "Diluted",
    "EBITDA": "EBITDA",
    "Revenue ": "売上",
    "Gross Margin %": "	売上総利益率",
    "Operating Income ": "営業利益",
    "Operating Margin %": "営業利益率",
    "Net Income ": "純利益",
    "Earnings Per Share USD": "EPS (USD)",
    "Dividends USD": "配当 (USD)",
    "Payout Ratio % *": "配当性向",
    "Shares Mil": "株数 (Mil)",
    "Book Value Per Share * USD": "1株あたり純資産",
    "Operating Cash Flow ": "営業キャッシュフロー",
    "Cap Spending ": "資本的支出",
    "Free Cash Flow ": "フリーキャッシュフロー(FCF)",
    "Free Cash Flow Per Share * USD": "1株あたりFCF",
    "Working Capital ": "運転資本",
    "Key Ratios -> Profitability": "",
    "Margins % of Sales": "Margins % of Sales",
    "COGS": "売上原価",
    "Gross Margin": "売上高総利益率",
    "SG&A": "販売費及び一般管理費",
    "R&D": "研究開発費",
    "Operating Margin": "営業利益率",
    "Net Int Inc & Other": "資金運用利益+その他",
    "EBT Margin": "EBTマージン",
    "Debt/Equity": "負債比率",
    "Receivables Turnover": "売上債権回転率",
    "Inventory Turnover": "棚卸資産回転率",
    "Fixed Assets Turnover": "固定資産回転率",
    "Asset Turnover": "総資産回転率",
    "USD Mil": "(USD Mil)",
    "-Year Average": "年平均",
}

def get_preprocessed_df(
    filepath,
    is_transpose=True,
    drop_ttm_lastqrt=True,
    ttm_datetime="2021-12",
    lastqtr_datetime="2021-12",
    add_ticker_column=True,
):
    """Morningstar, Incのデータを整形する

    Args:
        filepath (str): ファイルパス
        is_transpose (bool, optional): タイムスタンプ列を縦にするかどうか. Defaults to True.
        drop_ttm_lastqrt (bool, optional): TTM/Last Qrtの記録は削除する. Defaults to True.
        ttm_datetime (str, optional): 「TTM」をどの日付に置き換えるか. Defaults to "2021-12".
        lastqtr_datetime (str, optional): 「Latest Qtr」をどの日付に置き換えるか. Defaults to "2021-12".
        add_ticker_column (bool, optional): ticker symbolを示した列を追加するか. Defaults to True.

    Returns:
        DataFrame: 整形済みデータフレーム
    """
    df_preprocessed = []
    df_header = []
    row_header = ""
    df = pd.read_table(filepath, header=None)

    if not drop_ttm_lastqrt:
        print(f"[Note] TTM は {ttm_datetime} 、Last Qtrは {lastqtr_datetime} の日付として扱われます。")

    for idx, row in enumerate(df[0]):
        # 数値中の「,」を置換する
        row = re.sub('"(-?[0-9]+),', '"\\1', row)
        row = re.sub(',(-?[0-9]+)",', '\\1",', row)

        # 英語を対応する日本語に置き換える
        for str_en, str_jp in en_US_ja_JP_table.items():
            if str_en in row:
                row = row.replace(str_en, str_jp)

        # TTMがある行はタイムスタンプなのでヘッダー扱いにする
        if "TTM" in row or "Latest Qtr" in row:
            if drop_ttm_lastqrt:
                row = row.replace("TTM", "###IGNORE###")
                row = row.replace("Latest Qtr", "###IGNORE###")
            else:
                assert ttm_datetime not in row, "その日付はすでに存在しています!"
                assert lastqtr_datetime not in row, "その日付はすでに存在しています!"
                row = row.replace("TTM", ttm_datetime)
                row = row.replace("Latest Qtr", lastqtr_datetime)
            df_header = row.split(",")

            if is_transpose:
                df_header[0] = "月"
            else:
                df_header = ["月"] + df_header
            continue

        # 数値に変換できるデータは数値に変換してDataFrameに追加
        if len(row_splited := row.split(",")) > 1:
            row_data = [
                float(v) if re.match(r"^-?\d+(?:\.\d+)$", v) is not None else v
                for v in row_splited
            ]

            if is_transpose:
                row_data[0] = (
                    f"{row_header}/{row_data[0]}" if row_header else f"{row_data[0]}"
                )
            else:
                row_data = [row_header] + row_data
            df_preprocessed.append(row_data)
        else:
            # 先頭の行はファイルのタイトルが入っているので無視
            row_header = f"{row}" if idx > 0 else ""

    # データフレーム作成
    df_preprocessed = pd.DataFrame(df_preprocessed)
    df_preprocessed.columns = df_header
    if drop_ttm_lastqrt:
        df_preprocessed.drop("###IGNORE###", axis=1, inplace=True)

    # 不要な文字列を削除
    df_preprocessed.fillna(np.nan, inplace=True)

    if is_transpose:
        df_preprocessed = df_preprocessed.T.reset_index()
        df_preprocessed.columns = df_preprocessed.iloc[0, :]
        df_preprocessed.drop(0, inplace=True)
        df_preprocessed["月"] = pd.to_datetime(df_preprocessed["月"])
        for colname in df_preprocessed.columns:
            if colname != "月":
                df_preprocessed[colname] = pd.to_numeric(
                    df_preprocessed[colname], errors="coerce"
                )

    if add_ticker_column:
        filename = os.path.basename(filepath)
        ticker_symbol = filename[: filename.index(" ")]
        df_preprocessed["ticker"] = [
            ticker_symbol for _ in range(df_preprocessed.shape[0])
        ]

    return df_preprocessed

データを読み込む #

以下の例では Morningstar, Inc 社から提供されているデータを一部引用して使用しています。各企業のページから損益計算書(Income Statement)と Key Ratios を CSV で保存し、data/ フォルダに GOGL Income Statement.csv のようにティッカーで始まる名前で置いておきます。ここでは Golden Ocean Group Ltd(GOGL)、ZIM、Star Bulk(SBLK)の 3 社です。

※あくまで表示例であり正確性は保証しません。万一この情報に基づいて被ったいかなる損害についても一切責任を負い兼ねます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
df_is_gogl = get_preprocessed_df("data/GOGL Income Statement.csv")
df_kr_gogl = get_preprocessed_df("data/GOGL Key Ratios.csv")
df_is_zim = get_preprocessed_df("data/ZIM Income Statement.csv")
df_kr_zim = get_preprocessed_df("data/ZIM Key Ratios.csv")
df_is_sblk = get_preprocessed_df("data/SBLK Income Statement.csv")
df_kr_sblk = get_preprocessed_df("data/SBLK Key Ratios.csv")

df_income_statement = pd.concat([df_is_gogl, df_is_sblk, df_is_zim])
df_key_ratio = pd.concat([df_kr_gogl, df_kr_zim, df_kr_sblk])

display(df_income_statement.head())
display(df_key_ratio.head())

月売上収益コスト粗利益Operating expenses/販売費及び一般管理費Operating expenses/その他営業費用Operating expenses/営業費用Operating expenses/営業利益Operating expenses/支払利息Operating expenses/Other income (expense)...Operating expenses/当期純利益 from continuing operationsOperating expenses/当期純利益Operating expenses/普通株式に係る当期純利益Earnings per share/BasicEarnings per share/DilutedWeighted average shares outstanding/BasicWeighted average shares outstanding/DilutedWeighted average shares outstanding/EBITDAtickerOperating expenses/Other
12016-12-01258313-5513215-7042-16...-128-128-128-1.34-1.349595-22GOGLNaN
22017-12-014604006013-4951573...-2-2-2-0.02-0.02125125133GOGLNaN
32018-12-0165649915815-3121467312...8585850.590.59144144250GOGLNaN
42019-12-017065901161411510157-6...3737370.260.26144144188GOGLNaN
52020-12-016085644414-3113345-126...-138-138-138-0.96-0.9614314318GOGLNaN

5 rows × 22 columns

月Financials/売上 (USD Mil)Financials/\t売上総利益率Financials/営業利益(USD Mil)Financials/営業利益率Financials/純利益(USD Mil)Financials/EPS (USD)Financials/配当 (USD)Financials/配当性向Financials/株数 (Mil)...Key Ratios -> Financial Health/負債比率Key Ratios -> Efficiency Ratios/Days Sales OutstandingKey Ratios -> Efficiency Ratios/Days InventoryKey Ratios -> Efficiency Ratios/Payables PeriodKey Ratios -> Efficiency Ratios/Cash Conversion CycleKey Ratios -> Efficiency Ratios/売上債権回転率Key Ratios -> Efficiency Ratios/棚卸資産回転率Key Ratios -> Efficiency Ratios/固定資産回転率Key Ratios -> Efficiency Ratios/総資産回転率ticker
12011-12-0195.068.238.040.433.06.129.00NaN5.0...0.4218.7932.1236.0114.8919.4311.360.210.18GOGL
22012-12-0137.038.210.026.8-53.0-10.085.40488.75.0...0.3948.0439.3726.3961.027.609.270.110.08GOGL
32013-12-0138.031.37.018.2-4.0-0.693.15NaN6.0...0.3026.2520.6019.1627.6813.9117.720.130.09GOGL
42014-12-0197.025.419.020.116.01.382.81184.211.0...0.3911.4537.8516.1033.2031.889.640.130.12GOGL
52015-12-01190.0-28.6-72.0-37.6-221.0-7.30NaNNaN30.0...0.8011.9021.195.5727.5130.6817.220.130.11GOGL

5 rows × 87 columns

変化を可視化する #

縦に積んだ 3 社の表を seaborn.barplot に渡し、期ごとに 3 本の棒が並ぶ図を作る関数を用意します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
def show_barchart(data, x="月", y="売上", hue="ticker", is_log_scale=False):
    """バーチャートを表示する

    Args:
        data (pandas.DataFrame): データフレーム
        x (str, optional): 時間軸. Defaults to "月".
        y (str, optional): 比較する指標. Defaults to "売上".
        hue (str, optional): 何基準で比較するか. Defaults to "ticker".
        is_log_scale (bool, optional): logスケールで表示するかどうか. Defaults to False.
    """
    sns.set_theme(style="whitegrid", rc={"figure.figsize": (10, 4)})

    g = sns.barplot(data=data, x=x, y=y, hue=hue)
    g.set_xticklabels(
        [xt.get_text().split("-01")[0] for xt in g.get_xticklabels()]
    )  # TODO: mdates.DateFormatterで日付を表示するとなぜか日付がずれるのでラベルを直接書き換える
    g.tick_params(axis="x", rotation=90)
    if is_log_scale:
        g.set_yscale("log")
    plt.legend(loc="upper left", title="Ticker Name")
    plt.title(f"{y}の比較", fontsize=14)
    plt.show()
1
2
show_barchart(df_income_statement, x="月", y="売上", hue="ticker")
show_barchart(df_income_statement, x="月", y="Operating expenses/当期純利益", hue="ticker")

変化を可視化するの図

変化を可視化するの図

実行結果の読み方 #

1 枚目の売上では、2021 年の期に 3 社の棒がそろって前年の 2〜3 倍に伸びています。2 枚目の当期純利益では、2016〜2018 年に赤字(負の棒)だった期がある会社と、黒字を保った会社の違いが見え、売上の順位と利益の順位が一致しない期もあります。横軸は各社の決算期をまとめた月なので、決算月が違う会社を比べるときは同じ月の棒が「同じ時期」ではない点に注意してください。

使いどころと落とし穴 #

get_preprocessed_df は Morningstar の CSV の書式(1 行目がタイトル、TTM を含む見出し行、科目グループ名だけの行)に依存しているので、書式が変わると df_header が空のまま columns = df_header で失敗します。対訳辞書は「含まれていれば置換」なので、"Revenue" と "Revenue " のように似たキーが複数あると置換が二重に掛かることがあり、列名が想定と違うときは辞書の順番を疑います。TTM を捨てる既定(drop_ttm_lastqrt=True)は期を揃えて比較するためで、直近 12 か月も見たいときは ttm_datetime を決算月に合わせて False にします。

まとめ #

Morningstar の CSV は人が読む形なので、get_preprocessed_df() で 1 行ずつ「見出し・科目グループ・数値」を見分け、カンマ除去と対訳辞書の置換を経て「行 = 月、列 = 科目、ticker 列 = 会社」の表に組み直しました。3 社を pd.concat で積めば show_barchart(df, y="売上", hue="ticker") の 1 行で期ごとの比較図になり、y を "Operating expenses/当期純利益" に替えれば利益の比較に切り替わります。前処理関数を 1 つ用意しておくことで、CSV を追加するだけで比較対象の会社を増やせるのがこの構成の利点です。