この連載の読む順番
TDnet①〜④で、書類の役割からXBRLの解析・グラフ化までを順に扱います。その後、EDINET①〜④へ進みます。共通の基本操作はTDnet編を参照し、EDINET編では違いを確認します。
- TDnet解析① TDnetとは?決算短信・適時開示とEDINETの違い
- TDnet解析② XBRLとは?決算短信のPDFと数値データを図解
- TDnet解析③ XBRLの値を比較する前に、期間・単位・欠損を確かめる(この記事)
- TDnet解析④ 累計と四半期単独、実績と予想を分けてグラフにする
- EDINET解析① EDINETとは?有価証券報告書から企業を理解する
- EDINET解析② XBRLとは?有価証券報告書の構造とタグの仕組みを理解する
- EDINET解析③ TDnetの次に読むXBRL――ファイル・タグ・コンテキストの違いを確かめる
- EDINET解析④ 財務データを継続的な分析に使う
XBRLの入門で、まず一つの数値を取得したくなります。しかし、数字が取得できても、それだけでは比較やグラフに使えるとは限りません。
この記事では、「同じ売上高に複数の値があるとき、何を確認するか」を整理し、TDnetの公式サンプルをPythonで読むところまで進めます。タグや書類構造の説明はTDnet解析②を参照し、この回では実際の選択と検算に集中します。
100と110を比べる前に
この回の目標は、公式サンプルから一つの売上高を読み、開始日・終了日・単位・数値を自分で確かめることです。前半はコードなしで条件の選び方を整理し、後半で短いPythonを動かします。
Pythonを初めて使う場合は、実行準備と出力の見方も順に読んでください。先にTDnet解析②のタグの読み方を確認しておくと、プログラムの各処理が「手で探していたどの操作か」に対応します。
次の二つは架空の例です。
| 項目 | 値 | 単位 | 対象期間 | 範囲 | 区分 |
|---|---|---|---|---|---|
| 売上高 | 100 | 億円 | 当年度の1年間 | 連結 | 実績 |
| 売上高 | 110 | 億円 | 次年度の1年間 | 連結 | 予想 |
110の方が大きいからといって、「売上が10%増えた」とは言えません。次年度の予想を、達成済みの実績と混ぜているためです。
この違いはグラフにも影響します。実績と予想を描くなら凡例や線種で区別し、比較の対象をタイトルに書きます。
同じ売上高が四つ見つかったら、何を選ぶか
架空の青葉製作所の書類から、次の候補を見つけたとします。この表は解析の考え方を説明するために独自に整理したもので、実際のXBRLやAPIの応答形式ではありません。
| 候補 | 対象期間 | 範囲 | 区分 | 単位 | 値 |
|---|---|---|---|---|---|
| A | 前年度の1年間 | 連結 | 実績 | 百万円 | 9,000 |
| B | 当年度の1年間 | 連結 | 実績 | 百万円 | 10,000 |
| C | 当年度の1年間 | 個別 | 実績 | 百万円 | 7,000 |
| D | 次年度の1年間 | 連結 | 予想 | 百万円 | 11,000 |
欲しい値を「青葉製作所の当年度の連結売上高の実績」と言葉にすると、選ぶのはBです。数値の大きさでも、ファイルに出てきた順番でもなく、条件を使って選びます。
前年度と比べるならAとBを使います。この架空例で、期間の長さや会計上の扱いなどほかの比較条件も揃っているとすれば、売上高は90億円から100億円へ増え、増加率は約11.1%です。BとDなら、100億円の実績に対して次年度の会社予想が110億円、という別の比較になります。
CはBと同じ年度ですが、個別の数値です。「年度が一致した」という確認だけでは不十分なことが分かります。条件は一つずつ加え、何を基準に採用したかを説明できるようにします。
図:本文の4候補を条件で選びます。目的に合うのはBで、金額の大小や出現順は選択理由ではありません。
候補が残りすぎたときの扱いを決める
条件に合う候補が二つ残った場合、先頭の一件を取って処理を成功扱いにすると、見た目だけは完成してしまいます。学習用の例では、一件に決まらなければ確認が必要と扱う方が、問題を発見しやすくなります。
反対に候補がゼロ件でも、「売上高ゼロ」という意味ではありません。対象書類が違うのか、必要な条件を取り違えたのか、目的の項目が存在しないのかを調べます。件数の確認は、値を読む処理と分けて考えましょう。
最低限、値と一緒に持ちたい情報
学習用なら、まず次の形で値を整理すると理解しやすくなります。
{
"metric": "売上高",
"value": 10000000000,
"unit": "JPY",
"periodStart": "2025-04-01",
"periodEnd": "2026-03-31",
"scope": "連結",
"kind": "実績"
}
これは説明用の独自形式です。XBRLの構文でも、ZENITH CodeのAPI応答でもありません。例の狙いは、数値だけを保持しないことです。
XBRLでは参照先も読む
XBRLの数値には、対象や期間を示すコンテキスト、単位への参照などが結び付きます。タグ名に売上高らしい文字列があった、という理由だけで先頭の値を採用しません。
当年と前年、連結と個別など、必要な条件に合うかを確認します。コンテキストIDの文字列だけを決め打ちせず、その内容を見ます。TDnetの実際のタグと仕様書の確認箇所は、TDnet解析②に掲載しています。
この後のコードで読む参照先を、先に整理しておきます。
| 数値からたどるもの | 確認する中身 | このTDnetサンプルでの確認結果 |
|---|---|---|
contextRefと同じidのcontext
|
entity/identifier、period/startDate・endDate、scenario
|
主体10270、2023-04-01〜2024-03-31、連結・実績の設定 |
unitRefと同じidのunit
|
measureとその名前空間 |
iso4217:JPY(円) |
| 数値要素自身の属性 |
format、scale、sign、decimals、nil
|
表示文字1、scale=6から100万円を得る |
contextRefやunitRefは、参照先の名前です。期間や単位の意味そのものではありません。IDで場所を見つけ、その中身を確認する、という二段階で読みます。
また、XBRLが付いていれば文書全体を同じ粒度で取得できる、とは限りません。TDnetの対象範囲はJPXの仕様一覧で確認します。
数値を変換した後も、元の意味へ戻れるようにする
表の表示値を画面用に整えるときは、単位を一度だけ換算するようにします。上のBは10,000百万円なので、円単位では10,000,000,000円、億円単位では100億円です。
| 段階 | 値 | 単位 |
|---|---|---|
| 説明用の元表 | 10,000 | 百万円 |
| 円単位に統一した値 | 10,000,000,000 | 円 |
| グラフの表示値 | 100 | 億円 |
円単位に変換した値へ、さらに百万円から円への換算を掛けると、値が100万倍になってしまいます。元の値、元の単位、表示用の値を区別すると、この間違いを追いやすくなります。
ただし、実際のXBRLの数値は、画面に見えている表記と同じ形で保持されているとは限りません。ここでの計算は説明用の表に対する例です。ファイルを読む際には形式の仕様を確認し、紙面の「百万円」という見出しを見ただけで取得済みの数値を一律に100万倍しないようにします。
図:元の値・円に揃えた値・表示用の値を分けます。実際のXBRLから取得した値を、紙面の見出しだけで一律に100万倍する図ではありません。
年度のラベルだけで時間を判断しない
「2026年3月期」と表示するグラフでも、元データには対象期間の開始日と終了日を残します。2025年4月から2026年3月までの売上高と、2025年4月から9月までの売上高を、同じ年度のラベルだけで混ぜないためです。
同じ年度に見える数値でも、期間合計か、期末時点の値かという違いがあります。売上高の期間比較と、期末の資産残高の比較では、条件の見方を分けます。グラフへ渡す直前に年度の文字列を作り、意味を確かめるための情報は残すと整理しやすくなります。
ゼロと、見つからない値を別に表示する
架空の三年間の値が90億円、欠損、100億円だったとします。欠損をゼロにすれば、グラフには一度売上がなくなり、その後回復したような形が出てしまいます。元の情報は、そこまでの意味を持っていません。
この場合は、未確認の年を空白にして注記するなど、値がないことを読者へ伝えます。点を線で結ぶ場合も、間の年を確認できていないことが分かる表示にします。データの欠け方を隠すより、確認済みの範囲を伝える方が正確です。
図:上下は同じ縦軸です。前期の値を確認できない場合、ゼロへ置き換えず、欠損として伝えます。
取得後の三つの確認
- 比較する値の期間の長さと範囲は揃っているか。
- 単位を揃え、実績と予想を区別しているか。
- 見つからなかった値をゼロで埋めていないか。
まずは、これらを人が確認できる一つの例から始めます。汎用の解析器を最初から作ろうとするより、どこまで正しく読めたかを説明しやすくなります。
実際の解析処理に沿って、一件の結果を説明できるようにする
ここまでの確認を、実装の工程へ対応させます。今回確認した解析コードでは、リンクベースの読取り、コンテキストとユニットの収集、ファクトの抽出、表示構造への組立てが分かれていました。次の表は、その責務を一般化した説明です。
| 工程 | 入力のどこを読むか | 後で確認する結果 |
|---|---|---|
| 1 書類を識別 | TDnetの開示番号など、EDINETのdocIDなど | 出典書類と取得日時 |
| 2 ファイルを分類 | インスタンス、schemaRef、スキーマ、リンクベース | どの定義を参照したか |
| 3 参照先を集める | context、unit、名称・表示・定義リンク | IDと実体の対応 |
| 4 数値を読む | name、contextRef、unitRef、format、scale、sign、nil | 元の文字と解釈した値 |
| 5 条件で選ぶ | 期間、対象範囲、実績・予想、軸 | 採用理由と残った候補 |
| 6 表示する | 表示ロール、order、ラベル、表示単位 | 検証可能な表・グラフ |
参照先を先に集めるのは、値を読んだ時点で、そのコンテキストの定義がまだ見つかっていない可能性があるためです。複数ファイルから成る文書では、ファイルを一枚ずつ読み終えて値を確定するより、文書の構成を先に把握する方が説明しやすくなります。
公式サンプルで一件だけ検算する
TDnet解析②で読んだ公式サンプルについて、元の値から得られる結果を整理します。これは当社APIのレスポンスではなく、検算用の独自形式です。
{
"concept": "tse-ed-t:NetSales",
"contextId": "CurrentYearDuration_ConsolidatedMember_ResultMember",
"entity": "10270",
"startDate": "2023-04-01",
"endDate": "2024-03-31",
"scope": "ConsolidatedMember",
"kind": "ResultMember",
"rawText": "1",
"scale": 6,
"value": 1000000,
"unit": "JPY",
"decimals": -6
}
JPX公式サンプルの配布ページ。この数値はサンプル内の値で、実在企業の売上高ではありません。画面で「1百万円」と示すなら、保存した円単位の100万を表示時に100万で割ります。取得時の変換と表示時の変換を別の関数や工程に分けると、二重変換を追いやすくなります。
このInline XBRLの例では、表示文字1に10 ** 6を掛けて円単位の1000000へ戻します。scale="6"はこの桁の調整に使います。一方、decimals="-6"は百万の位までの精度に関する情報で、もう一度100万倍する指示ではありません。変換済みの.xbrlを読む場合との違いは、TDnetの実習を終えた後のEDINET編で扱います。
次のコードは、その特定のサンプルの一件だけを検算するものです。汎用のXBRLパーサーではありません。ZIP名をtdnet-official-sample.zipとして置き、lxmlが使えるPython環境で実行します。会社別の独自処理や名寄せの規則は含めていません。
実行前に用意するもの
ここでは、Python 3が利用でき、ターミナルでコマンドを実行できる環境を前提にします。ターミナルは、文字で実行する処理を指定するアプリです。以下の例はpython3コマンドを使います。環境でpythonやpyという名前を使う場合は、Python 3を指すコマンドに読み替えてください。
- 練習用のフォルダーを一つ作り、取得した公式ZIPを
tdnet-official-sample.zipという名前で保存します。このコードではZIPを直接読むので、展開済みフォルダーだけでは動きません。 - 下のPythonコードを、同じフォルダーの
read_sample.pyへ保存します。Markdownの区切りであるバッククォート3文字は含めず、コード欄の中身だけを保存します。 - ターミナルで、そのフォルダーを作業場所として開きます。相対パスで指定したZIPは、この作業場所から探されます。
- 必要なライブラリを、その練習用環境へ入れてから実行します。
ほかのプロジェクトとライブラリを分けたい場合は、次のように仮想環境を作れます。仮想環境は、この練習で使うPythonライブラリの置き場所を分ける仕組みです。
python3 -m venv .venv
macOS・Linuxでは、そのフォルダーで次を実行します。
.venv/bin/python -m pip install lxml
.venv/bin/python read_sample.py
WindowsのPowerShellでは、対応する実行ファイルを指定します。
.\.venv\Scripts\python.exe -m pip install lxml
.\.venv\Scripts\python.exe read_sample.py
lxmlはXMLを構造として読むライブラリです。インストールにはネットワークを使いますが、以下の解析コード自体は手元のZIPを読みます。公開APIのキーは使いません。仮想環境についてはPython公式ドキュメント、XMLの読み方はlxmlの公式チュートリアルも参照できます。
実行するコード
from decimal import Decimal
from zipfile import ZipFile
from lxml import etree
filename = "tse-acedjpsm-10270-20240718553471-ixbrl.htm"
with ZipFile("tdnet-official-sample.zip") as z:
member = next(n for n in z.namelist() if n.endswith(filename))
root = etree.fromstring(z.read(member),
parser=etree.XMLParser(resolve_entities=False, no_network=True))
ns = {"ix": root.nsmap["ix"],
"x": "http://www.xbrl.org/2003/instance"}
context_id = "CurrentYearDuration_ConsolidatedMember_ResultMember"
facts = root.xpath("//ix:nonFraction[@name='tse-ed-t:NetSales']"
"[@contextRef=$c]", namespaces=ns, c=context_id)
assert len(facts) == 1
fact = facts[0]
context = root.xpath("//x:context[@id=$c]", namespaces=ns, c=context_id)[0]
unit = root.xpath("//x:unit[@id=$u]", namespaces=ns, u=fact.get("unitRef"))[0]
assert fact.get("format") == "ixt:numdotdecimal"
assert fact.get("sign") is None
assert fact.get("{http://www.w3.org/2001/XMLSchema-instance}nil") is None
value = Decimal(fact.text.replace(",", "")) * Decimal(10) ** int(fact.get("scale"))
print(context.findtext("x:period/x:startDate", namespaces=ns))
print(context.findtext("x:period/x:endDate", namespaces=ns))
print(unit.findtext("x:measure", namespaces=ns), value)
コードは、何をどの順番でしている?
| コードの部分 | 手で行う操作に置き換えると |
|---|---|
ZipFile(...)、namelist()
|
ZIPを開き、中のファイル名一覧を見る |
endswith(filename) |
目的のファイル名で終わるものを探す |
etree.fromstring(...) |
ファイルの文字を、要素の親子関係として読む |
root.xpath(...)でfactsを作る |
売上高と対象コンテキストが一致する候補を集める |
assert len(facts) == 1 |
候補がちょうど一件かを確認する |
contextとunitを取得する |
参照先のIDを探して中身を読む |
Decimal(...)とscaleの計算 |
表示文字を数値にし、必要な桁に戻す |
最後の三つのprint(...)
|
調べた結果をターミナルへ表示する |
XPathは、XMLの中から条件に合う場所を探すための書き方です。この例の//ix:nonFractionは該当する要素を探し、[@name='...']は属性の条件で絞っています。$cには、コードで指定したコンテキストIDを渡しています。
nsは検索に使う接頭辞と名前空間URIの対応表です。コードではxをXBRLインスタンスの名前空間に割り当てているので、ファイル側でxbrli:contextと書かれていてもx:contextで検索できます。接頭辞の文字が同じだから見つかるのではなく、対応する名前空間が一致するから見つかります。
facts[0]は候補の先頭を取り出す書き方ですが、その前に一件だけであることを確認しています。複数あるのに先頭を無条件に採る例ではありません。assertは、指定した条件が成り立たなければ処理を止める確認です。この学習例では、前提が違う入力を黙って処理しないために置いています。
出力の三行を、元ファイルと照合する
2023-04-01
2024-03-31
iso4217:JPY 1000000
一行目は開始日、二行目は終了日、三行目は単位と数値です。前回読んだコンテキストのstartDate・endDate、ユニットのmeasure、表示文字1を100万倍した結果と、それぞれ比べます。
表示された結果を日本語で言い換えると、「このサンプルで指定したコンテキストの売上高は、2023年4月1日から2024年3月31日までの期間に対応する100万円」です。連結・実績の区分は前回コンテキストの中身を確認しました。この短いコードの出力には区分を表示していないので、出力の三行だけで全条件を確認できたとは扱いません。
動かなかったときに確認する場所
| 表示されることがあるエラー | この例で最初に確かめること |
|---|---|
FileNotFoundError |
ZIP名と、ターミナルの作業場所が合っているか |
ModuleNotFoundError: No module named 'lxml' |
実行に使うPythonと、インストールに使ったPythonが同じか |
StopIteration |
ZIP内に、指定した名前のファイルがあるか。別のサンプルや配布版ではないか |
AssertionError |
候補数やformatなど、どの前提で止まったか。確認行を削除して進めない |
IndexError |
指定IDのcontextやunitが見つかるか。今回のサンプルと同じ構造か |
これは原因を一つに断定する表ではなく、最初の確認順です。サンプルが変わった場合は、ファイル名だけを置き換えて成功扱いにせず、タグ・期間・単位からもう一度確認します。
確認した出力は、開始日2023-04-01、終了日2024-03-31、単位iso4217:JPY、値1000000です。この例では既知のコンテキストIDを指定しました。対象を他の会社・年度へ広げる段階で、期間と軸の中身に基づく選択へ変えます。formatごとの変換、分子・分母を持つ単位、注記の連結、重複ファクトなどは、この短いコードの対象外です。
最初に確認したい、実装上の落とし穴
- IDを全書類共通の鍵にする:別の書類に同じコンテキストIDがあっても、期間や主体は異なります。
- 軸を捨てる:同じ期間でも連結・個別、実績・予想、セグメントなどを混ぜる原因になります。
- 同じタグの先頭だけ採る:当期・前期や複数箇所の記載があり、文書中の順序だけでは選べません。
- 変換済みXBRLにscaleを再適用する:Inline XBRLの表示値と、変換後の数値を区別します。
- 最初のmeasureしか保存しない:円/株の分母などが失われます。
- 未知のタグを自動で既存項目へ統合する:ラベルの追加と、意味が同じという判断は別です。
- 訂正情報をタイトルだけで処理する:書類のID、親子関係、状態、対象期間も必要です。
同じタグ・コンテキスト・単位の候補が重複しても、直ちに異常とは限りません。同じ内容が複数箇所に現れることがあります。値が一致する重複と、値や精度が食い違う候補を分けて記録し、無条件に後勝ちで上書きしないようにします。
小さな例で確かめたい四つのケース
一つの正しい例が動いた後は、条件を少しずつ変えてみます。外部から大量に取得しなくても、架空データだけで選択や表示の誤りを考えられます。
| 変える条件 | 期待する扱い |
|---|---|
| 候補の並び順を入れ替える | 条件が同じなら採用する値は変わらない |
| 前年度の候補だけにする | 当年度の値として採用しない |
| 個別の候補を加える | 連結の値と混ぜない |
| 必要な値をなくす | ゼロではなく、該当なしとして扱う |
これは全てのXBRLに対応するための網羅的な試験ではありません。まず、数値を選ぶ自分の条件が結果へ反映されているかを確認する練習です。
調べたい項目が増えたときも、値だけを見て判断するのではなく、対象・期間・単位・範囲と一緒に確認します。この習慣ができると、XBRLを直接解析する場合も、構造化されたデータをAPIで受け取る場合も、比較の前提を整理しやすくなります。
データから画面を作るときにも同じ確認をする
APIで構造化された値を取得する場合も、期間・単位・欠損の確認は必要です。画面のグラフには、年度、単位、実績/予想を示します。見た目が整っていても、比較条件が違えば誤解を生むためです。
続けて読む
私たち株式会社Crabgrassは、財務データAPI「ZENITH Code」を開発・提供しています。
本記事は、明記した公式サンプルと架空例を使用しています。実在企業の業績や投資判断を示すものではありません。
関連するサービス内ガイド
実際のデータ利用へ進むときは、目的に合わせて次のガイドも参照できます。
- TDnet解析ガイド:決算短信の対象書類、実績と予想、期間・単位、訂正の確認順を整理しています。
- EDINET解析ガイド:書類・タグ・表示階層の関係を確認できます。
- 財務データAPIの選び方:主要項目・財務諸表の階層・XBRLファクトから、必要なデータの形を選べます。


