knowledge(경쟁사): 통제시험 수행 불가(라이선스 없음) 반영 · 요율 두 부류 구분
전제 정정 - STmate 라이선스가 없어 13번 T1~T11 을 띄울 수 없음. 「나중에 시험으로 확정」 계획이 성립하지 않으므로 13번 머리에 못박고, 대체 경로 셋과 그것으로 이미 푼 것을 적음. 목록 자체는 장부 겸 절차서로 남김 - 남은 것을 늘릴 유일한 수단은 표본 늘리기. 특히 수공(S)·실적(TA/SA) 형식 표본이 하나도 없어 그 세 형식은 전혀 검증되지 않음 통제시험 없이 새로 밝힌 것 - 요율 항목이 두 부류임. 고정 칸(보험·산안비·이윤)과 규모·기간에 따라 행이 움직이는 것(간접노무비·기타경비·일반관리비). 뒤쪽은 고정 좌표라는 모델 자체가 틀림 - 이윤 좌표 RATE_R[20,16] 확정 (3건 일치) - 요율이 그 프로젝트 표에 아예 없는 경우가 실재함. 간선임도 간접노무비 14.5%, 소광 이윤 13.435% 가 자기 RATE_R 에 없음 = 사용자 수동 입력. 재구현은 rate_source 를 table(coord)/manual 로 갈라 저장해야 함 교차검증 스크립트 2개 추가. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Prk9BCHG1EMAywk9k8wegA
This commit is contained in:
@@ -59,6 +59,9 @@
|
||||
| 중기 시간당 사용료 | 손료 계수 없음 | **A** — 손료율·취득가·연료·잡품률 25개 장비 실측 | 17번 §3·§4 |
|
||||
| 호표 성분 소계 반올림 | 미확정 | **A** — 절사 (중기 398건 100%) | 17번 §2 |
|
||||
| 자재 6후보 자동채택 | C | **A 실증** — 0 아닌 후보 중 **최저가**. 복수후보 76/76 | 17번 §5.5, 07번 §5 |
|
||||
| 이윤 요율 좌표 | 미확정 | **A** — `RATE_R[20,16]`, 3건 일치 | 15번 §4 |
|
||||
| 요율의 출처 | 표에서 고른다고 가정 | **표에 없는 요율이 실재** — 사용자 수동 입력 경로 필수 | 15번 §4 |
|
||||
| 통제시험 T1~T11 | 「나중에 수행」 | **수행 불가 — STmate 라이선스 없음.** 표본 늘리기가 유일한 확장 수단 | 13번 머리, 15번 §6 |
|
||||
|
||||
분석일: 2026-09-13
|
||||
대상: `C:\경영정보(주)`
|
||||
|
||||
@@ -2,8 +2,31 @@
|
||||
|
||||
> 정적 분석만으로는 확정할 수 없는 경계를 **시험 단위로 갈라 놓은 유일한 목록**이다. 05~12번 문서에 흩어져 있던 "미확정·C등급" 항목을 여기로 모았다. 각 문서는 이제 이 목록을 가리킨다.
|
||||
|
||||
기준일: 2026-09-13
|
||||
대상: `C:\경영정보(주)` — **원본은 읽기만 한다.** 시험은 프로젝트 **복제본**에서 한다.
|
||||
기준일: 2026-09-13 (전제 갱신 2026-09-13)
|
||||
대상: `C:\경영정보(주)` — **원본은 읽기만 한다.**
|
||||
|
||||
## ⛔ 전제 정정 — 이 시험들은 **실행할 수 없다**
|
||||
|
||||
**STmate 라이선스가 없어 프로그램을 띄울 수 없다.** 따라서 아래 T1~T11은 **수행 불가**이며, 「나중에 시험으로 확정한다」는 계획은 성립하지 않는다.
|
||||
|
||||
이 목록은 버리지 않고 **두 가지 용도로 남긴다.**
|
||||
|
||||
1. **무엇이 왜 확정 불가인지**를 항목별로 밝혀 둔 장부 — 어떤 값이 「모르는 것」인지 분명히 해 두어야 Aislo가 그 자리를 정책 플래그로 열어 둘 수 있다.
|
||||
2. **라이선스가 생기거나 실무자가 대신 눌러 줄 수 있게 되면** 그대로 쓸 절차서.
|
||||
|
||||
**대신 쓸 수 있는 길**은 세 갈래이며, 이미 상당 부분 성과가 났다.
|
||||
|
||||
| 대체 경로 | 무엇을 풀었나 |
|
||||
|---|---|
|
||||
| **출력 XLSX 역추적** — 비고 칸에 밑수·요율이 적혀 있음 | 보험 요율 좌표 6개, 이윤 좌표 (15번 §4) |
|
||||
| **골든셋 회귀** — 내역으로 원가계산을 다시 세워 대조 | 원가계산 전 과정, 끝전조정, 차수 규칙 (16번) |
|
||||
| **출력 시트 전수 검정** — 일위대가·중기·자재단가대비표 | 성분 절사, 중기 손료율, 자재 채택=최저가 (17번) |
|
||||
|
||||
**표본을 늘리는 것이 통제시험을 대신하는 유일한 확장 수단이다.** 프로젝트마다 공사규모·기간·공종이 다르므로, 표본이 늘수록 좌표와 조건이 저절로 좁혀진다.
|
||||
|
||||
---
|
||||
|
||||
아래는 라이선스가 생겼을 때를 위한 원 절차서다. 시험은 프로젝트 **복제본**에서 한다.
|
||||
|
||||
## 0. 왜 시험이 필요한가
|
||||
|
||||
|
||||
@@ -109,9 +109,32 @@ A등급 불변조건으로 적혀 있으나 실데이터에 **59행 중 2행이
|
||||
|
||||
⇒ **`RATE_R`의 행 45~49는 요율 행렬이 아니라 부속표 영역**이고, 행 0~44가 규모·기간 행렬임.
|
||||
|
||||
### 아직 좌표가 안 갈리는 항목
|
||||
### 항목이 두 부류로 갈림 — 통제시험 없이 밝힌 것
|
||||
|
||||
`연금보험료(4.5%)`·`일반관리비(6%)`·`이윤(15%)`·`환경보전비(0.8%)`·`부가가치세(10%)`는 **같은 값이 표 안 여러 곳에 있어** 단독으로는 못 가름. 확정하려면 **한 프로젝트에서 그 값만 특이한 숫자로 바꿔 저장**하는 통제시험이 필요함(13번 T1).
|
||||
프로젝트 5건의 요율을 각자의 `RATE_*` 표에 겹쳐 보면 항목이 두 갈래로 나뉨.
|
||||
|
||||
**① 고정 칸** — 공사규모·기간과 무관. 전 프로젝트에서 같은 좌표.
|
||||
|
||||
```text
|
||||
산재보험료 RATE_R[45, 1] 건강보험료 RATE_R[46, 1]
|
||||
연금보험료 RATE_R[46, 6] 장기요양 RATE_R[46, 11]
|
||||
고용보험 등급1~7 RATE_R[45, 11..17]
|
||||
산업안전보건관리비 RATE_R[20, 17] 율 · [20, 18] 기초액 ← 3건 전부 유일
|
||||
이윤 RATE_R[20, 16] ← 3건 전부 일치
|
||||
```
|
||||
|
||||
**② 규모·기간에 따라 칸이 움직임** — 간접노무비·기타경비·일반관리비. 전 프로젝트에 공통인 좌표가 **없음.** 행이 `(공사규모 밴드, 공사기간 밴드)`로 정해지므로 고정 좌표라는 모델 자체가 틀림. 열은 공종(토목·산설·조경·건축·산설건축)이고 행이 움직인다.
|
||||
|
||||
### ⚠ 요율이 표에 아예 없는 경우가 실재함
|
||||
|
||||
| 프로젝트 | 항목 | 쓴 요율 | 그 프로젝트 `RATE_R` 안에 |
|
||||
|---|---|---:|---|
|
||||
| 2024 간선임도 (기번3) | 간접노무비 | 14.5% | **없음** |
|
||||
| 2025 소광 (기번8) | 이윤 | 13.435% | **없음** |
|
||||
|
||||
즉 **사용자가 표를 거치지 않고 요율을 직접 써 넣은 것**이다. 11번 §8이 「순환이 생기면 사용자 수동 확정으로 넘긴다」고 한 경로가 실데이터에 그대로 나타났다.
|
||||
|
||||
**재구현 필수 조건** — 요율을 표에서 못 찾았다고 오류로 막으면 안 된다. `rate_source ∈ {table(coord), manual}` 을 항상 함께 저장하고, 수동값이면 좌표 없이 값만 보존해야 한다.
|
||||
|
||||
## 5. 그래서 무엇을 만들 수 있나
|
||||
|
||||
@@ -142,6 +165,14 @@ A등급 불변조건으로 적혀 있으나 실데이터에 **59행 중 2행이
|
||||
|
||||
**Aislo 목적으로는 필요한 정보가 모였음.** 「STmate 복제」는 애초에 불가능하고 목표도 아님.
|
||||
|
||||
### ⛔ 통제시험은 못 함 — 라이선스 없음
|
||||
|
||||
13번의 T1~T11은 **STmate를 띄워야 하는데 라이선스가 없어 수행 불가**다. 따라서 「나중에 시험으로 확정」이라 미뤄 둔 항목은 **다른 길로 풀거나, 모르는 채로 두고 Aislo에서 정책으로 여는 수밖에 없다.**
|
||||
|
||||
실제로 대체 경로가 잘 들었다 — 출력 XLSX 역추적·골든셋 회귀·출력 시트 전수 검정으로 09번이 꼽은 여섯 차단점 중 **둘을 완전히, 하나를 절반** 풀었다(17번 §7).
|
||||
|
||||
**남은 것을 늘릴 방법은 표본을 늘리는 것 하나뿐이다.** 공사규모·기간·공종·계산형식이 다른 프로젝트가 들어올수록 좌표와 조건이 저절로 좁혀진다. 특히 **수공형식(`S`)·실적형식(`TA`/`SA`) 표본이 하나도 없어** 그 세 형식은 지금 전혀 검증되지 않았다.
|
||||
|
||||
## 7. 다음에 할 것
|
||||
|
||||
| 순위 | 할 일 | 왜 |
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import sys, io, pathlib, re, zipfile, struct, collections, warnings, difflib
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
SRC = open(r"C:\Users\umsan\AppData\Local\Temp\claude\D--aislo-wt-sub\df75230a-b05b-4088-ad34-bb0c9da0a312\scratchpad\t1_consist.py",
|
||||
encoding="utf-8").read().split("items = collections.Counter()")[0]
|
||||
SRC = SRC.replace('sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")','')
|
||||
SRC = "\n".join(l for l in SRC.split("\n") if not l.strip().startswith("print("))
|
||||
ns = {}
|
||||
exec(SRC, ns)
|
||||
per = ns["per"]; tabs_by_proj = ns["tabs_by_proj"]
|
||||
out = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
def P(*a): print(*a, file=out)
|
||||
P("### 규모·기간 의존 항목 — 열이 고정인가 (RATE_R 행 0~44 안)")
|
||||
for lab in ("간접노무비","기타경비","일반관리비","이윤"):
|
||||
vals = {k: per[k][lab] for k in per if lab in per[k]}
|
||||
if len(vals) < 3: continue
|
||||
colsets=[]; detail=[]
|
||||
for k,v in vals.items():
|
||||
M = tabs_by_proj[k].get("RATE_R") or []
|
||||
hits = {(i,j+1) for i,row in enumerate(M[:45]) for j,x in enumerate(row) if x and abs(x-v)<1e-6}
|
||||
colsets.append({c for _,c in hits}); detail.append((k,v,sorted(hits)[:6]))
|
||||
common = set.intersection(*colsets) if colsets else set()
|
||||
P("\n■ %s 관측 %d건, 값 %s" % (lab, len(vals), sorted({v for v in vals.values()})))
|
||||
P(" 모든 프로젝트 공통 열: %s" % (sorted(common) or "없음"))
|
||||
for k,v,h in detail:
|
||||
P(" %-30s %7.3f%% %s" % (k[:30], v, ", ".join("(%d,%d)"%x for x in h) or "없음"))
|
||||
out.flush()
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""T1 대체 — 통제시험 없이, 모든 프로젝트에서 동시에 성립하는 좌표만 남긴다."""
|
||||
import sys, io, pathlib, re, zipfile, struct, collections, warnings, difflib
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
warnings.filterwarnings("ignore"); import openpyxl
|
||||
ROOT = pathlib.Path(r"D:\aislo-wt\sub\resources\knowledge\original\실무문서")
|
||||
def dbf(zf,name):
|
||||
b=zf.read(name); nrec,hlen,rlen=struct.unpack_from("<IHH",b,4)
|
||||
f=[];o=32
|
||||
while b[o]!=0x0D: f.append((b[o:o+11].rstrip(b"\0").decode("cp949","replace"),b[o+16]));o+=32
|
||||
M=[]
|
||||
for i in range(nrec):
|
||||
r=b[hlen+i*rlen:hlen+(i+1)*rlen]; v=[];c=1
|
||||
for nm,ln in f:
|
||||
s=r[c:c+ln].decode("cp949","replace").strip(); c+=ln
|
||||
try: v.append(round(float(s),4) if s else 0.0)
|
||||
except ValueError: v.append(0.0)
|
||||
M.append(v)
|
||||
return M
|
||||
RE = re.compile(r"([\d.]+)\s*%")
|
||||
def rates(xp):
|
||||
wb=openpyxl.load_workbook(xp,data_only=True,read_only=True)
|
||||
if "공사원가계산서" not in wb.sheetnames: return {}
|
||||
ws=wb["공사원가계산서"]; out={}
|
||||
for row in ws.iter_rows(min_row=1,max_row=45,max_col=7,values_only=True):
|
||||
cells=["" if v is None else v for v in row]
|
||||
lab=re.sub(r"\s+","",next((str(c) for c in cells[:3] if isinstance(c,str) and str(c).strip()),""))
|
||||
note=next((str(c) for c in cells[4:7] if isinstance(c,str) and "%" in str(c)),"")
|
||||
if not lab or not note: continue
|
||||
m=RE.search(note)
|
||||
if m and lab not in out: out[lab]=float(m.group(1))
|
||||
return out
|
||||
|
||||
pairs=[]
|
||||
for stc in sorted(ROOT.rglob("*.STC")):
|
||||
best=None;bs=0
|
||||
for x in list(stc.parent.rglob("*.xlsx"))+list(stc.parent.parent.rglob("*.xlsx")):
|
||||
if x.name.startswith("~$"): continue
|
||||
s=difflib.SequenceMatcher(None,stc.stem,x.stem).ratio()
|
||||
if s>bs: bs,best=s,x
|
||||
if best and bs>0.55: pairs.append((stc,best))
|
||||
|
||||
# 항목 -> {프로젝트: 요율}
|
||||
per = {}
|
||||
tabs_by_proj = {}
|
||||
for stc,xp in pairs:
|
||||
try: r = rates(xp)
|
||||
except Exception: continue
|
||||
if not r: continue
|
||||
z = zipfile.ZipFile(stc); tabs={}
|
||||
for n in z.namelist():
|
||||
if re.match(r"RATE.*\.DBF$", n, re.I):
|
||||
try: tabs[n.upper().replace(".DBF","")] = dbf(z,n)
|
||||
except Exception: pass
|
||||
key = stc.stem[:28]
|
||||
tabs_by_proj[key]=tabs; per[key]=r
|
||||
|
||||
items = collections.Counter()
|
||||
for k,r in per.items():
|
||||
for lab in r: items[lab]+=1
|
||||
print("프로젝트 %d건, 항목별 관측 수" % len(per))
|
||||
print()
|
||||
print("%-22s %5s %6s %s" % ("항목","관측","값종류","전 프로젝트에서 동시에 맞는 좌표"))
|
||||
print("-"*96)
|
||||
for lab, cnt in items.most_common():
|
||||
vals = {k: per[k][lab] for k in per if lab in per[k]}
|
||||
if len(vals) < 3: continue
|
||||
# 후보 좌표: 첫 프로젝트에서 값이 맞는 모든 자리
|
||||
first = next(iter(vals))
|
||||
cand = set()
|
||||
for tn, M in tabs_by_proj[first].items():
|
||||
for i,row in enumerate(M):
|
||||
for j,x in enumerate(row):
|
||||
if x and abs(x-vals[first])<1e-6: cand.add((tn,i,j))
|
||||
for k in list(vals)[1:]:
|
||||
keep=set()
|
||||
for (tn,i,j) in cand:
|
||||
M = tabs_by_proj[k].get(tn)
|
||||
if M and i<len(M) and j<len(M[i]) and abs(M[i][j]-vals[k])<1e-6: keep.add((tn,i,j))
|
||||
cand = keep
|
||||
if not cand: break
|
||||
nv = len(set(vals.values()))
|
||||
s = ", ".join("%s[%d,%d]"%(t,i,j+1) for t,i,j in sorted(cand)[:4]) or "없음"
|
||||
if len(cand)>4: s += " …외 %d" % (len(cand)-4)
|
||||
print("%-22s %5d %6d %s" % (lab[:22], len(vals), nv, s))
|
||||
Reference in New Issue
Block a user