Claude Media
Claude Codeで積算ソフトのCSVと手拾い表を突き合わせる — 数量差・欠落・単位違い

Claude Codeで積算ソフトのCSVと手拾い表を突き合わせる — 数量差・欠落・単位違い

積算ソフトが出力したCSVと手拾いの数量表を、工種・名称・規格をキーに結合して差異を一覧にする手順です。表記ゆれの正規化、単位違いの扱い、差異の理由を人が付ける分担を示します。

積算ソフトの出力と手拾いの数量表が合わないとき、2つの表を目で突き合わせると時間がかかります。しかも見落としが残ります。Claude Codeには、表を正規化して結合し、差異を一覧にするスクリプトを書かせて実行させられます。差の有無はスクリプトが機械的に判定し、差が出た理由を調べて決めるのは人です。

この記事は、工種・名称・規格をキーにした突き合わせの作り方を扱います。例のCSVは手順の確認用に作った合成データで、実際の工事のものではありません。図面PDFから数量を拾う段階は、Claudeで積算の数量拾いをする方法にあります。

差異は4種類に分けて出す

2つの表の差は、「数量が違う」だけではありません。原因も対処も違うので、区分を分けて出すと後の仕分けが楽になります。

区分

差異の4区分

  • 数量差

    キーが両方にあり、数量が許容差を超えて違う行です。拾い漏れや拾い過ぎ、範囲の解釈違いが原因になります。

  • ソフトのみ・手拾いのみ

    片方にしかない行です。拾い忘れのほか、規格の書き方が違って別の行に見えているだけの場合があります。

  • 単位違い

    同じ品目で単位が違う行です。kgとtのように換算できるものは換算して比べ、換算できないものは人に回します。

  • 一致

    許容差の範囲で数量が合った行です。件数だけ報告し、確認の対象から外します。

「一致」を出力に残すのは、突き合わせの網羅を示すためです。出力の行数がキーの総数と合っていれば、読み飛ばされた行がないと言えます。

突き合わせのルールを先に決める

スクリプトを書かせる前に、判定のルールを文章で決めます。ここが曖昧だと、実行のたびに結果が変わります。

決めること例決めないと起きること
結合キー例工種+名称+規格決めないと起きること同じ品名で規格違いの行が混ざる
表記の正規化例全角半角、φとΦ、空白を揃える決めないと起きること同じ規格が別のキーになる
許容差例相対0.5%決めないと起きること端数処理の差が数量差として大量に出る
単位の扱い例kg→tのみ換算、他は人に回す決めないと起きることm2とm3のような別物が黙って比べられる
重複キー例止めて報告する決めないと起きること後の行が前の行を上書きして数量が消える

許容差は、ソフトの端数処理と手拾いの丸め方で決まります。例の0.5%は説明用の値なので、自社の取り決めに置き換えてください。重複キーを止めるのは、同じ規格の行を合算するかどうかが現場の判断になるためです。

判定ルールをCLAUDE.mdに置く

CLAUDE.mdは、プロジェクトの直下に置くmarkdownファイルです。Claude Codeは毎回のセッションの開始時に読みます。公式のベストプラクティスは、この中に「作業を確かめる正確なコマンド」を書くことを勧めています。積算の突き合わせ用フォルダには、次のように置けます。

# 積算の突き合わせ
 
## 入力
- soft.csv: 積算ソフトの出力(列: 工種,名称,規格,数量,単位)
- hand.csv: 手拾いの数量表(同じ列)
- 文字コード: soft.csvはcp932、hand.csvはutf-8(開く前に確かめた結果を書く)
 
## ルール
- 数量の前処理: 桁区切りのカンマは除き、小計・合計・空行・見出しの繰り返し行は読み飛ばす
- 結合キーは工種+名称+規格。NFKCで正規化し、空白とφ/Φの違いは無視する
- 許容差は相対0.5%。超えたら「数量差」
- 換算するのはkg→tだけ。他の単位違いは「換算不可」で出力する
- キーが重複したら止めて報告する。合算はしない
 
## 確かめ方
- python3 -I reconcile.py soft.csv hand.csv
- 出力の行数がキーの総数と一致していること

列名や単位の揺れは現場ごとに違います。自社のCSVの見出しに合わせて書き換えます。

文字コードは、CSVを読ませる前に確かめておく項目です。国内のソフトやExcelが書き出すCSVはShift_JIS系(Pythonではcp932)のことがあり、UTF-8として読むとUnicodeDecodeErrorで止まります。どちらで出るかはソフトと書き出し設定によるので、既定値を決めつけず、ファイルごとにCLAUDE.mdへ書きます。

確かめ方は、実際に読んでみるのが早い方法です。次の1行はUTF-8として読めるかを試し、読めなければ例外で止まります。止まったファイルはcp932で試し、日本語が崩れず読めたほうをCLAUDE.mdに書きます。

python3 -I -c "open('soft.csv', encoding='utf-8-sig').read()"

