제5장
바이오마커를 질병에 비추어 평가하기 전에, 먼저 분석법을 그 자체로 평가해야 한다. 검체를 어떻게 다루었는지, 얼마나 낮은 농도까지 볼 수 있는지, 결과가 얼마나 흩어지는지, 값이 맞는지, 분석물이 얼마나 오래 버티는지, "정상"은 어떤 모습인지, 그리고 방법이 흔들릴 때 검사실이 이를 어떻게 알아차리는지를 다룬다.
제2장에서는 바이오마커 값을 고정된 숫자로 보고, 그 숫자가 질환이 있는 환자와 없는 사람을 얼마나 잘 가르는지를 물었다. 이 질문은 숫자 자체를 믿을 수 있을 때에만 의미가 있다. 런 간 변동계수가 25 %인 분석법으로 얻은 민감도 84 %는 생물학만큼이나 분석법의 잡음을 반영한다. 분석적 검증은 제1장에서 소개한 생애주기 가운데, 측정 절차가 의도한 용도에 적합한 결과를 낸다는 것을 별도의 실험과 별도의 허용 기준으로 입증하는 단계이다. 이 단계를 거쳐야 임상적 검증(제6장)이 검증할 안정된 대상을 갖게 된다.
WIA-BIO-004는 분석 성능을 세 곳에서 다룬다. 본 명세(§3.1.2 전분석 요구사항, §3.2 측정 기준과 정도관리, §3.3.1 참고구간), 품질 지표 부속서(검증 수준, 분석 지표, 안정성 지표, 정도관리), 그리고 @wia/bio-004 SDK의 몇몇 상수이다. 이 출처들은 수치 한계에서 항상 일치하지는 않는다. 이 장은 검사실이 실제로 일하는 순서, 즉 검체, 검출 능력, 정밀도, 정확도와 직선성, 안정성, 참고구간, 일상 정도관리 순으로 진행하고, 마지막에 모든 출처를 밝히고 이 책이 어떤 한계를 왜 적용하는지를 정리한 단일 대조표로 마무리한다.
바이오마커 데이터에 쌓이는 변동의 상당 부분은 검체가 분석기에 도달하기 전에 생긴다. 방금 식사한 환자, 아침이 아니라 저녁에 채취한 검체, 채혈자가 정맥을 찾는 동안 묶여 있던 토니켓, 반나절 동안 세포 위에 방치된 혈장, 여러 번 녹였다 다시 얼린 튜브는 모두 일부 분석물의 측정 농도를 좋은 분석법의 분석 불정밀도보다 크게 바꿀 수 있다. 환자군과 대조군이 서로 다른 조건에서 채취되면—편의 표본으로 구성한 환자-대조군 연구의 전형적인 약점이다—이런 효과가 두 군 사이에서 달라지고, 진단 신호를 만들어 내거나 가려 버릴 수 있다. 전분석 프로토콜은 이 요인들을 일정하게 유지하거나, 적어도 기록하여 나중에 분석할 수 있게 하려고 존재한다.
명세 §3.1.2는 통제 요인 여덟 가지를 두 묶음으로 제시한다. 채취 단계에서는 분석물이 요구하는 경우의 금식 여부, 하루 중 시각(일주기 변동 통제), 환자 자세(앉음/서 있음), 혈액의 경우 1분 미만의 토니켓 시간이다. 처리 단계에서는 혈장의 경우 2시간 미만의 처리 소요 시간, 원심분리 속도와 시간, 보관 온도, 3회 미만의 동결-해동 횟수이다.
지켰지만 기록하지 않은 프로토콜은 감사할 수 없고, 기록하지 않은 요인은 나중에 공변량으로 쓸 수 없다. 따라서 실무적인 질문은 각 전분석 요인이 WIA-BIO-004 레코드의 어디에 자리하느냐이다. 일부는 전용 필드가 있지만 여러 요인은 그렇지 않으며, v1.0은 이들을 위한 규범적 자리를 두지 않았다. 아래 표는 현재 상태를 보여 준다.
| 요인 (§3.1.2) | 기록 위치 | 비고 |
|---|---|---|
| 금식 여부 | patient.fastingStatus (§7.1), PatientSample.fastingStatus (SDK) | 불리언뿐이며 금식 시간은 담기지 않는다. |
| 채취 시각 | dateCollected | UTC로 저장된다. 일주기 분석에는 현지 시각이 필요하므로 사이트 시간대나 현지 타임스탬프를 함께 남긴다. |
| 자세 | 필드 없음 | 확장 객체에 기록한다(아래 참조). |
| 토니켓 시간 | 필드 없음 | 확장 객체에 초 단위로 기록한다. |
| 처리 소요 시간 | 유도 불가 | dateMeasured − dateCollected는 채취에서 분석까지의 시간이지 채취에서 원심분리까지의 시간이 아니다. |
| 원심분리 | sampleDetails.collectionMethod / storageConditions (SDK, 자유 텍스트) | 자유 텍스트는 기계로 검사할 수 없으므로 구조화된 값을 권한다. |
| 보관 온도 | sampleDetails.storageConditions (SDK, 자유 텍스트) | 위와 같다. |
| 동결-해동 횟수 | 필드 없음 | 명시적으로 세고, 한계에 이르면 qualityFlags.other에 메모를 남긴다. |
| 검체 상태 | qualityFlags.hemolyzed, lipemic, icteric; SDK는 clotted, diluted, outOfRange, other[]를 추가 | 전분석 정보 가운데 유일하게 1급 불리언 필드를 가진 항목이다. |
이후 버전이 규범적 구조를 추가하기 전까지, 이 책은 빠진 요인들을 SDK BiomarkerMeasurement의 metadata 객체 아래 preAnalytical라는 하위 객체에 둔다. 이 이름은 이 책의 관례일 뿐 v1.0 필드가 아니다. 중요한 것은 한 연구의 모든 사이트가 같은 이름을 일관되게 쓰는 것이다. 아래 값은 예시이다.
{
"id": "M-000731",
"biomarkerID": "BIO-12345",
"value": 12.5,
"unit": "mg/L",
"sampleType": "blood_serum",
"dateCollected": "2025-01-15T08:30:00Z",
"dateMeasured": "2025-01-15T10:45:00Z",
"qualityFlags": { "hemolyzed": false, "lipemic": false, "icteric": false, "other": [] },
"metadata": {
"preAnalytical": {
"collectionLocalTime": "2025-01-15T17:30:00+09:00",
"posture": "sitting",
"tourniquetSeconds": 40,
"minutesToCentrifugation": 55,
"centrifugation": { "rcfG": 1500, "minutes": 10, "temperatureC": 20 },
"storageTemperatureC": -80,
"freezeThawCycles": 1
},
"qcRunID": "RUN-2025-01-15-02"
}
}
이 레코드에서 두 가지를 짚어 둔다. 첫째, collectionLocalTime은 시간대 오프셋을 명시한다. UTC 값만 보는 단순한 분석은 서울에서 오후 늦게(현지 시각 17:30) 한 이 채혈을 오전 08:30 채혈로 읽는다. 둘째, qcRunID는 환자 결과를 그 결과를 승인한 정도관리 런(5.7절)과 연결한다. 이렇게 해 두면 나중에 관리 이탈로 판명된 런에서 나온 결과를 모두 회수할 수 있다.
품질 플래그는 검체가 용혈·지방혈증·황달 상태라는 사실을 기록할 뿐, 그것이 결과에 영향을 주는지는 말하지 않는다. 용혈은 어떤 분석물(적혈구 안에 고농도로 존재하는 물질이나, 유리 헤모글로빈이 광학 판독을 방해하는 분석법)에는 크게 작용하고 다른 분석물에는 거의 작용하지 않는다. 따라서 어떤 플래그를 제외 사유로 삼을지는 분석법별 결정이며 분석적 검증에 속한다. 보통은 헤모글로빈, 지질 유제, 빌리루빈을 알려진 양만큼 풀 검체에 첨가하는 간섭 시험으로 정한다(CLSI EP07 지침이 이런 설계를 다룬다). v1.0 SDK는 qualityFlags를 전혀 읽지 않는다. validateBiomarker는 넘겨받은 측정값을 모두 사용한다. 걸러 내는 일은 호출하는 쪽의 몫이며, 제외 목록은 그 분석법의 간섭 시험 결과에서 와야 한다.
import type { BiomarkerMeasurement, QualityFlags } from '@wia/bio-004/types';
// 이 분석법의 간섭 시험으로 확정된, 결과를 무효로 만드는 플래그
const REJECTING_FLAGS: (keyof QualityFlags)[] = ['hemolyzed', 'clotted'];
export function usableForAnalysis(m: BiomarkerMeasurement): boolean {
const flags: QualityFlags = m.qualityFlags ?? {};
const freezeThaw = Number(m.metadata?.preAnalytical?.freezeThawCycles ?? 0);
const flagged = REJECTING_FLAGS.some((k) => flags[k] === true);
return !flagged && freezeThaw < 3; // §3.1.2: 3회 미만
}
제외된 결과를 지워서는 안 된다. 사유별, 환자군/대조군별 제외 건수는 검증 보고서에 들어가야 한다. 두 군에서 제외 비율이 다르면 그 자체가 편향의 원천이 되기 때문이다.
품질 지표 부속서는 검출한계를 다음과 같이 정의한다.
LOD = mean_blank + 1.645 × SD_blank (단측 95 %)
LOD = mean_blank + 3 × SD_blank (더 보수적인 변형)
프로토콜은 공시료 20개와 예상 LOD 근처의 저농도 양성 검체 20개를 분석하는 것이고, 허용 기준은 선언한 LOD에서 저농도 검체의 95 % 이상이 검출(한계를 넘는 값으로 판독)되는 것이다. 1.645는 표준정규분포의 단측 95번째 백분위수이다. 공시료 결과가 대략 정규분포를 따른다면 평균 + 1.645 SD를 넘는 공시료 결과는 약 5 %에 불과하다.
첫 번째 식은 검출 능력에 관한 CLSI EP17 지침의 용어로 말하면 공시료 한계(limit of blank, LoB), 즉 분석물이 없는 검체에서 나올 수 있는 가장 높은 결과이다. 아직 검출한계가 아니다. 참 농도가 LoB와 같은 검체는 LoB를 중심으로 흩어진 결과를 내므로, 대략 절반이 LoB 아래로 떨어져 "검출되지 않음"으로 보고된다. 2단계 방식은 저농도 검체의 산포를 더해 이 틈을 메운다.
LoB = mean_blank + 1.645 × SD_blank
LoD = LoB + 1.645 × SD_low (저농도 양성 검체의 SD)
예시 수치로 계산해 보자. 공시료 반복 측정 20회의 평균이 0.020 ng/mL, SD가 0.010 ng/mL라면 LoB = 0.020 + 1.645 × 0.010 ≈ 0.036 ng/mL이다. 저농도 양성 검체의 SD가 0.015 ng/mL라면 LoD ≈ 0.036 + 1.645 × 0.015 ≈ 0.061 ng/mL이다. 부속서의 1단계 식은 0.036 ng/mL을, "3 SD" 변형은 0.050 ng/mL을 보고했을 것이다. 부속서 자체의 허용 시험—저농도 검체 20개 중 95 % 검출, 즉 20개 중 19개 이상이 한계를 넘는 것—이 바로 참된 LoD와 LoB를 가르는 기준이므로, 프로토콜을 끝까지 따르는 검사실은 대개 2단계 값에 이르게 된다. 이 책은 LoB와 LoD를 모두 계산하고, LoD를 선언하며, 20개 중 19개 검증 결과를 기록할 것을 권한다.
본 명세는 여기에 더해 LOD가 참고구간 하한의 10 % 미만일 것을 요구한다. 이 규칙은 참고구간 하한이 0보다 충분히 클 때에만 의미가 있다. 참고구간이 0에서 시작하는 분석물—§3.3.2의 PSA 예는 lowerLimit: 0.0이다—에서는 어떤 분석법도 이를 만족할 수 없으므로 적용하지 말아야 한다. 더 쓸모 있는 요구사항은 LoD(더 중요하게는 LOQ)가 바이오마커를 실제로 사용할 임상 판정 한계보다 충분히 낮은 곳에 있어야 한다는 것이다.
검출은 "있는가?"에, 정량은 "얼마나 있는가?"에 허용 가능한 정밀도로 답한다. 부속서는 LOQ를 CV가 20 % 미만인 가장 낮은 농도로 정의하고, 범위 하단에 걸친 5~7개 농도 수준을 각각 10회 반복 측정하여 정하도록 하며, 임상용 바이오마커는 보통 LOQ에서 CV 15 % 미만을 요구한다고 덧붙인다. 다음 예시 데이터는 CV 목표에 따라 LOQ가 어떻게 달라지는지 보여 준다.
| 공칭 농도 (ng/mL) | 평균 (n = 10) | SD | CV (%) | 20 % 미만? | 15 % 미만? |
|---|---|---|---|---|---|
| 0.05 | 0.058 | 0.019 | 32.8 | 아니오 | 아니오 |
| 0.10 | 0.104 | 0.024 | 23.1 | 아니오 | 아니오 |
| 0.20 | 0.197 | 0.033 | 16.8 | 예 | 아니오 |
| 0.50 | 0.492 | 0.041 | 8.3 | 예 | 예 |
| 1.00 | 1.010 | 0.052 | 5.1 | 예 | 예 |
| 2.00 | 1.980 | 0.083 | 4.2 | 예 | 예 |
20 % 규칙에서는 LOQ가 0.20 ng/mL이고, 15 % 규칙에서는 0.50 ng/mL이다. 0.20과 0.50 사이의 수준을 측정하지 않았기 때문이다. 두 목표 사이의 간격이 이렇게 넓다면 중간 수준을 추가하거나 정밀도 프로파일(농도에 따른 CV의 매끄러운 곡선)을 적합해 곡선에서 LOQ를 읽는 것이 좋다. 정밀도 프로파일은 임상 절단값이 CV가 아직 가파르게 변하는 구간에 있는지도 보여 준다. 그런 경우 작은 보정 이동만으로도 양성으로 분류되는 환자 비율이 크게 바뀌므로 중요한 경고 신호이다. LoD와 LOQ 사이의 결과는 숫자가 아니라 "검출되었으나 정량 범위 미만"으로 보고해야 한다. WIA 레코드에는 값을 그대로 저장하되 qualityFlags.other에 "below_LOQ" 같은 메모를 남길 수 있다.
정밀도는 같은 물질을 반복 측정했을 때 결과들이 서로 얼마나 가까운지를 뜻한다. 변동계수 CV = SD / 평균 × 100 %로 표현하므로 농도와 분석법이 달라도 비교할 수 있다. 두 가지 조건을 구분한다.
CLSI EP05에 기술된 널리 쓰이는 설계는 관리물질이나 풀 검체를 하루 2런, 런마다 2회씩 20일에 걸쳐 측정한다. 데이터는 분산 성분을 분리하는 계층형 분산분석으로 분석한다. 이렇게 얻은 성분은 CV가 아니라 분산으로 더해지며, 그래서 부속서는 총 정밀도를 다음과 같이 쓴다.
CV_total = √( CV²_within + CV²_between )
여기서 "between"은 런 간 성분, 즉 런이 바뀌면서 추가되는 분산을 뜻하며, 런 평균들의 원시 CV가 아니다. 원시 CV에는 런 내 잡음의 일부가 이미 섞여 있다. 예시로 런 내 성분 4 %, 런 간 성분 3 %이면 총 CV는 √(16 + 9) = 5 %이다. 설계가 일간 성분까지 분리한다면 식은 자연스럽게 확장된다. 3.2 %, 2.1 %, 2.4 %는 √(10.24 + 4.41 + 5.76) ≈ 4.5 %로 합쳐진다.
분석법 하나에 CV 하나를 붙이는 것은 거의 의미가 없다. 위 LOQ 표에서 보듯 CV는 대개 저농도에서 가파르게 커지기 때문이다. 정밀도는 최소한 내부 정도관리 물질의 농도와 임상 절단값 또는 그 근처에서 보고해야 한다. 절단값이 4.0 단위인 바이오마커라면 50 단위에서의 CV 3 %는 오분류율과 무관하다. 참값이 3.8인 환자가 얼마나 자주 절단값 위로 보고되는지를 정하는 것은 4.0 단위에서의 CV이다.
표준의 수치 목표는 출처마다 다르다. 본 명세는 진단용 CV < 10 %, 선별용 < 15 %를 요구한다. README의 검증 요구사항은 정량 바이오마커에 CV < 15 %를 제시한다. 부속서는 런 내 < 15 %(임상), < 20 %(연구)와 런 간 < 20 %를 제시한다. SDK는 MAX_CV_DIAGNOSTIC = 15.0과 MAX_CV_SCREENING = 20.0을 선언한다. SDK 상수는 선언만 되어 있고 v1.0의 어떤 함수도 사용하지 않는다는 점에 유의해야 한다. 정밀도를 검사하는 SDK 호출은 없다. 5.8절에서 이 수치들을 대조한다. 고정된 수치 외에 정밀도 목표를 정하는 확립된 방법으로 생물학적 변동에 근거하는 방식이 있다. 분석 CV가 해당 분석물의 개인 내 생물학적 CV의 절반보다 작으면, 분석법이 환자의 시간에 따른 총 변동에 보태는 몫은 작다.
정밀도는 결과가 올바른 값을 중심으로 모여 있는지에 대해서는 아무것도 말하지 않는다. 첨가 회수 시험은 실제 검체 기질에 알려진 양의 분석물을 넣고 분석법이 그것을 찾아내는지 확인한다.
% 회수율 = (첨가 검체 측정값 − 무첨가 검체 측정값) / 첨가량 × 100 %
예시: 혈청 풀의 측정값이 2.0 단위이고 5.0 단위를 첨가했더니 6.7 단위가 나왔다. 회수율 = (6.7 − 2.0) / 5.0 × 100 = 94 %이다. 첨가 검체가 7.8을 보였다면 회수율은 116 %로, 부속서의 80–120 % 기준은 통과하지만 본 명세의 90–110 % 기준은 벗어난다. 회수율은 여러 첨가 수준(저농도, 절단값 근처, 고농도)과 분석법이 주장하는 모든 검체 종류에서 시험해야 한다. 소변이나 뇌척수액의 기질 효과는 혈청과 크게 다를 수 있기 때문이다. 기질 자체가 희석되어 결과가 왜곡되지 않도록 첨가 부피는 검체 부피에 비해 작아야 한다.
인증 표준물질(CRM)이 있다면 이를 측정하는 것이 보정 소급성을 직접 확인하는 방법이다. 부속서는 인증값의 ±10 % 이내 일치를 목표로 한다. 본 명세는 기준 방법 대비 편향 5 % 미만이라는 요구를 더한다. 편향은 측정 범위를 고루 덮는 환자 검체 세트를 후보 방법과 비교 방법으로 모두 측정한 뒤, 차이 그림(Bland–Altman 방식: 차이 대 평균)과 두 방법 모두의 오차를 허용하는 회귀(Deming 회귀나 Passing–Bablok 회귀 등)로 짝지은 결과를 분석해 추정한다. CLSI EP09가 이 방법 비교 설계를 다룬다. 일반 최소제곱 회귀와 상관계수는 여기에 적합하지 않다. 높은 상관은 두 방법이 함께 움직인다는 것만 보여 줄 뿐, 일치한다는 것을 보여 주지 않는다.
편향은 바이오마커에서 특별한 방식으로 문제가 된다. 발굴 검사실에서 정한 절단값은 다른 검사실의 결과가 같은 척도 위에 있을 때에만 옮겨 쓸 수 있다. 절단값에서 5 %의 양의 편향이 있으면 문턱 근처의 모든 환자가 위로 밀리고, 누구도 생물학을 바꾸지 않았는데 그 사이트에서 관찰되는 민감도와 특이도가 달라진다.
직선성은 분석법의 반응이 농도에 비례하는 범위를 확립하며, 이것이 보고 가능 범위(분석 측정 범위)를 정한다. 부속서는 보통 고농도 풀과 저농도 풀을 알려진 비율로 섞어 만든 5~7개 농도 수준을 2회씩 측정하고 선형 회귀로 적합하도록 규정한다. 허용 기준은 R² > 0.95(임상, 본 명세의 수치이기도 하다) 또는 R² > 0.90(연구)이며, 잔차가 무작위로 분포해야 한다.
R² 하나만으로는 약한 기준이다. 넓은 범위에서는 상단에서 눈에 띄게 휘는 곡선도 R² 0.99를 넘을 수 있다. R²는 농도의 퍼짐에 좌우되기 때문이다. 잔차 패턴이 더 많은 것을 알려 준다. 양 끝에서 음수이고 가운데에서 양수이거나, 한 방향으로 꾸준히 커지는 잔차는 계통적인 곡률을 드러낸다. 실무적으로 보탤 방법은—CLSI EP06에서 쓰는 방식이다—직선 적합을 2차 또는 3차 다항식과 비교하고, 각 수준에서의 직선성 이탈을 백분율로 나타내어 회수 시험과 같은 총 오차 예산에 비추어 판단하는 것이다. 검증된 범위를 넘는 결과는 희석해 재측정하거나(SDK의 qualityFlags.diluted가 이를 기록한다) 상한 초과로 보고해야 하며, 결코 외삽해서는 안 된다.
안정성 시험은 검체가 어떤 조건에서 얼마나 오래 기다려도 대표성 있는 결과를 내는지 알려 준다. 이것이 5.1절 전분석 규칙의 경험적 근거이다. 동결-해동 한계나 2시간 처리 기한은 해당 분석물에 대한 안정성 시험이 뒷받침할 때에만 정당화된다. 부속서는 네 가지 조건을 규정한다.
| 시험 | 조건 | 허용 기준 (부속서) | 답하는 질문 |
|---|---|---|---|
| 동결-해동 | 3회 | 변화 < 20 % | 해동한 분취물을 재검사할 수 있는가? |
| 실온 방치 | 실온 4시간 | 변화 < 15 % | 런 동안 검체가 작업대에서 기다려도 되는가? |
| 처리 검체 | 4 °C 24시간 | 변화 < 15 % | 분리한 혈청·혈장을 다음 날 분석해도 되는가? |
| 장기 보관 | −80 °C 6개월 | 변화 < 20 % | 바이오뱅크 검체를 나중 연구에 쓸 수 있는가? |
변화율은 같은 물질의 기준 측정값에 대해 계산한다.
% 변화 = (스트레스 후 값 − 기준값) / 기준값 × 100 %
예를 들어(예시) 새로 준비했을 때 10.0 단위, 동결-해동 3회 뒤 8.9 단위를 보인 풀은 −11 % 변했으므로 20 % 한계 안에 있다. 세 가지 모범 관행이 적용된다. 첫째, 가능하면 스트레스를 준 분취물과 기준 분취물을 같은 런에서 분석해 런 간 불정밀도가 불안정성처럼 보이지 않게 한다. 그렇지 않다면 허용 한계를 분석법의 런 간 CV에 비추어 해석해야 한다. 둘째, 시험한 조건이 허용하는 조건을 덮어야 한다. 명세는 동결-해동을 3회 미만으로 허용하며 3회 시험이 이 규칙을 뒷받침하지만, 4회까지 허용하려는 검사실은 4회 시험이 필요하다. 셋째, 장기 안정성은 실제로 시험한 기간에 대해서만 주장할 수 있다. 6개월 데이터로는 5년 보관한 검체의 사용을 뒷받침할 수 없는데, 바이오뱅크를 활용하는 후향적 바이오마커 연구에서 흔한 상황이다. 이런 연구에서는 검체별 보관 기간을 기록하고 측정값 및 환자군/대조군 여부와 상관이 있는지 점검해야 한다.
참고구간은 정의된 건강한(또는 달리 정의된) 참조 모집단에서 기대되는 결과의 범위를 기술한다. 모든 WIA 측정 레코드에 따라붙는 referenceRange와 "높음", "낮음" 같은 플래그의 근거가 된다. 명세 §3.3.1은 최소 120명의 참조 개인, 연령·성별·인종의 대표성, 건강 상태 확인, 생활습관 요인 기록을 요구한다. 120이라는 수는 임의로 정한 것이 아니다. 비모수 2.5번째와 97.5번째 백분위수 각각에 90 % 신뢰구간을 붙일 수 있는 최소 표본이며, CLSI EP28-A3c(이전 명칭 C28-A3c)가 기술하는 방식이다. 연령군별·성별 같은 각 분할이 직접 한계를 정한다면 분할마다 120명이 필요하다.
명세는 세 가지 방법을 허용한다.
한계를 계산하기 전에 이상값을 검토해야 한다. 알아채지 못한 준임상 환자 한 명이 비모수 상한을 한 순위만큼 옮길 수 있다. 연령이나 성별로 분할할지 여부도 그 자체로 통계적 결정이다. 흔히 쓰는 검정(Harris–Boyd 기준)은 하위군 평균의 차이를 표준편차의 크기와 비교해 별도 구간이 필요한지 판단한다. 제조사나 다른 검사실이 발표한 구간을 채택할 때는 훨씬 작은 현지 표본으로 이전 가능성을 검증할 수 있다. CLSI EP28-A3c는 현지 참조 개인 20명을 검사해 구간 밖 결과가 2개 이하이면 그 구간을 받아들이는 방식을 기술한다.
산출물은 §3.3.2 참고구간 레코드이며, 분할마다 항목 하나를 둔다. 아래 예는 가상의 분석물과 예시 한계를 사용한다.
{
"biomarker": "BMX-1",
"unit": "ng/mL",
"referenceRanges": [
{ "ageGroup": "18-49", "sex": "female", "lowerLimit": 1.2, "upperLimit": 7.9, "median": 3.4 },
{ "ageGroup": "18-49", "sex": "male", "lowerLimit": 1.5, "upperLimit": 9.1, "median": 4.0 },
{ "ageGroup": "50-79", "sex": "female", "lowerLimit": 1.4, "upperLimit": 9.6, "median": 4.1 },
{ "ageGroup": "50-79", "sex": "male", "lowerLimit": 1.8, "upperLimit": 11.3, "median": 4.9 }
]
}
이 한계를 SDK의 ReferenceRange로 옮기면 lowerLimit와 upperLimit는 min과 max가 되고 단위는 객체 안으로 들어간다(전체 대응은 제3장 참조). 이 레코드에는 참조 개인 수, 사용한 방법, 한계의 신뢰구간, 분할 결정을 담을 자리가 없다. 이런 정보는 검증 문서에 두고 레코드에서 참조해야 한다. 유도 과정이 없는 구간은 판단할 수 없기 때문이다. 120명으로 추정한 상한과 40명으로 추정한 상한은 레코드 안에서 똑같아 보인다.
끝으로, 참고구간은 진단 절단값이 아니다. 건강한 사람의 97.5번째 백분위수는 건강한 값이 어디서 끝나는지를 기술하고, 제2장에서 고른 절단값은 특정 임상 질문에서 민감도와 특이도의 균형이 가장 좋은 곳을 기술한다. 둘이 일치할 수는 있지만, 데이터 세트가 이를 당연하게 가정해서는 안 된다.
검증은 방법이 성능을 낼 수 있음을 보여 주고, 정도관리는 런마다 여전히 그렇다는 것을 보여 준다. 명세 §3.2.2는 배치마다 2~3개 수준의 관리물질을 측정하고, Westgard 규칙으로 승인 여부를 판단하며, 매월 정도관리 데이터를 검토하도록 요구한다. 부속서는 수준을 저농도(LOQ 근처), 중간 농도(범위 중앙), 고농도(상한 근처)로 정한다. 한 수준을 임상 절단값 가까이에 두면 더 유용하다. 드리프트가 임상적 결과로 이어지는 곳이 바로 그 지점이기 때문이다.
각 관리물질에는 목표 평균과 SD가 있으며, 검사실이 안정적으로 운영되는 기간 동안 반복 측정해(예를 들어 20런 이상) 정한다. 새 관리 결과는 z = (관측값 − 목표 평균) / 목표 SD로 z-점수로 바꾸어 Levey–Jennings 도표에 시간순으로 찍는다. 평균에 가로 중심선을, ±1, ±2, ±3 SD에 선을 긋는다. 무작위 오차는 중심에서 멀리 떨어진 고립된 점으로, 계통 오차는 한쪽으로 몰린 연속된 점이나 꾸준한 추세로 나타난다.
z
+3 |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - 거부 (1-3s)
+2 |- - - - - - - - - - - - - - - - - - - - - - - - - - * - - 경고 (1-2s)
+1 |. . . . . . . . . . . . . . . . . .*. . *. . * . . . . .
0 |--*----*------*---*-----*----*--------------------------- 목표 평균
-1 |. . . . .*. . . . . . *. . . . . . . . . . . . . . . . .
-2 |- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-3 |- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
런 1 런 20
(예시: 16~20번 런이 위로 드리프트한다. 1-3s보다 먼저
+1 SD를 넘는 연속 4개 결과가 4-1s를 발동한다)
부속서는 Westgard 등이 도입한 표기법으로 규칙을 나열한다. "n-ks"는 관리 결과 n개가 k 표준편차를 넘는다는 뜻이다. 규칙들은 함께 적용되며, 1-2s는 그 자체로 거부 사유가 아니라 점검을 시작하는 방아쇠 역할을 한다.
| 규칙 | 조건 | 조치 | 주로 검출하는 오차 |
|---|---|---|---|
| 1-2s | 관리 결과 1개가 ±2 SD를 넘음 | 경고: 다른 규칙으로 점검 | — |
| 1-3s | 관리 결과 1개가 ±3 SD를 넘음 | 런 거부 | 무작위(및 큰 계통) 오차 |
| 2-2s | 연속된 관리 결과 2개가 같은 쪽으로 2 SD를 넘음(같은 수준의 연속 런, 또는 한 런의 두 수준) | 런 거부 | 계통 오차 |
| R-4s | 한 런 안에서 두 관리 결과의 차이가 4 SD를 넘음(하나는 +2 SD 위, 다른 하나는 −2 SD 아래) | 런 거부 | 무작위 오차 |
| 4-1s | 연속된 관리 결과 4개가 같은 쪽으로 1 SD를 넘음 | 런 거부 | 계통 오차(작은 이동) |
1-2s가 경고에 그치는 이유는 확률 때문이다. 정규분포를 따르는 관리 상태의 공정에서 관리 결과 하나가 ±2 SD 밖으로 나갈 확률은 약 4.6 %이다. 런마다 관리물질이 2개라면 완벽하게 정상인 런의 약 9 %에서 적어도 하나가 2 SD를 넘고, 3개라면 약 13 %에서 그렇다. 1-2s로 거부하면 유효한 런을 많이 버리게 된다. 반대로 관리 결과 하나가 ±3 SD 밖으로 나갈 확률은 우연으로는 약 0.27 %에 불과하다. 다중 규칙 조합은 거짓 거부를 낮게 유지하면서, 고정된 절단값에 가장 중요한 계통적 이동에는 민감하게 반응한다.
이 규칙들은 자동화하기 쉽다. 아래 코드는 한 런의 관리 수준별 z-점수와 수준별 최근 이력을 받아 런을 평가한다. 최소한의 예시일 뿐이며 @wia/bio-004 SDK의 일부가 아니다. v1.0 SDK에는 정도관리 함수가 없다.
export type WestgardRule = '1-3s' | '2-2s' | 'R-4s' | '4-1s';
export interface RunVerdict {
warning: boolean; // 1-2s 발동
violations: WestgardRule[]; // 하나라도 있으면 런 거부
}
const sameSide = (zs: number[], limit: number): boolean =>
zs.every((z) => z > limit) || zs.every((z) => z < -limit);
/**
* runZ[i] = 현재 런에서 관리 수준 i의 z-점수
* history[i] = 수준 i의 이전 z-점수, 오래된 것부터
*/
export function evaluateRun(runZ: number[], history: number[][]): RunVerdict {
const v = new Set<WestgardRule>();
if (runZ.some((z) => Math.abs(z) > 3)) v.add('1-3s');
// 한 런 안, 수준 간
if (runZ.length >= 2 && Math.max(...runZ) > 2 && Math.min(...runZ) < -2) v.add('R-4s');
for (let i = 0; i < runZ.length; i++) {
for (let j = i + 1; j < runZ.length; j++) {
if (sameSide([runZ[i], runZ[j]], 2)) v.add('2-2s');
}
}
// 런 간, 수준별
runZ.forEach((z, i) => {
const series = [...(history[i] ?? []), z];
if (series.length >= 2 && sameSide(series.slice(-2), 2)) v.add('2-2s');
if (series.length >= 4 && sameSide(series.slice(-4), 1)) v.add('4-1s');
});
return { warning: runZ.some((z) => Math.abs(z) > 2), violations: [...v] };
}
거부된 런은 장비만이 아니라 데이터에도 영향을 준다. 그 런에서 나온 환자 결과는 보류하거나 재측정해야 하며, 이미 보고된 결과는 해당 런까지 추적할 수 있어야 한다. 5.1절의 레코드처럼 각 측정값을 정도관리 런 식별자와 연결해 두는 이유가 여기에 있다. 바이오마커 검증 연구에서는 거부된 런 목록과 그 런에 속한 검체의 처리 결과가 연구 보고서에 실려야 한다.
내부 정도관리는 검사실 자신의 이력에 비해 달라진 것을 검출할 뿐, 처음부터 존재하던 편향은 검출하지 못한다. 외부 정도관리는 같은 물질을 여러 검사실에 보내 결과를 비교함으로써 이 문제를 다룬다. 명세는 숙련도 시험 참여, 동료 검사실과의 비교, 연 1회 방법 검증을 요구한다. 부속서는 빈도를 분기별 또는 연 2회로, 허용 기준을 동료 집단 평균의 ±2 SD 이내 또는 목표값의 ±20 % 이내로 정한다. 새로운 바이오마커에는 상용 숙련도 시험 프로그램이 없는 경우가 많다. 그럴 때는 검사실 간 교차 검사—검체 패널을 둘 이상의 사이트에 나누어 결과를 비교하는 것—가 같은 목적을 수행하며, 여러 사이트의 데이터를 하나의 검증 코호트로 합치기 전에 특히 중요하다.
표준의 수치 한계는 따로 작성된 것이 분명한 네 출처에서 나온다. 본 명세(§3.2.1과 §3.3.1), 품질 지표 부속서, README의 검증 요구사항, SDK의 BIOMARKER_CONSTANTS이다. 아래 표는 이들을 나란히 놓는다. 어느 것도 단독으로 "WIA의" 한계가 아니다. 출처를 밝히지 않고 한계를 인용하는 데서 서로 어긋나는 검증 보고서가 생긴다.
| 항목 | 본 명세 | 부속서 | README / SDK | 이 책의 적용 |
|---|---|---|---|---|
| 정밀도 (CV) | 진단용 < 10 %, 선별용 < 15 % | 런 내 임상 < 15 %, 연구 < 20 %; 런 간 < 20 % | README < 15 %; SDK MAX_CV_DIAGNOSTIC 15, MAX_CV_SCREENING 20 (미사용) | 진단용 ≤ 10 %, 선별용 ≤ 15 %, 연구용 ≤ 20 %, 절단값에서의 총 CV |
| 회수율 | 90–110 % | 80–120 % | — | 진단·선별용 90–110 %; 연구용 및 LOQ 근처 80–120 % |
| 편향 / CRM | 기준 방법 대비 < 5 % | CRM 값의 ±10 % | — | 절단값에서 기준 방법 대비 < 5 %; CRM ±10 % 이내 |
| 직선성 | R² > 0.95 | 임상 R² > 0.95, 연구 > 0.90; 잔차 무작위 | — | R² > 0.95와 잔차·이탈 점검 |
| LOD | 참고구간 하한의 < 10 % | 공시료 평균 + 1.645(또는 3) SD; 95 % 검출 | — | 2단계 LoB/LoD와 20개 중 19개 검증; 명세의 10 % 규칙은 하한 > 0일 때만 |
| LOQ | 참고구간 하한의 < 20 % | CV < 20 %인 최저 수준(임상은 보통 < 15 %) | — | 임상 CV ≤ 15 %, 연구 ≤ 20 %; 임상 절단값보다 아래 |
| 안정성 | 동결-해동 3회 미만(규칙) | 동결-해동 3회 < 20 %; 실온 4시간 < 15 %; 4 °C 24시간 < 15 %; −80 °C 6개월 < 20 % | README: 안정성 문서화 필수 | 부속서의 조건과 한계; 시험한 기간까지만 주장 |
| 참고구간 | ≥ 120명; 비모수, 모수, Box-Cox | — | — | 분할마다 ≥ 120명; 기본은 비모수법 |
| 내부 정도관리 | 배치당 2~3개 수준; Westgard; 월별 검토 | 저·중·고 수준; 1-2s, 1-3s, 2-2s, R-4s, 4-1s | — | 부속서대로, 한 수준은 절단값 근처 |
| 외부 정도관리 | 숙련도 시험; 동료 비교; 연 1회 검증 | 분기별 또는 연 2회; 동료 평균 ±2 SD 또는 목표값 ±20 % | — | 부속서대로; 프로그램이 없으면 검사실 간 교차 검사 |
선택은 하나의 원칙을 따른다. 한계는 결과가 쓰일 용도에 달려 있다. 개인의 진단을 좌우하는 결과는 가장 엄격한 수준(본 명세의 진단용 수치)을 적용한다. 절단값 근처의 환자는 몇 퍼센트의 불정밀도나 편향만으로도 분류가 바뀔 수 있기 때문이다. 양성을 두 번째 방법으로 확인하는 선별 검사는 불정밀도를 조금 더 허용할 수 있다. 임상의에게 보고하는 것이 아니라 후보를 순위 매기는 것이 목적인 발굴용 연구 분석법은 부속서의 연구용 한계를 쓸 수 있다. 다만 발굴에서 임상적 검증으로 넘어가는 바이오마커는 임상 성능을 추정하기 전에 더 엄격한 수치로 재검증해야 한다. 따라서 검증 보고서에는 모든 허용 한계 옆에 그 한계의 출처와 함께 의도한 용도 범주를 적어야 한다.
SDK와 관련해 두 가지를 더 짚어 둔다. SDK의 정밀도 상수는 "diagnostic"(15)과 "screening"(20)이라는 이름을 달고 있지만, 이는 본 명세의 진단/선별 구분이 아니라 부속서의 임상/연구 구분에 해당한다. MAX_CV_DIAGNOSTIC으로 검사하는 프로그램은 10 %가 아니라 15 %를 적용하는 셈이다. 또한 v1.0 SDK의 어떤 함수도 정밀도, 회수율, 직선성, 정도관리 상태를 검사하지 않으므로, validateBiomarker를 통과했다는 것은 분석적 타당성에 대해 아무것도 말해 주지 않는다. 분석적 허용 여부는 따로 확인하고, 그 증거를 임상 결과와 함께 넘겨야 한다.
BIOMARKER_CONSTANTS.MAX_CV_DIAGNOSTIC으로 정밀도를 검사한다. 실제로 적용하는 한계는 무엇이며, 어느 출처에 해당하는가?