Files
Pier_v1/Code/교각모델링앱/tools/analysis_w4_residual_sizing.py
T
ahnyongjooandClaude Sonnet 5.5 964a841de1 chore(rebar): Pier_T_rebar 작업 폴더 초기 구성 및 외부 의존 파일 통합
- Code\rebar_pipeline, review_pipeline, pier-program을 정션에서 실제 폴더로 전환
- Code\rhino_mcp 및 CLAUDE_rhino배경.md 추가, .mcp.json은 PYTHONPATH로 새 위치 사용
- pier-program 내부 .git 제거(이력은 backup\pier-program_20261028.bundle에 보관)
- worktree 가이드 반영(CLAUDE.local.md, HANDOFF.md)
- D:\Rhino-MCP 경로 참조를 새 위치로 치환

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-28 11:20:18 +09:00

68 lines
3.8 KiB
Python

"""Sizing study for W4 (stale template values): how many fields of a new pier still equal the template, how many of them vary between verified piers,
and how many of those were really wrong in the P4 12:11 state (restored P4 = truth).
usage: python -X utf8 analysis_w4_residual_sizing.py (run from Code/교각모델링앱/data; needs the P4 backup before_restore_decisions_20261020)
"""
import json
def L(p): return json.load(open(p,encoding="utf-8-sig"))
def flat(o,p=()):
if isinstance(o,dict):
for k,v in o.items(): yield from flat(v,p+(str(k),))
elif isinstance(o,list):
for i,v in enumerate(o): yield from flat(v,p+(str(i),))
else: yield p,o
def num(v): return isinstance(v,(int,float)) and not isinstance(v,bool)
def load(d):
out={}
for f in("params","expected"):
for p,v in flat(L(f"{d}/{f}.json")): out[(f,)+p]=v
return out
T=load("_템플릿/운곡교P1형")
ref={n:load(f"운곡교{n}") for n in("P1","P2","P3")}
truth=load("운곡교P4") # restored, verified-equivalent
cur=load("운곡교P4/backup/before_restore_decisions_20261020") # 12:11 state
# variability from P1..P3 only (leave-P4-out)
def varies(k):
vals=[ref[n].get(k) for n in ref]
return len(set(map(repr,vals)))>1
nums=[k for k,v in T.items() if num(v)]
print("템플릿 숫자 필드:",len(nums))
var=[k for k in nums if varies(k)]
print("P1~P3에서 교각마다 달라진 숫자 필드:",len(var))
wrong=[k for k in set(cur)|set(truth) if cur.get(k)!=truth.get(k)]
print("12:11 판과 정답(복원판)이 다른 필드:",len(wrong))
resid=[k for k in nums if cur.get(k)==T.get(k)] # still template value in 12:11 state
flag_old=[k for k in resid if k in var] # variable + still template
tp=[k for k in flag_old if truth.get(k)!=cur.get(k)]
fp=[k for k in flag_old if truth.get(k)==cur.get(k)]
print("12:11 판에서 템플릿 값 그대로인 숫자 필드:",len(resid))
print(" 그중 '교각마다 달라지는' 필드(새 기준 후보):",len(flag_old),"→ 실제 틀림(TP)",len(tp),"/ 맞게 같은 값(FP)",len(fp))
missed=[k for k in wrong if k not in flag_old]
print("정답과 달랐으나 새 기준이 못 잡은 필드:",len(missed))
from collections import Counter
def grp(k): return ".".join(k[:3])
print("TP 분포:",Counter(grp(k) for k in tp).most_common(12))
print("FP 분포:",Counter(grp(k) for k in fp).most_common(12))
print("missed 분포:",Counter(grp(k) for k in missed).most_common(12))
# compare to current C3b height-only rule
import sys; sys.path.insert(0,"../")
from engine.verify import height_like, EL_MIN, EL_MAX
c3b=[k for k in resid if EL_MIN<=abs(cur[k])<=EL_MAX and height_like(k[2:])]
print("현행 C3b(높이형) 기준 후보:",len(c3b),"→ TP",sum(1 for k in c3b if truth.get(k)!=cur.get(k)))
print("\n=== 정밀도 개선 시험")
def distinct(k): return len(set(repr(ref[n].get(k)) for n in ref))
# bundle changed vs template (section = file + first key)
def sec(k): return k[:2]
changed_secs={sec(k) for k in set(cur)|set(T) if cur.get(k)!=T.get(k)}
def stat(name,flag):
tp=[k for k in flag if truth.get(k)!=cur.get(k)]
print(f"{name}: 후보 {len(flag)} / 실제 틀림 {len(tp)} / 정밀도 {len(tp)/max(1,len(flag)):.0%}", [".".join(k[1:4]) for k in tp])
stat("A 교각마다 달라짐(2종 이상)", flag_old)
stat("B A + 바뀐 묶음 안", [k for k in flag_old if sec(k) in changed_secs])
stat("C B + 3교각 모두 서로 다름", [k for k in flag_old if sec(k) in changed_secs and distinct(k)==3])
# C with the 3 mostly-structural groups removed
skip=("translation_checks","layers","table","drawing_datums")
stat("D C + 구조성 그룹 제외", [k for k in flag_old if sec(k) in changed_secs and distinct(k)==3 and not any(s in k for s in skip)])
print("C 후보 분포:",Counter(grp(k) for k in flag_old if sec(k) in changed_secs and distinct(k)==3).most_common(10))