PDFから崩れて抜けた統計表を構造化データに直し、表の上に推移チャートを載せた
PDFから崩れて抜けた統計表を構造化データに直し、表の上に推移チャートを載せた
税務の公的研修教材(講本)を講義ノート化する作業を、役割ごとにセッションを分けて回している。この日は、別のセッションで用意した指示を渡して「壊れた統計表を構造化データに直す」係を1本走らせた。着手時点で崩れた表は497件。表の直し方さえ決めれば片が付くつもりでいた。
終わってみると、時間の大半は表ではなく図の残骸に使っていた。
表の棚卸しと、原本ページ番号の照合
最初にやらせたのは棚卸しだった。税収の推移を載せた表が原本PDFの15ページ目にあり、データ側が持っていた sourcePage の値と一致した。ページ番号の付け方の規約がここで確定し、以降は原本との突き合わせを機械的にできるようになった。
恒等式による検算
構造化した表には、合計が内訳の和に一致するという恒等式を持たせている。ただ、検算は「書いてあるのに動いていない」ことがある。値を1つわざと壊して、テストがきちんと落ちるところまで確認させた。
国民負担率の表だけは形が違った。行が年度で、恒等式が列方向に走る(③=①+②、⑤=③+④)。チェックが行方向しか見ていないので、そのままでは仕組みに乗らないと相談が上がってきた。転置して構造化する方針を出し、行見出しを左に固定して表だけが横スクロールする形に収めた。
数値表とテキスト主体の表の分岐
表のCSSは white-space: nowrap と数値の右寄せで、数値表だけを想定した作りになっている。租税体系の一覧や税率表のようなテキスト主体の表をそのまま流すと崩れる。そこで、数値表は構造化し、テキスト主体の表は本文の入れ子リストへ戻す方針にした。租税体系の表は、原本PDFとOCR結果を全19行突き合わせてから変換させている。
酒税と地方税の税率表も、数値が並んでいるように見えて中身は税率の条件分岐だった。これも同じくリストへ落とした。消費税の計算例だけは扱いが違う。原本が左右2枚組で並んでいて、そのまま写すとセルに文章が混ざる。値を数値だけに保つため、番号順の縦並びへ組み替えさせてから構造化した。
入門編の1冊で、この時点の成果はこうなった。
- 構造化した表を4件追加して計6件(うち3件に恒等式)
- テキスト主体の11件は本文のリストへ戻した
- 崩れた表の総数は497件から481件へ
残ったのは、図が表としてOCRされてしまった2件だけだった。これは図を担当している別セッションの領分なので、手を出させていない。
dev で見えた残骸
表示はブラウザで確認させた。行見出しの固定と横スクロールは意図どおりに効いている。そのうえで残骸が2つ見えた。表の上で単位の表記が重複しているものと、1項目だけが見出しに化けているものだ。読者に向けて書かれていない注記の文言も、あわせて直させた。
別の2つの節では、生のMarkdown表が横にはみ出したままだった。少子高齢化の表など、原本で数値を確かめてから構造化して潰した。
テーブルの上にチャートを載せる
ここで方針を1つ足した。構造化が済んだ表には、テーブルのすぐ上にチャートを出す。歳入の推移なら積み上げ棒、比率が取れるなら構成比100%の推移というように、その表で意味が出る形にする。縦の項目が多すぎる表は代表項目に集約して残りを「その他」でまとめてよい、という裁量も渡した。対象は一部ではなく全部。図の作成には svg-diagram スキルを使わせた。
着手前に、置き場所と制約を確認させた。本文のサニタイザを通ること、画像パスの検証に引っかからないこと、図を担当しているセッションの生成物と衝突しないこと。この3つが決まらないと、作ったSVGが表示されないか、あとで上書きされる。
チャートは表のJSONから生成させた。数字を手で書き写す箇所がゼロになるので、表を直せばチャートが勝手に追いつく。テストも2本足させている。チャートの数字が表由来であることと、SVGが表より古くなっていないことだ。
一度で決まったわけではない。年度のラベルが「決算」になる、折れ線が棒と重なる、凡例が単位表記と衝突する、といった不具合が出て、そのつど直させた。最終的に6つの表すべてにチャートが付いた。
表示崩れの正体
チャートまで乗ったページを開いた。租税の分類を扱う節が、どうにも整頓されていない。
http://localhost:3200/lessons/zeimu/nyumon/sozei?ci=0&si=10
スクリーンショットを渡して、テーブルのデータがうまく抜けていないのではないか、PDFで見ても読めなかったのか、と聞いた。
答えは「読めなかった」ではなかった。図から流出したテキストが、本文に約120行そのまま残っていた。PDFの文字を座標付きで抜き直させ、図の領域を高解像度で切り出させたところ、数字は明瞭に読めた。混乱の原因は、PDFのテキスト層に旧年度版の数字が重なって残っていたことだった。目で読める図と、そこから抜けるテキストは別物である。
全8冊に同じ壊れ方
1か所で分かったなら他にもある。全冊を機械的に洗い出させたら、8冊で256枚の図から3,530行の流出テキストが出てきた。ここからは掃除スクリプトの精度を上げる作業になった。
- 最初の検出器は範囲が広すぎたので、「図の直後」という本来のシグネチャに絞り直させた
---や記号だけの行で走査が止まっていた。ある表が6行しか拾えていないのに気づいて規則を直させた- 原本PDFが手元にある入門編はこのセッションで直接確認させ、残り7冊はサブエージェントを並行で派遣して検証させた
- サブエージェントから誤爆が6件ずつ上がってきた。原因は3つに集約できたので全部塞がせた(次の図のキャプションを飲み込む件を含む)。守るべき11件はすべて守られた
- 全冊をバックアップしてから適用させ、236枚の図から3,096行を削除し、見出しに化けていた42件を段落に戻した
あわせて、「(注)」が見出しに化けている行が36件見つかった。記事の目次が注記で埋まっていたわけで、これも同じスクリプトで直させた。逆に、自動化の価値がないものは手で片付けさせている。ある記号の混入を疑って7件を洗ったところ、6件は正当な注記だった。残る1件だけを手で直させた。
数値グラフを表とチャートに置き換える
掃除が終わってから、数値を持つ図をテーブルとチャートに変換させた。税収の内訳図では、合計778,190億円が入門編の税収表の令和7年度と完全に一致した。出典の裏が取れたので、図の権利区分を台帳で確かめたうえで、構成比の図には横棒チャートを足させた。「図は準備中」のまま置いてあった所得税のページが、これで表とチャートに置き換わった。
その過程で、見出しの数字が誤っているのを見つけた。3税の合計は85.8%なのに「6割」と書いてあった。直させた。
法人税のページは、令和5年度の法人数を示す円グラフ2枚を2つの表にした。消費税のページに5行だけ残っていた多段階課税の図の断片も、恒等式が豊富なので表とチャートに変換した。チャート上で 24,000 − 14,000 = 10,000 が合っている。
テストの落ち方と、残った取りこぼし
この変換の途中で接続エラーが出て、出力がぱたりと止まった。しばらく画面が動かないので、進んでいるのかを聞いた。掃除も変換も進んではいて、指摘した節のゴミテキストはすでに消えていた。
テストは1件落ちた。正体は、自分で足させた「SVGが表より古くないか」のテストだった。編集の途中でSVGが未生成なだけで、検知としては正しい。ビルド後に走らせ直したら、126ファイル、3,321件がすべて通った。
コンテキストが膨らんできたので、進捗をドキュメントに落として締めることにした。積み残しを数え直させたら、掃除の取りこぼしがまだ127枚の図、約1,800行ぶん残っていた。原因は、長い行で走査が止まることだった。「全冊に適用した」と書いたその足元に取りこぼしが残っているのが、この日いちばん大きい積み残しになった。続きは翌日に回す。
この日の学び
- 目で読める図と、PDFから抜けるテキストは別物だった。「PDFで見ても読めなかったのか」を確かめさせて初めて、旧年度版の数字がテキスト層に重なっているという原因に行き着いた
- 検算は書いた時点では信用できない。値を1つ壊して落ちることを確認するまでが1セットだ
- チャートの数字は表のJSONから生成する。手写しがゼロなら、表を直すだけでチャートが追いつく
- 全体に効く掃除スクリプトほど、走査が途中で止まる条件を疑う。
---の行や長い行という、本文では珍しくもないものが止め木になっていた