獣医疫学メモ帳

獣医疫学(に関係ないかもしれない)メモ帳。

PythonでPDFから表を抽出してcsvに変換する(Bing AIにサポートしてもらいながら)

背景

農林水産省が提供している「各種抗生物質・合成抗菌剤・駆虫剤・抗原虫剤の販売高と販売量」という資料がある。
この資料、1ページ目にタイトル、2ページ目から最終ページまで表、という形式になっているのだが、2019年分まではPDFでしか提供されていない。(2020年分からはExcelファイルも提供されている。)
コピペしてテキスト整形しようにもテキストの位置がガタガタで上手くいかない。Excelの「データの取得」でPDFを開くのを試しても、ページ毎に別々のシートに分割されるし(これはまあいい)、ヘッダー行・見出し列がなぜかシート毎に異なる行・列に配置されるし、セル内改行があると改行位置で行分割されるし、で「整形に何時間かかるんだ…」なファイルにしかならない。ということでRでなんとかできないかとtabulizerパッケージを試してみたのだがこれまた上手くいかず、PDF内で複数列に分かれているデータが同じ列として認識されてしまう。
ということで、最近Pythonを勉強しているのと生成系AIとの付き合い方を模索中なのもあり、Bing AIにPythonコードを書かせてみた。

実践

プロンプト

次のPDFについて話をしましょう。
https://www.maff.go.jp/nval/iyakutou/hanbaidaka/pdf/h17hanbaikoukin20180205.pdf

このPDFに含まれる表をcsv化する方法を探しています。
以下の手順で、Python用のコードを作成してください。
- Pythonのcamelotライブラリからread_pdf()を使用し、2ページ目から最終ページまでの表を読み取る。その際line_scaleは適切なものを使用する。
- 読み取られた表のリストに含まれる全ての表から1~2行目を削除する。
- 表のリストを縦に結合して1つの表を作成する。
- 列名は以下を使用する:class,group,substance,administration,sales,amount,beef,dairy,horse,pig,broiler,layer,dogcat,freshwater,seawater,ornamental,others
- substance列に「成分・計」の文字が含まれる行は削除する。
- group列に「系統名・計」の文字が含まれる行は削除する。
- セル内に含まれる改行は半角スペースに置換する。
- sales列からothers列までは数値形式に変換する。
- beef列からothers列に含まれる空白セルには代わりに0を入力する。
- 作成した表をcsv形式で保存する。

ライブラリと使用する関数の選定は自分で行った。

回答されたコード

import camelot
import pandas as pd

# PDFから表を読み取る
tables = camelot.read_pdf('https://www.maff.go.jp/nval/iyakutou/hanbaidaka/pdf/h17hanbaikoukin20180205.pdf', pages='2-end', line_scale=40)

# 全ての表から1~2行目を削除し、表のリストを縦に結合
df = pd.concat([table.df.iloc[2:] for table in tables])

# 列名を設定
df.columns = ['class', 'group', 'substance', 'administration', 'sales', 'amount', 'beef', 'dairy', 'horse', 'pig', 'broiler', 'layer', 'dogcat', 'freshwater', 'seawater', 'ornamental', 'others']

# substance列に「成分・計」の文字が含まれる行とgroup列に「系統名・計」の文字が含まれる行を削除
df = df[~df['substance'].str.contains('成分・計')]
df = df[~df['group'].str.contains('系統名・計')]

# セル内に含まれる改行を半角スペースに置換
df.replace('\n', ' ', regex=True, inplace=True)

# sales列からothers列までを数値形式に変換
cols = ['sales', 'amount', 'beef', 'dairy', 'horse', 'pig', 'broiler', 'layer', 'dogcat', 'freshwater', 'seawater', 'ornamental', 'others']
df[cols] = df[cols].apply(pd.to_numeric, errors='coerce')

# beef列からothers列に含まれる空白セルには代わりに0を入力
df[cols] = df[cols].fillna(0)

# 作成した表をcsv形式で保存
df.to_csv('output.csv', index=False)

結果

このコードを試すと無事csv化できた。
ちなみに思い通りの結果が得られるまでに3度ほどリテイクを出している。

感想

実はBing AIにコードを書かせる前に自分でPythonコードを書いていたしそれで成功もしていた。しかしふと思いついてBing AIにコードを書かせてみると自分で書くより数倍速かった。
まあPythonに慣れたらこれぐらいの内容は自分でコードを書く方が速いんだろうが、初心者のうちは生成系AIにコードを書いてもらって、それを自分で写経しつつエラーを修正していくのはかなり有用かもしれない。