CH07정량 분석: 검량선과 시스템 적합성
농도별 표준물로 검량선을 만들어 미지 시료의 농도를 구하고, 시스템 적합성 시험으로 결과를 믿을 수 있는지 확인합니다.
학습 목표#
- 외부표준법(external standard) 의 원리를 설명하고, 카페인 5 · 10 · 25 · 50 · 100 mg/L 의 5점 검량 수준을 설계해 시퀀스 표의 수준(Level) · 농도 칸에 지정할 수 있습니다.
- <b>Calibration › Calibration Table</b> 에서 검량 표를 읽고, 최소제곱 직선의 기울기 · 절편 · 결정계수(R²) 와 수준별 역산 농도 · 잔차(%) 로 검량선이 쓸 만한지 판단할 수 있습니다.
- 미지 시료(시뮬레이션 콜라 · 에너지 음료)의 면적으로 농도를 역산하고 희석 배수를 곱해 원래 음료의 카페인 함량(mg/L)을 구하며, 검량 범위를 벗어난 시료는 희석 후 재분석할 수 있습니다.
- 시스템 적합성 시험(SST)의 반복성(RSD) · 분리도 · 대칭도 · 이론단수 와 S/N · LOD · LOQ · 캐리오버 를 계산해 적합/부적합을 판정할 수 있습니다.
- Python(
numpy.polyfit,statistics)으로 검량선과 SST 판정표를 자동으로 만들고, 조건 · 크로마토그램 · 검량선 · 결과 표 · 판정을 담은 보고서 한 장과 데이터 무결성 원칙에 맞는 실험 노트를 정리할 수 있습니다.
이론#
1교시 — 검량선 작성과 미지 시료 정량#
이 단원에서 할 일#
ch06 에서 카페인 50 mg/L 표준물 하나를 주입해 **면적 약 1640 mAU*s 를 얻었습니다. 그런데 면적만으로는 "그래서 몇 mg/L 인가?" 에 답할 수 없습니다. 면적과 농도를 잇는 자(눈금) 가 필요하고, 그것이 검량선(calibration curve)** 입니다. 이번 단원에서는 카페인 표준물 5개 농도를 만들어 시퀀스 하나로 주입하고, 시뮬레이터의 검량 표에서 직선을 맞춘 뒤, 시판 음료를 흉내 낸 시료 2종의 카페인 함량을 구합니다. 마지막으로 같은 계산을 Python numpy.polyfit 으로 직접 해서 시뮬레이터 결과와 맞춰 봅니다.
3D 장비 모델(assets/robot/robot.json)은 강좌 진행에 맞춰 추가됩니다. 차시 머리에 3D 뷰어 버튼이 보이지 않으면 아직 준비 중입니다. 이 차시는 대부분 데이터 분석 화면과 Playground 에서 진행합니다. 모델이 준비되면 트레이 위 바이알 10개의 배치와, 시퀀스가 도는 동안 니들이 바이알 → 세척 바이알 → 주입구를 오가는 순서를 3D 로 확인해 봅니다.
외부표준법의 원리#
외부표준법(external standard method) 은 농도를 아는 표준물을 시료와 따로(외부에서) 주입해 "농도 → 면적" 관계를 먼저 만들고, 시료의 면적을 그 관계에 거꾸로 넣어 농도를 구하는 방법입니다.
- 표준물 여러 농도(x)를 주입해 면적(y)을 얻습니다.
- 점들에 직선 y = a · x + b 를 맞춥니다 (a = 기울기, b = 절편).
- 시료 면적 y_시료 를 넣어 x = (y_시료 − b) / a 로 바이알 속 농도를 구합니다.
- 시료를 묽혀 넣었다면 희석 배수(dilution factor) 를 곱해 원래 시료의 농도로 되돌립니다.
이 방법이 맞으려면 두 가지가 지켜져야 합니다.
- 표준물과 시료가 똑같이 처리되어야 합니다. 같은 메서드 · 같은 주입량 · 같은 적분 조건(ch06 의 Apply to All)이어야 면적을 서로 비교할 수 있습니다.
- 주입량이 매번 같아야 합니다. 외부표준법은 "10 µL 를 넣었다" 를 그대로 믿기 때문에, 주입 재현성이 결과 정확도를 좌우합니다. 그래서 2교시에서 반복 주입 RSD 를 먼저 확인합니다.
주입량 · 전처리 손실이 크게 흔들리는 시료(추출 · 농축을 거치는 시료 등)에는 시료마다 다른 물질(내부표준) 을 같은 양 넣고 "분석물 면적 ÷ 내부표준 면적" 으로 검량하는 내부표준법을 씁니다. 이번 음료 시료는 묽혀서 바로 넣으므로 외부표준법으로 충분합니다.
검량 수준(level) 설계 — 5점#
| 수준 (Level) | 시료 이름 | 카페인 농도 (mg/L) | 원액 1012 mg/L 에서 덜어 낼 양 (10 mL 플라스크) |
|---|---|---|---|
| 1 | STD-CAF-L1 | 5 | 0.049 mL → 중간 희석(STD-CAF-100 에서 0.500 mL) |
| 2 | STD-CAF-L2 | 10 | 0.099 mL → 중간 희석(STD-CAF-100 에서 1.000 mL) |
| 3 | STD-CAF-L3 | 25 | 0.247 mL |
| 4 | STD-CAF-L4 | 50 | 0.494 mL (ch06 의 STD-CAF-50 과 같음) |
| 5 | STD-CAF-L5 | 100 | 0.988 mL |
수준을 정할 때의 원칙은 다음과 같습니다.
- 시료 농도를 가운데에 두도록 범위를 잡습니다. 시판 콜라의 카페인은 대략 100 mg/L 안팎, 에너지 음료는 300 mg/L 안팎으로 알려져 있어(제품마다 다름 — 라벨 확인), 둘 다 묽혀서 30 ~ 50 mg/L 로 맞추면 5 ~ 100 mg/L 범위 한가운데에 들어옵니다.
- 점은 최소 5개로 하고, 범위 전체에 고르게 퍼뜨립니다. 한쪽 끝에 점이 몰리면 직선이 그 점들에 끌려갑니다.
- 0.1 mL 보다 작은 부피를 덜어야 하는 수준(L1 · L2)은 ch06 실습 A 처럼 중간 희석을 거칩니다. 여기서는 L5(100 mg/L)를 중간 원액으로 써서 L1 · L2 를 만듭니다.
- 검량 범위는 가장 낮은 표준물 ~ 가장 높은 표준물 입니다. 이 범위 밖에서는 직선이 계속 맞는다는 증거가 없으므로 결과를 그대로 보고하지 않습니다(아래 "검량 범위 밖 시료").
- 실물 실습에서는 보안경 · 장갑 · 실험복을 착용하고, 메탄올이 든 이동상은 후드 안에서 다룹니다.
- 탄산음료는 탈기(degassing) 할 때 거품이 넘칠 수 있으므로 비커 부피의 1/3 이하만 담고 초음파 세척기에 넣습니다.
- 시린지 필터로 거를 때 필터를 시린지에 단단히 끼우고, 바이알 쪽을 사람에게 향하지 않게 합니다. 힘을 너무 주면 필터가 빠져 용액이 튑니다.
- 음료 시료라도 실험실에 들어온 것은 마시지 않습니다. 실험대 위 음식물 금지 규칙은 똑같이 적용됩니다.
시료 준비 — 시뮬레이션 음료 2종#
시뮬레이터의 시약 목록에는 시판 음료를 흉내 낸 시료 두 개가 들어 있습니다. 실제 제품의 값이 아니라 실습용으로 만든 가상 시료입니다.
| 시료 | 시뮬레이터 이름 | 전처리 | 희석 | 바이알 이름 |
|---|---|---|---|---|
| 콜라 | DRINK-COLA | 초음파 탈기 10 min → 0.45 µm 시린지 필터 | 5.00 mL → 10 mL (× 2) | SMP-COLA-D2 |
| 에너지 음료 | DRINK-ENERGY | 초음파 탈기 10 min → 0.45 µm 시린지 필터 | 5.00 mL → 10 mL (× 2) | SMP-ENERGY-D2 |
- 탈기: 탄산(CO₂)이 남아 있으면 주입할 때 기포가 함께 빨려 들어가 주입량이 흔들립니다(ch05 의 공기 흡입 경고).
- 여과: 당 · 색소 · 미세 입자가 컬럼 입구 프릿을 막아 압력이 오르는 것을 막습니다(ch03 압력 상승).
- 희석 용매는 이동상(물:메탄올 70:30)으로 합니다(ch06 오류 5번 — 강한 시료 용매).
- 에너지 음료도 처음에는 콜라와 같이 × 2 로만 묽힙니다. 일부러 검량 범위를 넘게 해서, 범위 밖 시료를 처리하는 과정을 연습하기 위해서입니다.
시퀀스에 검량 수준 지정하기#
ch05 에서 미뤄 두었던 시퀀스 표의 수준(Level) · 농도(Amount) · 희석 배수(Dilution) 칸을 이번에 씁니다. 종류(SampleType)를 Standard 로 고르면 수준 · 농도 칸이, Sample 로 고르면 희석 배수 칸이 열립니다.
| 줄 | 위치 | 시료 이름 | 종류 | 수준 | 농도 (mg/L) | 희석 배수 | 주입 |
|---|---|---|---|---|---|---|---|
| 1 | 1 | BLANK-1 | Blank | — | — | — | 1 |
| 2 | 2 | STD-CAF-L1 | Standard | 1 | 5 | — | 1 |
| 3 | 3 | STD-CAF-L2 | Standard | 2 | 10 | — | 1 |
| 4 | 4 | STD-CAF-L3 | Standard | 3 | 25 | — | 1 |
| 5 | 5 | STD-CAF-L4 | Standard | 4 | 50 | — | 1 |
| 6 | 6 | STD-CAF-L5 | Standard | 5 | 100 | — | 1 |
| 7 | 1 | BLANK-2 | Blank | — | — | — | 1 |
| 8 | 8 | SMP-COLA-D2 | Sample | — | — | 2 | 2 |
| 9 | 9 | SMP-ENERGY-D2 | Sample | — | — | 2 | 2 |
| 10 | 5 | CHK-CAF-50 | Sample | — | — | 1 | 1 |
- 표준물은 낮은 농도 → 높은 농도 순으로 주입합니다. 높은 농도 다음에 낮은 농도를 넣으면 잔류(캐리오버)가 낮은 농도 면적을 부풀립니다.
- 7번 줄
BLANK-2는 가장 진한 표준물 바로 뒤에 넣어 캐리오버를 확인하는 블랭크입니다(2교시). - 10번 줄
CHK-CAF-50은 L4 바이알을 시료처럼 한 번 더 넣은 확인 표준(check standard)입니다. 시퀀스 끝에서 50 mg/L 이 그대로 나오면 시퀀스 동안 장비가 흔들리지 않았다는 증거가 됩니다(ch05 의 브래킷). - 위치 7 은 세척 바이알(ch05)이므로 시료 바이알은 8번부터 놓습니다.
따라하기 — 검량 표준물 · 시료 준비와 시퀀스 실행#
- <b>편집 › 표준용액 조제</b>를 고르고, ch06 에서 만든 원액
STOCK-CAF(1012 mg/L)가 목록에 있는지 확인합니다. 없으면 ch06 따라하기 1 ~ 2단계로 다시 만듭니다. - 희석 탭에서 원액
STOCK-CAF, 농도100mg/L, 플라스크10 mL, 용매이동상 A70/B30→ 계산(0.988 mL) → 만들기 하고 이름을STD-CAF-L5로 바꿉니다. 같은 방법으로STD-CAF-L4(50) ·STD-CAF-L3(25)를 만듭니다. - 희석 탭의 원액을
STD-CAF-L5로 바꾸고10mg/L(1.000 mL) →STD-CAF-L2,5mg/L(0.500 mL) →STD-CAF-L1을 만듭니다. 만들 때마다 창 아래 실제 농도 칸 값과 덜어 낸 부피를 실험 노트에 그대로 옮깁니다. - 같은 창의 시료 탭에서
DRINK-COLA를 고르고 탈기(10 min) → 여과(0.45 µm) → 희석5.00 mL → 10 mL, 용매이동상 A70/B30→ 만들기 → 이름SMP-COLA-D2.DRINK-ENERGY도 똑같이 해서SMP-ENERGY-D2를 만듭니다. - <b>편집 › 바이알 준비</b>(ch04)에서 위치 1
BLANK, 2 ~ 6STD-CAF-L1~L5, 8SMP-COLA-D2, 9SMP-ENERGY-D2를 놓습니다. 위치 1 · 5 는 두 번 쓰므로 액량을 1.5 mL 로 채웁니다. - <b>Sequence › New Sequence</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>N</kbd>)로
SEQ_QUANT_CAF를 만들고, 운영자 칸에 학번을 넣습니다. 위 표대로 10줄을 채웁니다. 메서드 칸은 모두PRAC_ISO_W70M30_v01이며, 위 칸을 고르고 아래로 끌어 <kbd>Ctrl</kbd>+<kbd>D</kbd> 로 채웁니다. - 종류 칸에서
Standard를 고른 줄은 수준 · 농도 칸이 노란색으로 바뀝니다. 2 ~ 6번 줄에 수준1~5, 농도5·10·25·50·100을 넣습니다. 8 · 9번 줄의 희석 배수 칸에2, 10번 줄에1을 넣습니다. - C 영역 트레이 대조가 모두
●이고 D 영역 예상 시간(12회 × 약 11 min ≈ 132 min)과 용매 · 폐액 여유를 확인한 뒤 <b>Sequence › Save As</b> 로 저장합니다. - ch06 시스템 준비 체크리스트(퍼지 → 평형 → Balance)를 마치고 <b>Run › Run Sequence</b>(<kbd>F6</kbd>)로 시작합니다. 기다리기 길면 <b>보기 › 시간 배속</b>(<kbd>Ctrl</kbd>+<kbd>]</kbd>)을 씁니다.
ch05 에서 했던 것처럼 시퀀스 CSV 를 Python 으로 만들고 <b>Sequence › Import CSV</b>(<kbd>Ctrl</kbd>+<kbd>I</kbd>)로 불러오면 오타가 줄어듭니다. 열 이름은 Vial,SampleName,SampleType,Level,Amount,Dilution,Injections,Method 입니다.
검량 표 화면#
시퀀스가 끝나면 <b>모드 › 데이터 분석</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd>)에서 SEQ_QUANT_CAF 폴더를 열고 검량 표를 만듭니다.
| 영역 | 하는 일 |
|---|---|
| A 화합물 목록 | 검량할 화합물 이름 · 기준 RT · RT 창(이 시간 안에 든 피크를 그 화합물로 인식) |
| B 수준 표 | 수준마다 농도 · 면적 · 감응 계수(면적 ÷ 농도) · 역산 농도 · 잔차(%) |
| C 검량선 그래프 | 점과 맞춘 직선. 점을 클릭하면 B 표의 그 줄이 선택됨 |
| D 맞춤 설정 | 모델(직선 · 2차), 원점 처리(포함 · 무시 · 강제), 가중치(없음 · 1/x · 1/x²) |
| E 결과 요약 | 식 y = a·x + b, 결정계수 R², 점 개수, 경고 |
따라하기 — 검량 표 만들기#
- A 영역 데이터 목록에서
SEQ_QUANT_CAF폴더를 펼치고,STD-CAF-L4파일을 더블클릭합니다. ch06 처리 조건(적분 끄기 0 ~ 2 min 등)을 <b>Integration › Apply to All</b> 로 폴더 전체에 적용하고 <kbd>F8</kbd> 로 적분합니다. - <b>Calibration › New Calibration Table</b>을 고릅니다. 카페인 피크(5.16 min)를 클릭하면 A 영역에
Caffeine · RT 5.161 · 창 ± 5 %가 들어옵니다. - <b>Calibration › Process Sequence</b>를 누르면 시퀀스 표의 수준 · 농도를 읽어
Standard파일 5개의 카페인 면적이 B 표에 채워집니다. - <b>Calibration › Calibration Table</b>(<kbd>Ctrl</kbd>+<kbd>K</kbd>)로 검량 표를 엽니다. D 영역을 **모델
직선· 원점포함 안 함(무시)· 가중치없음** 으로 둡니다. - E 영역의 식과 R² 를 기록합니다. 그다음 B 표의 역산 농도 · 잔차(%) 칸을 위에서부터 훑어, 크게 벗어난 수준이 없는지 봅니다.
- 시료 결과를 보려면 데이터 목록에서
SMP-COLA-D2-01을 엽니다. D 적분 결과 표에 Amount (바이알 농도) 와 Final (× 희석 배수) 칸이 새로 생깁니다. - <b>Calibration › Quant Results</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>K</kbd>)로 폴더 전체의 정량 결과를 한 표로 보고, <b>파일 › 결과 내보내기 › CSV</b> 로
SEQ_QUANT_CAF_results.csv를 저장합니다(실습 A · B).
검량 표 읽기#
이번 시뮬레이터 결과 예시입니다(면적 단위 mAU*s).
| 수준 | 농도 (mg/L) | 면적 | 감응 계수 (면적/농도) | 역산 농도 (mg/L) | 잔차 (%) |
|---|---|---|---|---|---|
| 1 | 5 | 168.9 | 33.78 | 5.00 | −0.03 |
| 2 | 10 | 324.1 | 32.41 | 9.76 | −2.40 |
| 3 | 25 | 826.3 | 33.05 | 25.17 | +0.67 |
| 4 | 50 | 1641.8 | 32.84 | 50.18 | +0.37 |
| 5 | 100 | 3262.0 | 32.62 | 99.89 | −0.11 |
E 영역: y = 32.596 · x + 5.964, R² = 0.99998, 점 5개.
| 값 | 뜻 | 이번 값의 해석 |
|---|---|---|
| 기울기 a | 농도 1 mg/L 가 늘 때 면적이 늘어나는 양 = 감도(sensitivity) | 1 mg/L 당 약 32.6 mAU*s |
| 절편 b | 농도 0 일 때의 면적. 이상적으로는 0 | 5.96 — 가장 낮은 표준물 면적(168.9)의 약 3.5 % 로 작음 |
| 결정계수 R² | 면적의 흩어짐 중 직선이 설명하는 비율. 1 에 가까울수록 점이 직선에 붙음 | 0.99998 — 매우 좋음 |
| 역산 농도 | 그 표준물의 면적을 검량식에 거꾸로 넣은 농도 | 넣은 농도와 비교해 차이를 봄 |
| 잔차 (%) | (역산 농도 − 넣은 농도) ÷ 넣은 농도 × 100 | 모든 수준이 ±15 % (이 강좌 예시 기준) 안 |
- 최소제곱 직선은 잔차 제곱의 합이 가장 작아지도록 a · b 를 고릅니다. 식은 a = Σ(x − x̄)(y − ȳ) / Σ(x − x̄)², b = ȳ − a · x̄ 입니다 (x̄, ȳ 는 평균).
- R² = 1 − Σ(y − ŷ)² / Σ(y − ȳ)² 입니다(ŷ 는 직선 위의 값).
R² 는 높은 농도 점에 끌려 다닙니다. 100 mg/L 의 면적은 3000 이 넘고 5 mg/L 은 170 정도라, 낮은 농도에서 10 % 가 틀려도 R² 는 0.999 를 쉽게 넘습니다. 그래서 반드시 수준별 잔차(%) 를 함께 봅니다. 이번 L2 의 −2.4 % 는 기준 안이지만 다른 수준보다 크므로, 같은 일이 반복되면 L2 조제(중간 희석 피펫)를 의심해 봅니다. R² · 잔차 기준값은 실험실 SOP 와 밸리데이션 문서가 정하며, 위 ±15 % 는 이 강좌의 실습용 예시입니다.
미지 시료 농도 역산과 희석 배수#
검량식으로 구한 값은 바이알 속 농도입니다. 원래 음료의 농도는 희석 배수를 곱해야 합니다.
농도(원 시료) = (면적 − b) ÷ a × 희석 배수
| 시료 | 주입 | 면적 | 바이알 농도 (mg/L) | 희석 배수 | 원 시료 (mg/L) | 판정 |
|---|---|---|---|---|---|---|
| SMP-COLA-D2 | -01 | 1582.4 | 48.36 | 2 | 96.7 | 범위 안 |
| SMP-COLA-D2 | -02 | 1592.0 | 48.66 | 2 | 97.3 | 범위 안 |
| SMP-ENERGY-D2 | -01 | 5226.8 | 160.2 | 2 | (320.3) | **범위 초과 >Cal** |
| SMP-ENERGY-D2 | -02 | 5231.5 | 160.3 | 2 | (320.6) | **범위 초과 >Cal** |
| CHK-CAF-50 | -01 | 1645.2 | 50.29 | 1 | 50.3 | 확인 표준 +0.6 % |
- 콜라는 2회 평균 97.0 mg/L 입니다. 두 주입의 차이는 0.6 % 로 작습니다.
- 에너지 음료의 면적 5226.8 은 가장 높은 표준물(3262.0)보다 큽니다. 시뮬레이터는 Amount 칸에
>Cal표시를 붙이고 로그에 경고를 남깁니다. 괄호 안의 값은 직선을 범위 밖으로 늘려서(외삽) 얻은 값이라 보고하지 않습니다. - 확인 표준
CHK-CAF-50이 50.3 mg/L 로 나와 시퀀스 처음과 끝 사이에 감도가 거의 변하지 않았음을 보여 줍니다.
검량 범위 밖 시료의 처리 — 희석 후 재분석#
범위를 넘은 시료는 숫자를 고치는 것이 아니라 시료를 다시 준비합니다.
- 목표 바이알 농도를 범위 가운데(약 30 ~ 50 mg/L)로 잡고 필요한 희석 배수를 정합니다. 에너지 음료는 대략 320 mg/L 로 짐작되므로 × 10 ~ × 20 이 알맞습니다.
- <b>편집 › 표준용액 조제</b> 시료 탭에서 탈기 · 여과한
DRINK-ENERGY1.00 mL → 10 mL(× 10)로 묽혀SMP-ENERGY-D10을 만듭니다. × 2 바이알을 다시 × 5 로 묽혀도 같은 배수가 되지만, 희석 단계가 늘수록 피펫 오차가 쌓이므로 원 시료에서 한 번에 묽히는 편이 낫습니다. - <b>편집 › 바이알 준비</b>에서 위치 10 에 놓고, 시퀀스
SEQ_QUANT_CAF를 <b>Sequence › Save As</b> 로SEQ_QUANT_CAF_RE로 복사합니다. 블랭크 1줄,SMP-ENERGY-D10(희석 배수10, 주입 2),CHK-CAF-501줄만 남기고 <kbd>F6</kbd> 로 실행합니다. - 같은 날 · 같은 메서드 · 같은 처리 조건이므로 기존 검량 표를 그대로 씁니다. 결과는 면적 1047.6 · 1051.3 → 바이알 31.96 · 32.07 mg/L → × 10 → 319.6 · 320.7 mg/L, 평균 320.1 mg/L 입니다.
- 실험 노트에는 × 2 결과(
>Cal)를 지우지 않고 "범위 초과 → × 10 재분석" 이라고 이유와 함께 남깁니다.
재분석 결과 320.1 mg/L 는 × 2 에서 얻은 외삽값(약 320 mg/L)과 비슷합니다. 이 시뮬레이터는 높은 농도에서도 직선으로 반응하도록 만들어져 있기 때문입니다. 실제 검출기는 높은 흡광도에서 반응이 휘어질 수 있어 외삽값이 크게 틀릴 수 있으므로, 비슷해 보여도 범위 안에서 다시 잰 값만 보고합니다.
희석 배수는 머릿속 계획이 아니라 실제로 쓴 피펫 · 플라스크 부피로 정해집니다. 시퀀스 표에 넣기 전에 실험 노트의 조제 기록과 한 번 더 대조합니다. 시퀀스 표의 희석 배수를 나중에 고치는 것은 처리(계산) 조건의 수정이므로 허용되지만, 고친 이유와 시각이 기록(감사 추적)에 남아야 합니다(2교시 데이터 무결성).
- 표준물을 높은 농도부터 주입합니다 → 캐리오버가 낮은 수준 면적을 부풀려 검량선 아래쪽이 휩니다.
- 수준 번호와 농도를 엇갈리게 넣습니다(예: L2 줄에 25) → 한 점이 직선에서 크게 벗어나고 잔차가 수십 % 가 됩니다(오류 1번).
- 희석 배수를 시퀀스 표에도 넣고 Python 에서도 또 곱합니다 → 결과가 두 배로 나옵니다(오류 4번).
- 범위 초과(
>Cal) 표시를 무시하고 외삽값을 보고합니다 → 범위 밖에서는 직선이 맞는다는 보장이 없습니다. 반드시 다시 묽혀 재분석합니다. - 검량 표를 만든 뒤 적분 파라미터를 표준물 파일만 바꿉니다 → 표준물과 시료의 처리 조건이 달라집니다. 파라미터를 바꿨다면 Apply to All 후 <b>Calibration › Process Sequence</b> 를 다시 합니다.
단축키 · 메뉴 정리 (1교시)#
| 동작 | 메뉴 | 단축키 |
|---|---|---|
| 표준용액 · 시료 조제 | <b>편집 › 표준용액 조제</b> (희석 · 시료 탭) | — |
| 새 시퀀스 | <b>Sequence › New Sequence</b> | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>N</kbd> |
| 시퀀스 CSV 불러오기 | <b>Sequence › Import CSV</b> | <kbd>Ctrl</kbd>+<kbd>I</kbd> |
| 아래로 채우기 | 칸 여러 개 선택 후 | <kbd>Ctrl</kbd>+<kbd>D</kbd> |
| 시퀀스 실행 | <b>Run › Run Sequence</b> | <kbd>F6</kbd> |
| 데이터 분석 화면 | <b>모드 › 데이터 분석</b> | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>D</kbd> |
| 새 검량 표 | <b>Calibration › New Calibration Table</b> | — |
| 시퀀스의 표준물로 검량 표 채우기 | <b>Calibration › Process Sequence</b> | — |
| 검량 표 열기 | <b>Calibration › Calibration Table</b> | <kbd>Ctrl</kbd>+<kbd>K</kbd> |
| 폴더 전체 정량 결과 | <b>Calibration › Quant Results</b> | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>K</kbd> |
| 결과 표 저장 | <b>파일 › 결과 내보내기 › CSV</b> | — |
단축키는 시뮬레이터 버전에 따라 바뀔 수 있습니다. <b>도움말 › 단축키 목록</b>(<kbd>?</kbd>)에서 최신 목록을 확인합니다.
결과 확인#
- 검량 표 E 영역이 y ≈ 32.6x + 6.0, R² ≥ 0.9999 이고 B 표의 잔차가 모두 ±15 % 안입니다.
SMP-COLA-D2의 Final 이 약 97 mg/L, 재분석한 에너지 음료(희석 배수 10)가 약 320 mg/L 입니다.CHK-CAF-50이 50 mg/L 의 ±2 % 안(이 강좌 예시 기준)으로 나옵니다.
Python 으로 검량선 맞추기 — numpy.polyfit#
numpy.polyfit(x, y, 1) 은 점 (x, y) 에 1차식(직선)을 최소제곱으로 맞추고 [기울기, 절편] 순서로 돌려줍니다. 시뮬레이터 E 영역과 같은 값이 나오는지 확인해 봅니다.
import numpy as np
conc = np.array([5, 10, 25, 50, 100], dtype=float) # x: 농도 (mg/L)
area = np.array([168.9, 324.1, 826.3, 1641.8, 3262.0]) # y: 면적 (mAU*s)
a, b = np.polyfit(conc, area, 1) # [기울기, 절편] 순서
pred = a * conc + b
r2 = 1 - np.sum((area - pred) ** 2) / np.sum((area - area.mean()) ** 2)
print(f"y = {a:.3f} x + {b:.3f}, R2 = {r2:.5f}")
# 공식으로 직접 계산해도 같음
xm, ym = conc.mean(), area.mean()
a2 = np.sum((conc - xm) * (area - ym)) / np.sum((conc - xm) ** 2)
b2 = ym - a2 * xm
print(f"직접 계산: a = {a2:.3f}, b = {b2:.3f}")
back = (area - b) / a
for c, bc in zip(conc, back):
print(f" {c:6.1f} mg/L -> 역산 {bc:7.2f} 잔차 {(bc - c) / c * 100:+6.2f} %")
cola = (np.mean([1582.4, 1592.0]) - b) / a * 2
print(f"콜라: {cola:.1f} mg/L")- 출력이
y = 32.596 x + 5.964, R2 = 0.99998이면 시뮬레이터와 같습니다. np.polyfit(area, conc, 1)처럼 x · y 를 바꿔 넣어도 오류 없이 다른 숫자가 나옵니다(오류 7번). 검량선은 항상 x = 농도, y = 면적 으로 맞춥니다.
2교시 — 시스템 적합성과 결과 보고#
이 단원에서 할 일#
1교시의 콜라 97.0 mg/L 는 "장비가 그날 제대로 동작했다" 는 전제 위에 서 있습니다. 주입량이 흔들렸거나 컬럼이 망가져 피크가 겹쳤다면 숫자는 그럴듯해도 믿을 수 없습니다. 시스템 적합성 시험(System Suitability Test, SST) 은 시료를 분석하기 전에, 그날 장비 · 컬럼 · 메서드가 정해 둔 성능을 내는지 확인하는 시험입니다. 이번 단원에서는 SST 용액을 6회 반복 주입해 판정표를 만들고, S/N · LOD · LOQ · 캐리오버를 확인한 뒤, 결과 보고서 한 장으로 정리합니다.
SST 항목과 기준#
SST 용액 SST-MIX 는 테오필린 20 mg/L + 카페인 50 mg/L 를 이동상에 녹인 것입니다(ch06 의 MIX-XAN 원액으로 조제). 테오필린은 카페인 바로 앞에 나오는 가장 가까운 이웃 피크라 분리도를 보기에 알맞습니다.
| 항목 | 계산 (ch06 식) | 무엇을 확인하나 | 이 강좌 기준 (예시) |
|---|---|---|---|
| RT 반복성 | 6회 RT 의 RSD | 펌프 유속 · 조성 · 온도 안정 | ≤ 1.0 % |
| 면적 반복성 | 6회 면적의 RSD | 주입량 재현성 (외부표준법의 전제) | ≤ 2.0 % |
| 분리도 Rs (테오필린–카페인) | 1.18 (tR₂ − tR₁) / (w½₁ + w½₂) | 이웃 피크와 섞이지 않음 | ≥ 2.0 |
| 대칭도 T (카페인) | W0.05 / (2f) | 피크 꼬리 · 앞끌림 (컬럼 · 시료 용매 상태) | ≤ 2.0 |
| 이론단수 N (카페인) | 5.54 (tR / w½)² | 컬럼 효율 | ≥ 2000 |
- 기준값은 공정서(USP · 대한민국약전 등)나 그 방법의 밸리데이션 문서가 정합니다. 위 표는 이 강좌 실습용 예시이며, 실제 시험에서는 해당 문서의 값을 씁니다(참고자료의 USP 〈621〉).
- Rs · T · N 은 6회 중 가장 나쁜 값으로 판정합니다(이 강좌 규칙). 평균으로 판정하면 한 번 나빴던 주입이 가려질 수 있기 때문입니다.
- 한 항목이라도 기준을 넘으면 SST 부적합이고, 원인을 고치고 SST 를 다시 통과할 때까지 시료 결과를 보고하지 않습니다.
따라하기 — SST 6회 주입과 판정#
- <b>편집 › 표준용액 조제</b> 혼합 희석 탭에서
STOCK-TPH20 mg/L +STOCK-CAF50 mg/L, 10 mL, 용매이동상 A70/B30→SST-MIX를 만들고 위치 11 에 놓습니다. - 시퀀스
SEQ_SST_CAF를 만듭니다: 1번 줄BLANK-1(위치 1, 1회), 2번 줄SST-MIX(위치 11, 종류Standard, 수준 칸 비움, 주입 6회). 수준을 비워 두면 검량 표에 들어가지 않습니다. - 실제 분석에서는 SST 시퀀스를 1교시 정량 시퀀스보다 먼저 돌립니다. <kbd>F6</kbd> 로 실행합니다.
- 데이터 분석 화면에서
SEQ_SST_CAF폴더에 처리 조건을 Apply to All 하고 <kbd>F8</kbd> 로 적분합니다. - <b>Report › System Suitability</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Y</kbd>)를 엽니다. 기준 설정 탭에서 위 표의 기준 다섯 개와 "가장 나쁜 값으로 판정" 을 넣고, 기준 피크
Caffeine, 이웃 피크Theophylline을 고릅니다. - 결과 탭에 6회 주입의 RT · 면적 · Rs · T · N 과 평균 · RSD, 항목별
PASS/FAIL이 나옵니다. 아래 예시와 비교합니다. - S/N 탭에서 노이즈 구간을
4.00 ~ 4.60 min(피크가 없는 평평한 구간)으로 정하고STD-CAF-L1의 S/N 을 봅니다. - <b>파일 › 결과 내보내기 › CSV</b> 로 SST 결과를
SEQ_SST_CAF_sst.csv로 저장합니다(실습 C).
| 주입 | 카페인 RT (min) | 카페인 면적 | Rs (TPH–CAF) | T | N |
|---|---|---|---|---|---|
| 1 | 5.161 | 1643.5 | 3.21 | 1.12 | 5690 |
| 2 | 5.158 | 1650.2 | 3.18 | 1.14 | 5650 |
| 3 | 5.164 | 1638.7 | 3.24 | 1.11 | 5720 |
| 4 | 5.160 | 1646.9 | 3.19 | 1.13 | 5610 |
| 5 | 5.166 | 1655.1 | 3.22 | 1.12 | 5680 |
| 6 | 5.159 | 1641.0 | 3.20 | 1.15 | 5700 |
| 판정 | RSD 0.06 % → PASS | RSD 0.37 % → PASS | 최소 3.18 → PASS | 최대 1.15 → PASS | 최소 5610 → PASS |
검출 한계(LOD) · 정량 한계(LOQ)와 S/N#
- 검출 한계(LOD, limit of detection): 노이즈와 구별해 "있다" 고 말할 수 있는 가장 낮은 농도입니다. 흔히 S/N ≈ 3 인 농도로 잡습니다.
- 정량 한계(LOQ, limit of quantitation): 정해진 정밀도 · 정확도로 "얼마" 라고 말할 수 있는 가장 낮은 농도입니다. 흔히 S/N ≈ 10 인 농도로 잡습니다.
- 이 시뮬레이터의 S/N 은 USP · EP 와 같은 방식인 S/N = 2H / h 로 계산합니다. H 는 피크 높이, h 는 노이즈 구간의 최댓값 − 최솟값(peak-to-peak) 입니다. 소프트웨어마다 노이즈 정의(peak-to-peak, RMS 등)가 달라 같은 데이터라도 S/N 이 다르게 나오므로, 보고서에 계산 방식을 적습니다.
| 값 | 이번 결과 |
|---|---|
| 노이즈 h (4.00 ~ 4.60 min, peak-to-peak) | 0.060 mAU |
| STD-CAF-L1 (5 mg/L) 높이 H | 16.4 mAU |
| S/N = 2H / h | 약 547 |
| LOD (S/N = 3) ≈ 5 × 3 / 547 | 약 0.03 mg/L |
| LOQ (S/N = 10) ≈ 5 × 10 / 547 | 약 0.09 mg/L |
- 위처럼 S/N 이 농도에 비례한다고 보고 짐작한 값은 반드시 그 농도 근처 표준물을 실제로 주입해 확인해야 합니다.
- 검량선의 잔차로 구하는 방법도 있습니다(ICH Q2: LOD = 3.3 σ / a, LOQ = 10 σ / a, σ 는 잔차 표준편차, a 는 기울기). 이번 데이터로는 LOD 약 0.69 · LOQ 약 2.1 mg/L 로, S/N 방법보다 훨씬 큽니다. 넓은 범위(5 ~ 100 mg/L)의 흩어짐이 σ 에 들어가기 때문으로, 어떤 방법으로 구했는지에 따라 값이 크게 다르다는 점을 기억합니다(실습 D).
- LOQ 가 낮게 나와도 보고할 수 있는 가장 낮은 농도는 가장 낮은 표준물(5 mg/L) 입니다. 그보다 낮은 결과는
< 5 mg/L (검량 범위 미만)으로 씁니다.
블랭크 · 캐리오버 확인#
캐리오버(carryover) 는 앞 주입의 시료가 니들 · 주입 밸브 · 루프에 남아 다음 주입에 섞여 나오는 현상입니다(ch06 오류 1번). 1교시 시퀀스 7번 줄 BLANK-2 가 바로 이것을 보는 줄입니다.
- 데이터 분석 화면에서
BLANK-1·STD-CAF-L5·BLANK-2를 <kbd>Ctrl</kbd>+클릭으로 고르고 <kbd>O</kbd> 로 겹칩니다. - 5.0 ~ 5.4 min · 0 ~ 1 mAU 를 끌어 확대합니다.
BLANK-1에는 없던 작은 피크가BLANK-2의 5.16 min 에 보입니다. BLANK-2를 <kbd>F8</kbd> 로 적분하면 면적 **1.9 mAU*s** 가 나옵니다.
캐리오버(%) = 블랭크의 분석물 면적 ÷ 바로 앞 표준물 면적 × 100 = 1.9 ÷ 3262.0 × 100 ≈ 0.058 %
- 이 강좌 예시 기준(≤ 0.1 %) 안이라 적합입니다.
- 하지만 같은 1.9 를 가장 낮은 표준물(168.9)과 비교하면 1.1 % 입니다. 진한 시료 바로 뒤에 묽은 시료가 오면 그만큼 부풀려질 수 있으므로, 진한 시료 다음에는 블랭크 한 줄을 두는 시퀀스 설계(ch05)가 여전히 중요합니다.
- 캐리오버가 크면 ch04 의 니들 세척(세척 바이알 · 세척 용매)을 먼저 확인합니다(오류 6번).
결과 보고서 구성#
보고서는 다른 사람이 같은 결과를 다시 만들 수 있을 만큼 적어야 합니다. 이 강좌의 한 장 보고서는 다섯 칸입니다.
| 칸 | 넣을 것 | 이번 실습 예 |
|---|---|---|
| ① 조건 | 메서드 이름 · 버전, 컬럼, 이동상, 유속, 온도, 파장, 주입량, 시퀀스 이름, 분석 일시, 분석자 | PRAC_ISO_W70M30_v01, C18, 물:메탄올 70:30, 1 mL/min, 30 °C, 273 nm, 10 µL |
| ② 크로마토그램 | 표준물 1개 + 시료 1개, 피크 이름 · RT 표시 | STD-CAF-L4, SMP-COLA-D2-01 |
| ③ 검량선 | 그래프, 식, R², 범위, 수준별 잔차 | y = 32.596x + 5.964, R² 0.99998, 5 ~ 100 mg/L |
| ④ 결과 표 | 시료 · 주입별 면적 · 바이알 농도 · 희석 배수 · 최종 농도 · 평균 | 콜라 97.0 mg/L, 에너지 음료 320.1 mg/L |
| ⑤ 판정 | SST 항목별 PASS/FAIL, 캐리오버, 확인 표준, 결론, 분석자 · 검토자 | SST 적합, 캐리오버 0.058 %, CHK +0.6 % → 결과 유효 |
시뮬레이터에서는 <b>Report › Specify Report</b>에서 템플릿 QUANT_SST_1P 를 고르고, <b>Report › Print Preview</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>)로 확인한 뒤 <b>Report › Export PDF</b> 로 저장합니다.
실험 노트와 데이터 무결성 기본#
결과를 믿을 수 있으려면 숫자를 만든 과정도 믿을 수 있어야 합니다. 이것을 데이터 무결성(data integrity) 이라 하고, 흔히 ALCOA 다섯 글자로 정리합니다.
| 글자 | 뜻 | 이 실습에서 지킬 것 |
|---|---|---|
| A — Attributable | 누가 했는지 알 수 있음 | 시퀀스 운영자 칸에 본인 학번, 노트에 서명 |
| L — Legible | 읽을 수 있고 오래 남음 | 볼펜으로 기록, 지울 수 있는 연필 · 수정액 금지 |
| C — Contemporaneous | 그 자리에서 바로 기록 | 저울 값 · 피펫 부피를 잰 즉시 적음 (나중에 몰아 쓰지 않음) |
| O — Original | 원자료 보존 | 시퀀스 폴더 · run.log · 원 CSV 를 지우거나 덮어쓰지 않음 |
| A — Accurate | 정확함 | 희석 배수 · 단위를 조제 기록과 대조 |
- 원자료 보존: 다시 분석했다고 첫 번째 데이터를 지우지 않습니다. 에너지 음료 × 2 결과(
>Cal)도 "범위 초과 → × 10 재분석" 이라는 기록과 함께 그대로 남깁니다. - 수정 이력: 노트에서 틀린 값은 한 줄로 긋고(원래 값이 보이게) 옆에 고친 값 · 날짜 · 서명 · 이유를 적습니다. 시뮬레이터에서는 처리 조건 · 희석 배수 · 수동 적분을 바꿀 때마다 <b>파일 › 감사 추적 보기</b>에 누가 · 언제 · 무엇을 · 왜 바꿨는지가 남습니다.
- 골라 쓰기 금지: SST 6회 중 나쁜 주입 하나를 빼고 5회로 RSD 를 계산하는 것은 근거 없이 할 수 없습니다. 장비 오류 같은 기록된 원인이 있을 때만, 그 이유와 함께 SOP 에 따라 처리합니다.
- SST 를 정량 시퀀스 뒤에 돌리거나 아예 건너뜁니다 → SST 가 부적합으로 나오면 앞의 시료 결과를 모두 다시 해야 합니다. SST 는 항상 먼저 돌립니다.
- 면적 RSD 를 n 으로 나눈 모표준편차(
pstdev)로 계산합니다 → 실제보다 작게 나옵니다. 반복 주입에는stdev(n − 1)를 씁니다(ch06 실습 C). - S/N 노이즈 구간을 피크 꼬리나 용매 피크 근처로 잡습니다 → h 가 커져 S/N 이 작게 나옵니다. 피크가 없는 평평한 구간을 고릅니다.
- 보고서에 최종 농도만 적고 희석 배수 · 검량 범위 · 메서드 버전을 빠뜨립니다 → 다른 사람이 결과를 다시 확인할 수 없습니다.
- 다시 분석한 뒤 첫 데이터 파일을 지웁니다 → 원자료 보존 위반입니다. 폴더 이름을 바꿔 남기고 노트에 이유를 적습니다.
단축키 · 메뉴 정리 (2교시)#
| 동작 | 메뉴 | 단축키 |
|---|---|---|
| 시스템 적합성 창 | <b>Report › System Suitability</b> | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Y</kbd> |
| 여러 데이터 겹쳐 보기 | 데이터 목록 <kbd>Ctrl</kbd>+클릭 | <kbd>O</kbd> |
| 자동 적분 | <b>Integration › Auto Integrate</b> | <kbd>F8</kbd> |
| 보고서 템플릿 고르기 | <b>Report › Specify Report</b> | — |
| 보고서 미리보기 | <b>Report › Print Preview</b> | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> |
| 보고서 PDF 저장 | <b>Report › Export PDF</b> | — |
| 감사 추적 보기 | <b>파일 › 감사 추적 보기</b> | — |
| 결과 표 저장 | <b>파일 › 결과 내보내기 › CSV</b> | — |
결과 확인#
- SST 판정표가 다섯 항목 모두
PASS이고, 면적 RSD 약 0.37 %, 최소 Rs 3.18, 최대 T 1.15, 최소 N 5610 입니다. STD-CAF-L1의 S/N 이 약 550,BLANK-2의 캐리오버가 약 0.06 % 입니다.- 미리보기 한 장에 ① ~ ⑤ 칸이 모두 채워지고, 맨 아래에 분석자 · 날짜가 들어갑니다.
Python 으로 SST 판정 흉내 내기#
판정의 핵심은 "항목마다 값을 계산하고, 기준과 비교해 PASS/FAIL 을 붙이는 것" 입니다. 기준을 딕셔너리 하나로 모아 두면 기준이 바뀌어도 코드는 그대로 둘 수 있습니다.
import statistics as st
rt = [5.161, 5.158, 5.164, 5.160, 5.166, 5.159]
area = [1643.5, 1650.2, 1638.7, 1646.9, 1655.1, 1641.0]
def rsd(v):
return st.stdev(v) / st.mean(v) * 100
limits = {"RT RSD %": ("<=", 1.0), "Area RSD %": ("<=", 2.0)}
values = {"RT RSD %": rsd(rt), "Area RSD %": rsd(area)}
for name, (op, lim) in limits.items():
v = values[name]
ok = v <= lim if op == "<=" else v >= lim
print(f"{name:12s} {v:7.3f} 기준 {op} {lim:<5} {'PASS' if ok else 'FAIL'}")실습 C 에서 여기에 Rs · T · N 과 "가장 나쁜 값" 규칙을 더해 판정표를 완성합니다.
실습 (시뮬레이터)#
시뮬레이터에서 내보낸 SEQ_QUANT_CAF_results.csv 와 같은 형식의 글자를 아래 코드 위쪽에 넣어 두었습니다(직접 내보낸 파일이 있으면 그 내용으로 바꿔도 됩니다).
- CSV 를 읽어
Standard줄의 (농도, 면적)으로np.polyfit직선을 맞추고 기울기 · 절편 · R² 를 출력합니다. - 수준별 역산 농도와 잔차(%)를 출력하고, ±15 % 를 넘는 수준이 있으면
확인 필요를 붙입니다. Sample줄마다 바이알 농도와 희석 배수를 곱한 원 시료 농도를 구하고, 시료 이름별 평균을 출력합니다.- 면적이 가장 낮은 표준물보다 작거나 가장 높은 표준물보다 크면 결과 대신
<Cal/>Cal을 출력합니다.
import csv
import io
import numpy as np
CSV_TEXT = """SampleName,SampleType,Level,Amount,Dilution,Injection,Area
STD-CAF-L1,Standard,1,5,,1,168.9
STD-CAF-L2,Standard,2,10,,1,324.1
STD-CAF-L3,Standard,3,25,,1,826.3
STD-CAF-L4,Standard,4,50,,1,1641.8
STD-CAF-L5,Standard,5,100,,1,3262.0
SMP-COLA-D2,Sample,,,2,1,1582.4
SMP-COLA-D2,Sample,,,2,2,1592.0
SMP-ENERGY-D2,Sample,,,2,1,5226.8
SMP-ENERGY-D2,Sample,,,2,2,5231.5
CHK-CAF-50,Sample,,,1,1,1645.2
SMP-ENERGY-D10,Sample,,,10,1,1047.6
SMP-ENERGY-D10,Sample,,,10,2,1051.3"""
rows = list(csv.DictReader(io.StringIO(CSV_TEXT)))
std = [r for r in rows if r["SampleType"] == "Standard"]
smp = [r for r in rows if r["SampleType"] == "Sample"]
x = np.array([float(r["Amount"]) for r in std])
y = np.array([float(r["Area"]) for r in std])
a, b = 0.0, 0.0 # TODO 1: np.polyfit 으로 기울기 a, 절편 b
r2 = 0.0 # TODO 1: 결정계수
print(f"y = {a:.3f} x + {b:.3f}, R2 = {r2:.5f}")
# TODO 2: 수준별 역산 농도 · 잔차(%) 출력, 15 % 초과면 "확인 필요"
results = {}
for r in smp:
area = float(r["Area"])
dil = float(r["Dilution"])
# TODO 4: 범위 밖이면 "<Cal" / ">Cal" 출력 후 continue
vial = 0.0 # TODO 3: 바이알 농도
final = 0.0 # TODO 3: 원 시료 농도
print(f"{r['SampleName']:15s} #{r['Injection']} 면적 {area:8.1f} -> {final:7.1f} mg/L")
results.setdefault(r["SampleName"], []).append(final)
# TODO 3: 시료 이름별 평균 출력import csv
import io
import numpy as np
CSV_TEXT = """SampleName,SampleType,Level,Amount,Dilution,Injection,Area
STD-CAF-L1,Standard,1,5,,1,168.9
STD-CAF-L2,Standard,2,10,,1,324.1
STD-CAF-L3,Standard,3,25,,1,826.3
STD-CAF-L4,Standard,4,50,,1,1641.8
STD-CAF-L5,Standard,5,100,,1,3262.0
SMP-COLA-D2,Sample,,,2,1,1582.4
SMP-COLA-D2,Sample,,,2,2,1592.0
SMP-ENERGY-D2,Sample,,,2,1,5226.8
SMP-ENERGY-D2,Sample,,,2,2,5231.5
CHK-CAF-50,Sample,,,1,1,1645.2
SMP-ENERGY-D10,Sample,,,10,1,1047.6
SMP-ENERGY-D10,Sample,,,10,2,1051.3"""
rows = list(csv.DictReader(io.StringIO(CSV_TEXT)))
std = [r for r in rows if r["SampleType"] == "Standard"]
smp = [r for r in rows if r["SampleType"] == "Sample"]
x = np.array([float(r["Amount"]) for r in std])
y = np.array([float(r["Area"]) for r in std])
a, b = np.polyfit(x, y, 1)
pred = a * x + b
r2 = 1 - np.sum((y - pred) ** 2) / np.sum((y - y.mean()) ** 2)
print(f"검량식 y = {a:.3f} x + {b:.3f}, R2 = {r2:.5f}, 범위 {x.min():g} ~ {x.max():g} mg/L")
print("\n수준 농도 역산 잔차")
for r, xi, yi in zip(std, x, y):
back = (yi - b) / a
res = (back - xi) / xi * 100
flag = " 확인 필요" if abs(res) > 15 else ""
print(f"L{r['Level']} {xi:6.1f} {back:7.2f} {res:+6.2f} %{flag}")
lo, hi = y.min(), y.max()
results = {}
print("\n시료 결과")
for r in smp:
area = float(r["Area"])
dil = float(r["Dilution"])
name = f"{r['SampleName']:15s} #{r['Injection']}"
if area < lo:
print(f"{name} 면적 {area:8.1f} -> <Cal (검량 범위 미만)")
continue
if area > hi:
print(f"{name} 면적 {area:8.1f} -> >Cal (검량 범위 초과, 다시 희석)")
continue
vial = (area - b) / a
final = vial * dil
print(f"{name} 면적 {area:8.1f} -> 바이알 {vial:6.2f} x {dil:g} = {final:7.1f} mg/L")
results.setdefault(r["SampleName"], []).append(final)
print("\n평균")
for name, vals in results.items():
spread = (max(vals) - min(vals)) / np.mean(vals) * 100 if len(vals) > 1 else 0.0
print(f" {name:15s} {np.mean(vals):7.1f} mg/L (n={len(vals)}, 차이 {spread:.2f} %)")검량식은 y = 32.596x + 5.964, R² = 0.99998 이고, 잔차가 가장 큰 수준은 L2(−2.40 %)입니다. 콜라는 97.0 mg/L, × 2 에너지 음료는 두 번 모두 >Cal 로 걸러지고, × 10 으로 다시 묽힌 에너지 음료가 320.1 mg/L 로 나옵니다. 확인 표준 CHK-CAF-50 은 50.3 mg/L 입니다.
범위를 벗어난 시료를 "몇 배로 다시 묽혀야 하는지" 계산하는 함수를 만듭니다.
plan_dilution(area, a, b, now_dil, target=40, choices=(2, 5, 10, 20, 50, 100))는 지금 면적 · 검량식 · 지금 희석 배수로 원 시료 농도를 어림하고, 바이알 농도가target에 가장 가깝게 되는 희석 배수를choices에서 고릅니다.- 고른 배수로 묽혔을 때의 예상 바이알 농도가 검량 범위(5 ~ 100) 안인지 확인해 출력합니다.
- 에너지 음료 × 2(면적 5226.8)와, 가상의 진한 커피 추출액 × 2(면적 21500.0)에 적용해 봅니다.
a, b = 32.596, 5.964 # 1교시 검량식
LOW, HIGH = 5, 100 # 검량 범위 (mg/L)
def plan_dilution(area, a, b, now_dil, target=40, choices=(2, 5, 10, 20, 50, 100)):
rough = 0.0 # TODO 1: (area - b) / a * now_dil (외삽 어림값)
best = choices[0] # TODO 1: rough / d 가 target 에 가장 가까운 d
expected = 0.0 # TODO 2: rough / best
return rough, best, expected
for name, area, dil in [("SMP-ENERGY-D2", 5226.8, 2), ("SMP-COFFEE-D2", 21500.0, 2)]:
rough, best, expected = plan_dilution(area, a, b, dil)
# TODO 2: 범위 안인지 출력
print(name, rough, best, expected)a, b = 32.596, 5.964 # 1교시 검량식
LOW, HIGH = 5, 100 # 검량 범위 (mg/L)
def plan_dilution(area, a, b, now_dil, target=40, choices=(2, 5, 10, 20, 50, 100)):
rough = (area - b) / a * now_dil # 외삽 어림값 (보고용 아님)
best = min(choices, key=lambda d: abs(rough / d - target))
expected = rough / best
return rough, best, expected
for name, area, dil in [("SMP-ENERGY-D2", 5226.8, 2), ("SMP-COFFEE-D2", 21500.0, 2)]:
rough, best, expected = plan_dilution(area, a, b, dil)
inside = LOW <= expected <= HIGH
print(f"{name}: 어림 원 시료 {rough:.0f} mg/L -> x {best} 희석, "
f"예상 바이알 {expected:.1f} mg/L ({'범위 안' if inside else '범위 밖 - 다시 계획'})")에너지 음료는 어림값 약 320 mg/L → × 10 (예상 32.0 mg/L), 커피 추출액은 약 1319 mg/L → × 50 (예상 26.4 mg/L)이 나옵니다. × 20 이 아니라 × 10 이 골라진 이유는 target 40 에 더 가깝기 때문입니다(16.0 보다 32.0 이 40 에 가까움). 어림값은 외삽이라 틀릴 수 있으므로, 예상 바이알 농도가 범위 가운데에 오도록 고르는 것이 안전합니다.
검량식이 y = 32.6x + 6.0 (x: mg/L, y: mAU*s, 범위 5 ~ 100 mg/L)입니다. 시료를 × 5 로 묽혀 주입했더니 면적이 984.0 이었습니다. 원 시료의 카페인 농도로 가장 알맞은 것은?
- 30.0 mg/L
- 150.0 mg/L
- 151.0 mg/L
- 검량 범위를 넘으므로 보고할 수 없다
정답 보기
② — 바이알 농도 = (984.0 − 6.0) / 32.6 = 30.0 mg/L 로 범위(5 ~ 100) 안이고, × 5 를 곱하면 150.0 mg/L 입니다. ① 은 희석 배수를 빠뜨린 값, ③ 은 절편을 빼지 않고 984.0 / 32.6 × 5 로 계산한 값입니다. ④ 범위 판정은 원 시료가 아니라 **바이알 농도(면적)** 로 합니다.5점 검량선의 R² 가 0.9995 로 나왔는데, 수준별 잔차가 L1 +38 %, 나머지는 ±2 % 안입니다. 가장 알맞은 대응은?
- R² 가 0.999 를 넘으므로 그대로 시료 계산에 쓴다
- L1 점을 아무 이유 없이 빼고 4점으로 다시 맞춘다
- L1 의 조제 기록 · 바이알 위치 · 앞 주입의 캐리오버를 확인하고, 원인을 찾아 L1 을 다시 조제 · 주입한다
- 원점 강제(0, 0)로 바꿔 R² 를 높인다
정답 보기
③ — R² 는 높은 농도 점에 끌려 낮은 농도의 큰 오차를 숨깁니다. L1 이 +38 % 라면 중간 희석 실수, 바이알 혼동, 앞 주입 캐리오버 같은 원인이 있을 가능성이 큽니다. 원인 없이 점을 빼는 것(②)이나 숫자를 좋게 보이려고 모델을 바꾸는 것(④)은 데이터 무결성에 어긋납니다.SEQ_SST_CAF_sst.csv 와 같은 형식의 데이터 두 벌(Day 1 · Day 2)이 있습니다. 같은 함수로 두 날을 판정합니다.
- 기준을
LIMITS딕셔너리 하나에 모읍니다 (RT RSD ≤ 1.0, Area RSD ≤ 2.0, Rs ≥ 2.0, T ≤ 2.0, N ≥ 2000). sst_table(rows)는 항목마다 판정에 쓸 값(RSD 또는 가장 나쁜 값)을 계산하고(항목, 값, 기준, PASS/FAIL)목록을 돌려줍니다.- 판정표를 줄 맞춰 출력하고, 마지막 줄에
종합: 적합또는종합: 부적합 (FAIL 항목 이름)을 출력합니다. - Day 2 가 부적합이면 어떤 원인을 먼저 의심해야 할지 주석으로 적습니다.
import csv
import io
import statistics as st
DAY1 = """inj,rt,area,rs,tf,n
1,5.161,1643.5,3.21,1.12,5690
2,5.158,1650.2,3.18,1.14,5650
3,5.164,1638.7,3.24,1.11,5720
4,5.160,1646.9,3.19,1.13,5610
5,5.166,1655.1,3.22,1.12,5680
6,5.159,1641.0,3.20,1.15,5700"""
DAY2 = """inj,rt,area,rs,tf,n
1,5.162,1648.2,2.31,1.84,3150
2,5.165,1601.5,2.28,1.88,3080
3,5.160,1652.8,2.25,1.93,3010
4,5.163,1639.4,2.22,1.97,2960
5,5.167,1570.3,2.19,2.04,2890
6,5.161,1660.1,2.16,2.08,2830"""
def load(text):
return [{k: float(v) for k, v in r.items()} for r in csv.DictReader(io.StringIO(text))]
# TODO 1: 기준 딕셔너리 "항목 이름": (열 이름, 계산 방식, 비교, 기준값)
LIMITS = {
"RT RSD (%)": ("rt", "rsd", "<=", 1.0),
}
def sst_table(rows):
table = []
for name, (col, how, op, lim) in LIMITS.items():
vals = [r[col] for r in rows]
value = 0.0 # TODO 2: how 가 "rsd" 면 RSD, "worst" 면 op 에 맞는 가장 나쁜 값
ok = False # TODO 2: 기준과 비교
table.append((name, value, f"{op} {lim}", "PASS" if ok else "FAIL"))
return table
for day, text in (("Day 1", DAY1), ("Day 2", DAY2)):
print(f"== {day} ==")
for name, value, rule, verdict in sst_table(load(text)):
print(f"{name:18s} {value:9.3f} {rule:8s} {verdict}")
# TODO 3: 종합 판정 출력import csv
import io
import statistics as st
DAY1 = """inj,rt,area,rs,tf,n
1,5.161,1643.5,3.21,1.12,5690
2,5.158,1650.2,3.18,1.14,5650
3,5.164,1638.7,3.24,1.11,5720
4,5.160,1646.9,3.19,1.13,5610
5,5.166,1655.1,3.22,1.12,5680
6,5.159,1641.0,3.20,1.15,5700"""
DAY2 = """inj,rt,area,rs,tf,n
1,5.162,1648.2,2.31,1.84,3150
2,5.165,1601.5,2.28,1.88,3080
3,5.160,1652.8,2.25,1.93,3010
4,5.163,1639.4,2.22,1.97,2960
5,5.167,1570.3,2.19,2.04,2890
6,5.161,1660.1,2.16,2.08,2830"""
def load(text):
return [{k: float(v) for k, v in r.items()} for r in csv.DictReader(io.StringIO(text))]
LIMITS = {
"RT RSD (%)": ("rt", "rsd", "<=", 1.0),
"Area RSD (%)": ("area", "rsd", "<=", 2.0),
"Rs TPH-CAF min": ("rs", "worst", ">=", 2.0),
"Tailing T max": ("tf", "worst", "<=", 2.0),
"Plates N min": ("n", "worst", ">=", 2000),
}
def sst_table(rows):
table = []
for name, (col, how, op, lim) in LIMITS.items():
vals = [r[col] for r in rows]
if how == "rsd":
value = st.stdev(vals) / st.mean(vals) * 100
else: # 가장 나쁜 값
value = min(vals) if op == ">=" else max(vals)
ok = value <= lim if op == "<=" else value >= lim
table.append((name, value, f"{op} {lim:g}", "PASS" if ok else "FAIL"))
return table
for day, text in (("Day 1", DAY1), ("Day 2", DAY2)):
rows = load(text)
table = sst_table(rows)
print(f"== {day} (n={len(rows)}) ==")
print(f"{'항목':18s} {'값':>9s} {'기준':8s} 판정")
for name, value, rule, verdict in table:
print(f"{name:18s} {value:9.3f} {rule:8s} {verdict}")
fails = [t[0] for t in table if t[3] == "FAIL"]
print("종합: 적합" if not fails else f"종합: 부적합 ({', '.join(fails)})")
print()
# Day 2 해석
# - 면적 RSD 가 2 % 를 넘음: 2 · 5회째 면적이 크게 낮음 -> 주입량 문제.
# ch05 의 공기 흡입 경고(바이알 액량 부족) · 니들 · 시린지부터 확인.
# - T 가 주입마다 커지고 Rs · N 이 계속 줄어듦 -> 컬럼 입구 오염 · 열화 경향.
# (조건 최적화 · 컬럼 교체 판단은 다음 차시들에서 다룸)Day 1 은 다섯 항목 모두 PASS 로 적합입니다(면적 RSD 0.37 %). Day 2 는 면적 RSD 약 2.16 %, 최대 T 2.08 로 두 항목이 FAIL 이라 부적합입니다. 평균 T(약 1.96)로 판정했다면 PASS 로 보였을 것이므로 "가장 나쁜 값" 규칙의 차이를 확인할 수 있습니다. Rs(최소 2.16) · N(최소 2830)은 통과했지만 주입마다 계속 나빠지는 경향이 보이는 것도 기록해 둡니다.
1교시 · 2교시 결과를 모아 보고서의 ① ~ ⑤ 칸을 텍스트로 출력합니다.
- S/N = 2H / h 로
STD-CAF-L1의 S/N 을 구하고, 비례 가정으로 LOD(S/N 3) · LOQ(S/N 10) 농도를 어림합니다. - 검량선 잔차 표준편차 σ 로 ICH 방식 LOD = 3.3σ/a, LOQ = 10σ/a 도 구해 나란히 출력합니다.
- 캐리오버(%) =
BLANK-2면적 ÷STD-CAF-L5면적 × 100 을 구하고 ≤ 0.1 % 기준으로 판정합니다. - 조건 · 검량선 · 결과 표 · 판정을 한 화면(약 30줄)에 담는 보고서를 출력합니다.
import numpy as np
conc = np.array([5, 10, 25, 50, 100], dtype=float)
area = np.array([168.9, 324.1, 826.3, 1641.8, 3262.0])
H_L1, NOISE_PP = 16.4, 0.060 # mAU
BLANK2, STD_L5 = 1.9, 3262.0 # mAU*s
a, b = np.polyfit(conc, area, 1)
sn = 0.0 # TODO 1: 2H / h
lod_sn = 0.0 # TODO 1: 5 mg/L x 3 / sn
loq_sn = 0.0 # TODO 1: 5 mg/L x 10 / sn
sigma = 0.0 # TODO 2: 잔차 제곱합 / (n - 2) 의 제곱근
lod_ich = loq_ich = 0.0 # TODO 2
carry = 0.0 # TODO 3
# TODO 4: 보고서 출력 (조건 / 검량선 / 결과 / SST / 결론)
print(sn, lod_sn, loq_sn, lod_ich, loq_ich, carry)import numpy as np
conc = np.array([5, 10, 25, 50, 100], dtype=float)
area = np.array([168.9, 324.1, 826.3, 1641.8, 3262.0])
H_L1, NOISE_PP = 16.4, 0.060 # mAU
BLANK2, STD_L5 = 1.9, 3262.0 # mAU*s
a, b = np.polyfit(conc, area, 1)
pred = a * conc + b
r2 = 1 - np.sum((area - pred) ** 2) / np.sum((area - area.mean()) ** 2)
sn = 2 * H_L1 / NOISE_PP
lod_sn = 5 * 3 / sn
loq_sn = 5 * 10 / sn
sigma = np.sqrt(np.sum((area - pred) ** 2) / (conc.size - 2))
lod_ich = 3.3 * sigma / a
loq_ich = 10 * sigma / a
carry = BLANK2 / STD_L5 * 100
results = {"SMP-COLA-D2": ([1582.4, 1592.0], 2), "SMP-ENERGY-D10": ([1047.6, 1051.3], 10)}
sst = [("RT RSD", "0.06 %", "<= 1.0 %"), ("Area RSD", "0.37 %", "<= 2.0 %"),
("Rs min", "3.18", ">= 2.0"), ("T max", "1.15", "<= 2.0"), ("N min", "5610", ">= 2000")]
line = "-" * 62
print(line)
print(" 카페인 정량 결과 보고서 SEQ_QUANT_CAF / SEQ_SST_CAF")
print(line)
print(" (1) 조건 PRAC_ISO_W70M30_v01, C18, Water/MeOH 70:30")
print(" 1.0 mL/min, 30 C, 273 nm, 10 uL")
print(" (2) 크로마토그램 STD-CAF-L4, SMP-COLA-D2-01 (첨부 PNG)")
print(f" (3) 검량선 y = {a:.3f} x + {b:.3f}, R2 = {r2:.5f}, 5 ~ 100 mg/L")
print(f" LOD/LOQ (S/N) {lod_sn:.3f} / {loq_sn:.3f} mg/L (S/N L1 = {sn:.0f})")
print(f" LOD/LOQ (ICH) {lod_ich:.2f} / {loq_ich:.2f} mg/L (sigma = {sigma:.2f})")
print(" (4) 결과")
print(f" {'시료':16s} {'면적 평균':>10s} {'희석':>5s} {'결과 mg/L':>10s}")
for name, (areas, dil) in results.items():
m = float(np.mean(areas))
final = (m - b) / a * dil
print(f" {name:16s} {m:10.1f} {dil:5d} {final:10.1f}")
print(" (5) 판정")
for item, val, rule in sst:
print(f" SST {item:9s} {val:>7s} ({rule}) PASS")
print(f" 캐리오버 {carry:.3f} % (<= 0.1 %) {'PASS' if carry <= 0.1 else 'FAIL'}")
print(" 확인 표준 CHK-CAF-50 50.3 mg/L (+0.6 %) PASS")
print(" 결론: 시스템 적합. 콜라 97.0 mg/L, 에너지 음료 320.1 mg/L 보고")
print(" 분석자: ________ 검토자: ________ 날짜: 2026-10-11")
print(line)S/N 은 약 547 이라 S/N 방식 LOD · LOQ 는 약 0.027 · 0.091 mg/L, ICH 잔차 방식은 약 0.69 · 2.09 mg/L 입니다. 같은 데이터인데 20배 넘게 차이가 나므로 보고서에는 어느 방법으로 구했는지를 꼭 적습니다. 캐리오버는 0.058 % 로 적합입니다. 보고서의 결과는 검량식에 평균 면적을 넣은 값이라 주입별 결과의 평균(97.0 · 320.1)과 같습니다.
SST 6회 주입의 면적이 1640, 1645, 1650, 1642, 1648, 1560 입니다. 기준이 면적 RSD ≤ 2.0 % 일 때 올바른 판단은?
- 1560 은 튀는 값이므로 빼고 5회로 RSD 를 계산해 적합으로 판정한다
- RSD 가 약 2.1 % 로 기준을 넘어 부적합이다. 6회째 주입의 원인(공기 흡입 · 바이알 액량 등)을 로그로 확인하고, 고친 뒤 SST 를 다시 한다
- 평균이 1600 을 넘으므로 적합이다
- RSD 대신 n 으로 나눈 모표준편차로 계산하면 기준 안이므로 적합이다
정답 보기
② — 표본 표준편차로 RSD ≈ 2.1 % 로 2.0 % 를 넘습니다. 기록된 원인 없이 값을 빼는 것(①)은 골라 쓰기이고, 계산 방식을 바꿔 기준에 맞추는 것(④)도 허용되지 않습니다. 부적합이면 원인을 찾아 고친 뒤 SST 전체를 다시 하고, 그 전에 얻은 시료 결과는 보고하지 않습니다.100 mg/L 표준물(면적 3262.0) 바로 다음 블랭크의 카페인 자리 면적이 4.9 mAU*s 입니다. 이 강좌 기준(≤ 0.1 %)으로 판단하고 가장 먼저 할 일로 알맞은 것은?
- 캐리오버 약 0.15 % 로 부적합 — 니들 세척 방식 · 세척 바이알의 용매와 액량을 확인하고 블랭크를 다시 주입한다
- 캐리오버 약 0.015 % 로 적합 — 그대로 진행한다
- 블랭크는 판정 대상이 아니므로 무시한다
- 블랭크 파일을 지우고 다시 주입한다
정답 보기
① — 4.9 ÷ 3262.0 × 100 ≈ 0.15 % 로 기준을 넘습니다. 캐리오버의 흔한 원인은 니들 바깥에 묻은 시료이므로 ch04 의 니들 세척 설정부터 확인합니다. ② 는 소수점 자리를 잘못 옮긴 값이고, ④ 원자료를 지우는 것은 데이터 무결성 위반입니다.자주 나는 오류와 해결#
1) 검량 표에서 한 수준만 크게 벗어남#
[Calibration] Caffeine: R2 = 0.98712, Level 3 residual +94.6 % (> 15 %)- 원인: 시퀀스 표의 수준 · 농도 칸을 엇갈려 넣었거나(예: L3 줄에 농도
25대신 L4 바이알 위치5), 바이알 위치가 바뀌어 다른 농도를 주입했습니다. - 해결: 시퀀스 표의 해당 줄(위치 · 수준 · 농도)과 <b>편집 › 바이알 준비</b>의 트레이 배치를 대조합니다. 칸만 틀렸으면 시퀀스 표를 고치고 <b>Calibration › Process Sequence</b> 를 다시 합니다(수정 이력 남음). 바이알을 잘못 놓았으면 그 수준을 다시 주입합니다.
2) 시료 결과 칸이 비어 있음 — 화합물을 찾지 못함#
[Quant] Caffeine not identified in SMP-COLA-D2-01: nearest peak 5.24 min outside RT window 5.161 +/- 0.050 min- 원인: A 영역의 RT 창을 너무 좁게 바꿨거나, 표준물과 시료 사이에 RT 가 밀렸습니다(평형 부족 · 조성 변화). 시료에 다른 피크가 많으면 엉뚱한 피크가 잡히기도 합니다.
- 해결: <kbd>Ctrl</kbd>+<kbd>K</kbd> → A 영역 RT 창을 기본값(± 5 %)으로 되돌립니다. 확인 표준 · 표준물의 RT 와 시료 RT 를 겹쳐(<kbd>O</kbd>) 비교하고, RT 가 계속 밀리면 ch06 오류 4번(평형)을 봅니다.
3) 결과 옆에 >Cal 표시와 범위 초과 경고#
[Quant] Warning: SMP-ENERGY-D2-01 area 5226.8 above highest level (3262.0) - result extrapolated- 원인: 시료가 가장 높은 표준물보다 진합니다.
- 해결: 외삽값을 보고하지 않습니다. 실습 B 처럼 희석 배수를 다시 정해 시료를 새로 묽히고, 시퀀스에 희석 배수를 실제 조제 기록대로 넣어 재분석합니다. 반대로
<Cal이면 덜 묽히거나, 낮은 수준 표준물을 추가해 범위를 넓힙니다.
4) 결과가 정확히 2배(또는 1/2)로 나옴#
SMP-COLA-D2 Final = 194.0 mg/L (예상 약 97 mg/L)- 원인: 희석 배수를 두 번 곱했습니다(시퀀스 표에
2를 넣었는데 Python · 엑셀에서 Final 에 또 × 2). 반대로 1/2 이면 시퀀스 표 희석 배수 칸을 비워 두고 Amount(바이알 농도)를 최종값으로 썼습니다. - 해결: 시뮬레이터 결과 CSV 에서 Amount(바이알) 와 Final(× 희석) 칸을 구분해 읽습니다. 희석 배수는 한 곳(시퀀스 표)에서만 적용합니다.
5) SST 면적 RSD 부적합#
[SST] SEQ_SST_CAF: Area RSD 2.16 % (limit <= 2.0 %) FAIL
[Sampler] Warning: air drawn - liquid level below needle tip (vial 11)- 원인: 같은 바이알을 6회 주입하는 동안 액량이 줄어 니들 끝이 공기를 빨아들였습니다(ch05). 바이알 마개 셉타가 찢어져 증발한 경우, 탈기하지 않은 시료의 기포도 원인이 됩니다.
- 해결: 바이알 액량을 1.5 mL 로 채우거나 SST 용액을 바이알 두 개로 나눕니다. 원인을 고친 뒤 SST 6회 전체를 다시 합니다. 나쁜 주입 하나만 빼고 다시 계산하지 않습니다.
6) 진한 표준물 뒤 블랭크에 카페인 피크 — 캐리오버 초과#
[Data] 007-BLANK-2-01: peak at 5.16 min, area 4.9 mAU*s (Blank)
[SST] Carryover 0.150 % (limit <= 0.1 %) FAIL- 원인: 니들 세척이 꺼져 있거나 세척 바이알(위치 7)의 용매가 바닥났습니다. 세척 용매가 카페인을 잘 녹이지 못해도 같은 결과가 나옵니다.
- 해결: <b>모드 › 샘플러 설정</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>L</kbd>)에서 니들 세척 방식 · 위치를 확인하고 세척 바이알을 새 이동상으로 채웁니다. 블랭크를 두 번 연속 주입해 줄어드는지 봅니다. 고치기 전까지는 진한 시료 다음에 블랭크 줄을 넣습니다.
7) Python 검량선의 기울기가 0.03 근처로 나옴#
y = 0.031 x + -0.182, R2 = 0.99998
콜라: 103487.6 mg/L- 원인:
np.polyfit(area, conc, 1)처럼 x 와 y 를 바꿔 넣었습니다. 오류는 나지 않고 "면적 → 농도" 직선이 나와, 그 기울기 · 절편을 농도 → 면적 식에 넣으면 결과가 엉뚱해집니다.a, b = np.polyfit(...)의 순서(기울기, 절편)를 바꿔 받아도 비슷한 일이 생깁니다. - 해결: 항상
a, b = np.polyfit(conc, area, 1)(x = 농도, y = 면적, 반환은 기울기 · 절편 순서)으로 쓰고, 기울기가 감응 계수(면적 ÷ 농도, 약 32.6)와 비슷한지 출력해 확인합니다.
과제 / 팀 미션#
아래 다섯 문제를 풀고, 각 답의 근거를 한 줄씩 적어 제출합니다. 실습 A 의 시료 결과 출력과 실습 C 의 Day 1 · Day 2 판정표도 함께 냅니다.
- 외부표준법에서 결과의 정확도를 가장 직접적으로 좌우하는 장비 성능은? ① 컬럼 온도 표시 정밀도 ② 주입량 재현성 ③ 램프 사용 시간 ④ 폐액 통 크기
- 검량 범위가 5 ~ 100 mg/L 이고 × 2 로 묽힌 시료의 바이알 농도가 160 mg/L(외삽)로 나왔습니다. 알맞은 처리는? ① 320 mg/L 로 보고 ② 100 mg/L 로 보고 ③ × 10 정도로 다시 묽혀 재분석 ④ 검량선을 2차식으로 바꿔 계산
a, b = np.polyfit(conc, area, 1)에서 a 와 b 는? ① 절편, 기울기 ② 기울기, 절편 ③ R², 기울기 ④ 평균, 표준편차- SST 에서 테오필린–카페인 분리도를 보는 이유는? ① 카페인 면적을 크게 만들기 위해 ② 가장 가까운 이웃 피크와 섞이지 않아 면적이 정확한지 확인하기 위해 ③ 이론단수를 계산하기 위해 ④ 캐리오버를 확인하기 위해
- 데이터 무결성 원칙에 맞는 행동은? ① 재분석 후 첫 데이터를 지운다 ② 노트의 틀린 값을 수정액으로 지운다 ③ 틀린 값에 한 줄을 긋고 고친 값 · 날짜 · 서명 · 이유를 적는다 ④ SST 에서 나쁜 주입을 빼고 계산한다
정답 보기
1-② 외부표준법은 표준물과 시료의 주입량이 같다고 믿고 면적을 비교하므로 주입 재현성이 핵심입니다(SST 면적 RSD). 2-③ 범위 밖 값은 보고하지 않고, 바이알 농도가 범위 가운데(약 32 mg/L)에 오도록 다시 묽힙니다. 3-② polyfit 은 높은 차수 계수부터 돌려주므로 1차식은 [기울기, 절편] 입니다. 4-② 이웃 피크와 겹치면 적분 경계가 흔들려 면적이 틀어집니다. 5-③ 원래 값이 보이게 남기고 수정 이력을 적습니다(ALCOA).시퀀스 끝의 확인 표준 CHK-CAF-50 이 처음보다 3 % 낮게(48.5 mg/L) 나왔다고 가정합니다.
- 시퀀스 처음의 검량선과, 끝에서 다시 주입한 5개 수준(면적이 모두 3 % 낮다고 가정)의 검량선을 각각 만들고, 콜라 결과가 각각 얼마인지 Python 으로 계산합니다.
- 두 검량선을 평균한 브래킷(bracketing) 검량으로 계산한 결과와 비교하고, 시퀀스 중간에 감도가 바뀐다면 어떤 시료가 가장 크게 영향을 받는지 세 줄로 씁니다.
- 확인 표준의 허용 범위를 ±2 % 로 정했다면 이 시퀀스의 결과를 보고할 수 있는지 판단하고, 그 근거를 보고서 ⑤ 칸 문장으로 씁니다.
참고자료#
- Agilent OpenLab CDS 제품 페이지 (검량 · 정량 · 시스템 적합성 · 보고서 개요): https://www.agilent.com/en/product/software-informatics/analytical-software-suite/chromatography-data-systems/openlab-cds
- Agilent OpenLab CDS · ChemStation Edition 데이터 분석(검량 표 · 정량 · 시스템 적합성 · 보고서 템플릿) 설명서 — Agilent 문헌 라이브러리에서 제품명으로 검색: https://www.agilent.com/en/support
- ICH Q2(R2) Validation of Analytical Procedures — 직선성 · 범위 · 검출 한계 · 정량 한계 정의: https://www.ich.org/page/quality-guidelines
- USP 일반 시험법 〈621〉 Chromatography — 시스템 적합성(반복성 · 분리도 · 대칭도 · 이론단수 · S/N) 정의 (최신판은 USP-NF 에서 확인)
- 식품의약품안전처 「의약품 등 시험방법 밸리데이션 가이드라인」 · 대한민국약전 일반시험법 액체크로마토그래프법 — 국내 기준 확인용 (최신판은 식약처 누리집에서 확인)
- U.S. FDA, Data Integrity and Compliance With Drug CGMP — Questions and Answers (2018) — ALCOA, 원자료 · 감사 추적: https://www.fda.gov/regulatory-information/search-fda-guidance-documents/data-integrity-and-compliance-drug-cgmp-questions-and-answers
- L. R. Snyder, J. J. Kirkland, J. W. Dolan, Introduction to Modern Liquid Chromatography, 3rd ed., Wiley (2010) — 정량 · 검량 · 시스템 적합성
- PubChem 카페인 (물성 · 안전 정보): https://pubchem.ncbi.nlm.nih.gov/compound/Caffeine
- NumPy
polyfit: https://numpy.org/doc/stable/reference/generated/numpy.polyfit.html · Pythonstatistics: https://docs.python.org/3/library/statistics.html ·csv: https://docs.python.org/3/library/csv.html - 이 사이트의 도구: Python Playground · 3D 뷰어 · 3D 시뮬레이터
- 이전 차시: ch06 첫 분석: 카페인 표준물 · 다음 차시: ch08 실무 실험: 조건 최적화와 방법 개발