# 개찰 데이터 6개월 관측 (2026-09-06) — 재현 안내

세 번째 증거 글(데이터 피드 페이지)의 근거. 실험이 아니라 **관측**이다 — 파이프라인이 이미 모아 둔 개찰 데이터를 한 번 훑어 관측치를 낸다. 스크립트 둘, 결과 둘.

## 원본 데이터

`~/Projects/miriboa/outreach/out/raw/*.jsonl.gz` — 조달청 공공데이터개방표준서비스 **낙찰정보**(`getDataSetOpnStdScsbidInfo`)를 `outreach/g2b_targets.py` 가 일별 창(bsnsDivCd 3=공사, 5=용역)으로 받은 원본. 439파일, 798MB, 응찰 1행 = 한 공고에 한 업체가 낸 입찰 하나. 2026-01-09(용역)/02-11(공사) ~ 09-01. 이 디렉터리에는 복사하지 않는다(정본은 미리보아 리포).

수집 주의: `g2b_targets.py` 는 `if out.exists(): continue` 라 장애 시점의 불완전 파일이 영구히 남는다(메모리 miriboa-inbound-content-track). 빈 파일(51바이트)이 휴일인지 장애인지는 `observe.py` 의 `empty_days` 와 요일로 가른다.

## 실행

```
cd ~/Projects/docs/experiments/bid-opening-observation-2026-09-06
python3 observe.py    # 약 5분 → observations.json  (규모·월별·계약방법·응찰자 수·낙찰률·부적격 사유·재공고·추정가격·신선도·결측·기관)
python3 observe2.py   # 약 5분 → observations2.json (파일별 수집 기준일, 낙찰하한율 분포, 응찰률−하한율 간격 히스토그램)
```

순수 파이썬(pandas 없음). 같은 원본이면 같은 숫자가 난다. 표로 보려면 `python3 - <<'EOF' ... EOF` 로 json 을 읽거나, 글(`../article-bidopening-ko.md`)의 표와 대조한다.

## 관측치의 뜻 (필드 → 지표)

| 지표 | 계산 |
|---|---|
| 공고 수 | `bidNtceNo` 고유값. 차수(`bidNtceOrd`)가 다르면 같은 번호로 묶인다(재공고는 `ntceKindNm`·`bidNtceOrd` 로 별도 집계) |
| 응찰자 수 | 공고당 행 수 |
| 낙찰률 | `sucsfYn=Y` 행의 `fnlSucsfRt`(없으면 `bidprcRt`) — 예정가격 대비 낙찰가 % |
| 낙찰하한율 | `sucsfLwstlmtRt` — 이 아래로 쓰면 자동 탈락 |
| 응찰률−하한율 간격 | `bidprcRt − sucsfLwstlmtRt`, 적격심사·소액수의·제한적최저가만(하한율이 있는 방식) |
| 부적격 사유 | `dqlfctnRsn` ≠ '정상' |
| 신선도 | `dataBssDate − opengDate` 일수 |
| 결측 | 주요 필드가 비어 있는 행 비율 |

## 한계

- 공사·용역만(물품·외자 제외). 2026-02~08 이 온전한 달이고 1월은 용역만, 9월은 하루.
- 낙찰률·응찰률은 예정가격 대비 %라, 예정가격이 공개되지 않은 방식(협상에 의한 계약 등)은 값의 뜻이 다르다 — 표에서 방식별로 나눈 이유.
- `fnlSucsfRt` 결측 17.8% 는 수집 시점에 낙찰이 확정되지 않은 공고(개찰완료·낙찰 대기)가 대부분.
- 빈 평일 둘(공사 2026-07-30, 용역 2026-08-11)은 휴일이 아니다. API 로 그날 건수를 되물으려 했으나 낙찰정보 API 가 무응답(09-06). **수집 실패 후보**로 남긴다.