例のコードがutf-8-sigなのは、ExcelがUTF-8で書き出すときに付ける先頭のBOMを読み飛ばすためです。cp932のファイルを読むときは、load()のencoding=をその値に替えます。2つのCSVで文字コードが違う場合は、load()に引数で渡す形に直します。

数量の列にも前処理が要ります。1,234.5のような桁区切りのカンマは、そのままDecimalに渡すと例外になります。小計や合計の行、見出しが途中で繰り返される行は、品目として読まれて数量の合算や重複キーの原因になります。「カンマは除く、小計・合計は読み飛ばす」をルールに書いておけば、スクリプトの側で落とせます。

スクリプトを書かせる

次の依頼は、ルールがCLAUDE.mdにある前提で短く書けます。

soft.csvとhand.csvを突き合わせるreconcile.pyを書いて、実行して。
結果が出たら、数量差の行と片方にしかない行を一覧にして。
差の理由は推測で書かず、「要確認」とだけ書いて。

最後の一文が大事です。差の理由は図面や拾いの根拠を見た人にしか決められません。Claudeに推測で埋めさせると、もっともらしい理由が付いて、確認の手間が増えます。

書かれるスクリプトは毎回同じ形にはなりません。例として挙げる形のものが得られれば十分です。

import csv, sys, unicodedata, re
from decimal import Decimal
 
UNIT = {"m3": "m3", "㎥": "m3", "m2": "m2", "㎡": "m2",
        "m": "m", "t": "t", "kg": "kg"}
TO_BASE = {("kg", "t"): Decimal("0.001")}  # kg -> t
 
def norm_text(s):
    s = unicodedata.normalize("NFKC", s)
    s = s.replace("Φ", "φ").replace("Ф", "φ")
    return re.sub(r"\s+", "", s)
 
def norm_unit(u):
    u = unicodedata.normalize("NFKC", u).strip().lower()
    return UNIT.get(u, u)
 
def load(path):
    rows = {}
    with open(path, encoding="utf-8-sig", newline="") as f:
        for r in csv.DictReader(f):
            if not r["数量"].strip() or r["名称"].strip() in ("小計", "合計"):
                continue
            key = (norm_text(r["工種"]), norm_text(r["名称"]),
                   norm_text(r["規格"]))
            if key in rows:
                sys.exit(f"重複キー: {key} ({path})")
            rows[key] = (Decimal(norm_text(r["数量"]).replace(",", "")),
                         norm_unit(r["単位"]))
    return rows
 
def main(soft_path, hand_path, tol=Decimal("0.005")):
    soft, hand = load(soft_path), load(hand_path)
    print("区分,工種,名称,規格,ソフト,手拾い,差,備考")
    for key in sorted(soft.keys() | hand.keys()):
        s, h = soft.get(key), hand.get(key)
        k = ",".join(key)
        if s is None:
            print(f"手拾いのみ,{k},,{h[0]}{h[1]},,")
        elif h is None:
            print(f"ソフトのみ,{k},{s[0]}{s[1]},,,")
        else:
            (sq, su), (hq, hu) = s, h
            note = ""
            if su != hu:
                factor = TO_BASE.get((hu, su))
                if factor is None:
                    print(f"単位違い,{k},{sq}{su},{hq}{hu},,換算不可")
                    continue
                hq, hu, note = hq * factor, su, f"{h[1]}から換算"
            diff = hq - sq
            kind = "数量差" if abs(diff) > abs(sq) * tol else "一致"
            print(f"{kind},{k},{sq}{su},{hq}{hu},{diff:+},{note}")
 
if __name__ == "__main__":
    main(sys.argv[1], sys.argv[2])

数量の計算にfloatでなくDecimalを使うのは、0.1+0.2のような2進数の誤差を、許容差の判定に混ぜないためです。Claudeがfloatで書いてきたら、CLAUDE.mdに「数量はDecimalで扱う」と1行足します。

合成データで実行した結果

次の2つのCSVで実行しました。ソフト側は9行、手拾い側も9行で、表記を意図的にずらしてあります。手拾い側は㎥や全角のm、Φ100、鉄筋のkg表記を含みます。

python3 -I reconcile.py soft.csv hand.csv

出力は次のとおりです(Python 3.12.2)。

区分,工種,名称,規格,ソフト,手拾い,差,備考
一致,コンクリート,型枠,普通合板,312.0m2,312m2,+0.0,
一致,コンクリート,生コンクリート,24-8-20,96.4m3,96.4m3,+0.0,
ソフトのみ,仮設,足場,くさび式,640.0m2,,,
一致,土工,埋戻し,購入土,185.0m3,185m3,+0.0,
数量差,土工,掘削,バックホウ0.8m3,420.5m3,418.0m3,-2.5,
手拾いのみ,残土処分,残土運搬,10tダンプ,,180m3,,
一致,配管,硬質塩化ビニル管,VPφ100,126.0m,126m,+0.0,
数量差,配管,硬質塩化ビニル管,VPφ150,58.0m,64.0m,+6.0,
一致,鉄筋,異形棒鋼,SD345D13,2.85t,2.850t,+0.000,kgから換算
ソフトのみ,鉄筋,異形棒鋼,SD345D16,4.10t,,,
手拾いのみ,鉄筋,異形棒鋼,SD345D19,,1.20t,,

9行と9行が、11個のキーに結合されました。㎥とm3、全角と半角、φとΦの違いは正規化で吸収され、鉄筋D13は2850kgが2.850tに換算されて一致しています。

出力を読む順番

11行のうち一致は5行で、確認が要るのは残りの6行です。区分ごとに、疑う順番が変わります。

  • 数量差(2行): 掘削は2.5m3の差で、相対0.6%です。許容差0.5%をわずかに超えたものです。VPφ150の6.0mは約10%で、拾いの範囲を疑う大きさです。
  • ソフトのみ・手拾いのみ(4行のうち足場と残土運搬): 片方の表にしか工種が出ていません。ソフト側に足場が入っていて手拾いに無いなら、手拾い側が仮設を拾っていない可能性があります。
  • 規格違いの疑い(鉄筋の残り2行): D16がソフトのみ、D19が手拾いのみです。同じ異形棒鋼で径だけ違うので、2行をまとめて「規格の書き違い」と見て図面の配筋表を確かめます。

最後の例が、欠落を欠落のまま読まない理由です。片方にしかない行が、反対側の別の行と対になっている場合があります。「ソフトのみと手拾いのみで、工種・名称が同じで規格だけ違う組を挙げて」と頼めば、対になりそうな行を並べさせられます。ただし、同じ品目とみなすかどうかを決めるのは人です。

落とし穴

換算の向きと、ソフト側の数量が0の行

例のスクリプトが持つ換算は、手拾いがkg、ソフトがtの組(("kg", "t"))だけです。ソフトがkgで手拾いがtの場合は「換算不可」になるため、逆向きを使う現場では("t", "kg"): Decimal("1000")を足します。もう1つ、ソフトの数量が0の行は、許容差の幅も0になります。少しでも差があれば数量差に出るので、判定をabs(diff) > max(abs(sq), abs(hq)) * tolに直すと、0の行でも相対で比べられます。

落とし穴

欠落を2行に分けたまま「拾い漏れ」と報告する

規格の書き違いは、「ソフトのみ」と「手拾いのみ」の2行になって出ます。どちらも数量が出ているので、片方の拾い漏れに見えます。規格以外が同じ組を先に探してから、残りを欠落として扱います。

実行を任せるときの設定

2回目以降は、スクリプトがあるので実行だけを任せられます。非対話モードのclaude -pは、1回の依頼を実行して結果を返します。権限の扱いは、公式のheadless解説に沿って決めます。

claude -p "reconcile.pyを実行し、数量差の行だけ表にして" \
  --allowedTools "Read,Bash(python3 -I reconcile.py *)"

--allowedToolsに載せたツールは、確認なしで使えます。Bashには、コマンドを指定した形で書けます。上の例は、reconcile.pyの実行だけを通す書き方です。Claude Code v2.1.302以降のclaude -pは、権限モードの指定がなければManualモードで始まります。一覧にない操作で承認が要るものは、承認する仕組みが無い限り拒否されます。

突き合わせは入力を書き換えないので、Editは載せません。結果をファイルに残したいときは、Claudeに書かせず、人がpython3 -I reconcile.py soft.csv hand.csv > diff.csvを実行します。

突き合わせの結果が正しいかを確かめる

スクリプトが「一致」と出したことは、数量が正しいことを意味しません。2つの表が同じ間違いを共有していれば、一致します。確かめる範囲は次のとおりです。

  1. キーの総数: 出力の行数が、ソフトの行と手拾いの行の和集合の数と合っているか
  2. 一致の抜き取り: 一致の行から数本を選び、元のCSVで数量と単位を目で照合する
  3. 既知の差を仕込む: 数量を1行だけ変えたCSVを作り、「数量差」に出ることを確かめる

公式のベストプラクティスは、Claudeに完了の証拠として「実行したコマンドとその出力」を求めるよう勧めています。入荷予定との照合は入荷検品の差異の仕分け、見積書同士の比較は相見積もりの比較表に例があります。

まとめ

数量の突き合わせは、判定ルールをCLAUDE.mdに書いてスクリプトに判定させ、差の理由は人が付ける分担にすると回しやすくなります。2回目からはスクリプトを再利用でき、変わるのは入力のCSVだけです。

最初に確かめたいのは、自社のCSVでキーが重複しないかどうかです。重複が出たら、合算してよい行かどうかを現場の判断で決めてから、ルールに書き足します。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/overview
  2. code.claude.com/docs/en/best-practices
  3. code.claude.com/docs/en/headless
  4. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn