전체 과정 P3 기초 실험 · 약 100분 · 초급

CH07정량 분석: 검량선과 시스템 적합성

농도별 표준물로 검량선을 만들어 미지 시료의 농도를 구하고, 시스템 적합성 시험으로 결과를 믿을 수 있는지 확인합니다.

선수 챕터: CH05 CH06

학습 목표#

  • 외부표준법(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 모델 준비 상황

3D 장비 모델(assets/robot/robot.json)은 강좌 진행에 맞춰 추가됩니다. 차시 머리에 3D 뷰어 버튼이 보이지 않으면 아직 준비 중입니다. 이 차시는 대부분 데이터 분석 화면과 Playground 에서 진행합니다. 모델이 준비되면 트레이 위 바이알 10개의 배치와, 시퀀스가 도는 동안 니들이 바이알 → 세척 바이알 → 주입구를 오가는 순서를 3D 로 확인해 봅니다.

3D 시뮬레이터 열기

3D 뷰어 열기

외부표준법의 원리#

외부표준법(external standard method) 은 농도를 아는 표준물을 시료와 따로(외부에서) 주입해 "농도 → 면적" 관계를 먼저 만들고, 시료의 면적을 그 관계에 거꾸로 넣어 농도를 구하는 방법입니다.

  1. 표준물 여러 농도(x)를 주입해 면적(y)을 얻습니다.
  2. 점들에 직선 y = a · x + b 를 맞춥니다 (a = 기울기, b = 절편).
  3. 시료 면적 y_시료 를 넣어 x = (y_시료 − b) / a 로 바이알 속 농도를 구합니다.
  4. 시료를 묽혀 넣었다면 희석 배수(dilution factor) 를 곱해 원래 시료의 농도로 되돌립니다.

이 방법이 맞으려면 두 가지가 지켜져야 합니다.

  • 표준물과 시료가 똑같이 처리되어야 합니다. 같은 메서드 · 같은 주입량 · 같은 적분 조건(ch06 의 Apply to All)이어야 면적을 서로 비교할 수 있습니다.
  • 주입량이 매번 같아야 합니다. 외부표준법은 "10 µL 를 넣었다" 를 그대로 믿기 때문에, 주입 재현성이 결과 정확도를 좌우합니다. 그래서 2교시에서 반복 주입 RSD 를 먼저 확인합니다.
내부표준법은 언제 쓰나요

주입량 · 전처리 손실이 크게 흔들리는 시료(추출 · 농축을 거치는 시료 등)에는 시료마다 다른 물질(내부표준) 을 같은 양 넣고 "분석물 면적 ÷ 내부표준 면적" 으로 검량하는 내부표준법을 씁니다. 이번 음료 시료는 묽혀서 바로 넣으므로 외부표준법으로 충분합니다.

검량 수준(level) 설계 — 5점#

수준 (Level)시료 이름카페인 농도 (mg/L)원액 1012 mg/L 에서 덜어 낼 양 (10 mL 플라스크)
1STD-CAF-L150.049 mL → 중간 희석(STD-CAF-100 에서 0.500 mL)
2STD-CAF-L2100.099 mL → 중간 희석(STD-CAF-100 에서 1.000 mL)
3STD-CAF-L3250.247 mL
4STD-CAF-L4500.494 mL (ch06 의 STD-CAF-50 과 같음)
5STD-CAF-L51000.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)희석 배수주입
11BLANK-1Blank———1
22STD-CAF-L1Standard15—1
33STD-CAF-L2Standard210—1
44STD-CAF-L3Standard325—1
55STD-CAF-L4Standard450—1
66STD-CAF-L5Standard5100—1
71BLANK-2Blank———1
88SMP-COLA-D2Sample——22
99SMP-ENERGY-D2Sample——22
105CHK-CAF-50Sample——11
  • 표준물은 낮은 농도 → 높은 농도 순으로 주입합니다. 높은 농도 다음에 낮은 농도를 넣으면 잔류(캐리오버)가 낮은 농도 면적을 부풀립니다.
  • 7번 줄 BLANK-2 는 가장 진한 표준물 바로 뒤에 넣어 캐리오버를 확인하는 블랭크입니다(2교시).
  • 10번 줄 CHK-CAF-50 은 L4 바이알을 시료처럼 한 번 더 넣은 확인 표준(check standard)입니다. 시퀀스 끝에서 50 mg/L 이 그대로 나오면 시퀀스 동안 장비가 흔들리지 않았다는 증거가 됩니다(ch05 의 브래킷).
  • 위치 7 은 세척 바이알(ch05)이므로 시료 바이알은 8번부터 놓습니다.

따라하기 — 검량 표준물 · 시료 준비와 시퀀스 실행#

  1. <b>편집 › 표준용액 조제</b>를 고르고, ch06 에서 만든 원액 STOCK-CAF(1012 mg/L)가 목록에 있는지 확인합니다. 없으면 ch06 따라하기 1 ~ 2단계로 다시 만듭니다.
  2. 희석 탭에서 원액 STOCK-CAF, 농도 100 mg/L, 플라스크 10 mL, 용매 이동상 A70/B30 → 계산(0.988 mL) → 만들기 하고 이름을 STD-CAF-L5 로 바꿉니다. 같은 방법으로 STD-CAF-L4(50) · STD-CAF-L3(25)를 만듭니다.
  3. 희석 탭의 원액을 STD-CAF-L5 로 바꾸고 10 mg/L(1.000 mL) → STD-CAF-L2, 5 mg/L(0.500 mL) → STD-CAF-L1 을 만듭니다. 만들 때마다 창 아래 실제 농도 칸 값과 덜어 낸 부피를 실험 노트에 그대로 옮깁니다.
  4. 같은 창의 시료 탭에서 DRINK-COLA 를 고르고 탈기(10 min) → 여과(0.45 µm) → 희석 5.00 mL → 10 mL, 용매 이동상 A70/B30 → 만들기 → 이름 SMP-COLA-D2. DRINK-ENERGY 도 똑같이 해서 SMP-ENERGY-D2 를 만듭니다.
  5. <b>편집 › 바이알 준비</b>(ch04)에서 위치 1 BLANK, 2 ~ 6 STD-CAF-L1 ~ L5, 8 SMP-COLA-D2, 9 SMP-ENERGY-D2 를 놓습니다. 위치 1 · 5 는 두 번 쓰므로 액량을 1.5 mL 로 채웁니다.
  6. <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> 로 채웁니다.
  7. 종류 칸에서 Standard 를 고른 줄은 수준 · 농도 칸이 노란색으로 바뀝니다. 2 ~ 6번 줄에 수준 1 ~ 5, 농도 5 · 10 · 25 · 50 · 100 을 넣습니다. 8 · 9번 줄의 희석 배수 칸에 2, 10번 줄에 1 을 넣습니다.
  8. C 영역 트레이 대조가 모두 ● 이고 D 영역 예상 시간(12회 × 약 11 min ≈ 132 min)과 용매 · 폐액 여유를 확인한 뒤 <b>Sequence › Save As</b> 로 저장합니다.
  9. ch06 시스템 준비 체크리스트(퍼지 → 평형 → Balance)를 마치고 <b>Run › Run Sequence</b>(<kbd>F6</kbd>)로 시작합니다. 기다리기 길면 <b>보기 › 시간 배속</b>(<kbd>Ctrl</kbd>+<kbd>]</kbd>)을 씁니다.
수준 · 농도 칸을 CSV 로 한 번에

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 폴더를 열고 검량 표를 만듭니다.

Calibration › Calibration Table (Ctrl+K) 파일 · Integration · Calibration · Report · 도움말 A 화합물 목록 Caffeine RT 5.161 RT 창: ± 5 % (기본) 단위: mg/L New Calibration Table 에서 피크를 클릭해 추가 B 수준 표 Lv 농도 면적 감응계수 역산 잔차% 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 C 검량선 그래프 농도 (mg/L) 면적 점 클릭: B 표 해당 줄 선택 D 맞춤 설정 모델: 직선 ▾ 원점: 무시 (포함 · 강제) ▾ 가중치: 없음 (1/x · 1/x²) ▾ E 결과 요약 y = 32.596 x + 5.964 R² = 0.99998 점 5개 범위 5 ~ 100 mg/L 경고 없음 (잔차 모두 ±15 % 안)
검량 표 화면 구성도 — A 화합물 목록, B 수준 표, C 검량선 그래프, D 맞춤 설정, E 결과 요약
영역하는 일
A 화합물 목록검량할 화합물 이름 · 기준 RT · RT 창(이 시간 안에 든 피크를 그 화합물로 인식)
B 수준 표수준마다 농도 · 면적 · 감응 계수(면적 ÷ 농도) · 역산 농도 · 잔차(%)
C 검량선 그래프점과 맞춘 직선. 점을 클릭하면 B 표의 그 줄이 선택됨
D 맞춤 설정모델(직선 · 2차), 원점 처리(포함 · 무시 · 강제), 가중치(없음 · 1/x · 1/x²)
E 결과 요약식 y = a·x + b, 결정계수 R², 점 개수, 경고

따라하기 — 검량 표 만들기#

  1. A 영역 데이터 목록에서 SEQ_QUANT_CAF 폴더를 펼치고, STD-CAF-L4 파일을 더블클릭합니다. ch06 처리 조건(적분 끄기 0 ~ 2 min 등)을 <b>Integration › Apply to All</b> 로 폴더 전체에 적용하고 <kbd>F8</kbd> 로 적분합니다.
  2. <b>Calibration › New Calibration Table</b>을 고릅니다. 카페인 피크(5.16 min)를 클릭하면 A 영역에 Caffeine · RT 5.161 · 창 ± 5 % 가 들어옵니다.
  3. <b>Calibration › Process Sequence</b>를 누르면 시퀀스 표의 수준 · 농도를 읽어 Standard 파일 5개의 카페인 면적이 B 표에 채워집니다.
  4. <b>Calibration › Calibration Table</b>(<kbd>Ctrl</kbd>+<kbd>K</kbd>)로 검량 표를 엽니다. D 영역을 **모델 직선 · 원점 포함 안 함(무시) · 가중치 없음** 으로 둡니다.
  5. E 영역의 식과 R² 를 기록합니다. 그다음 B 표의 역산 농도 · 잔차(%) 칸을 위에서부터 훑어, 크게 벗어난 수준이 없는지 봅니다.
  6. 시료 결과를 보려면 데이터 목록에서 SMP-COLA-D2-01 을 엽니다. D 적분 결과 표에 Amount (바이알 농도) 와 Final (× 희석 배수) 칸이 새로 생깁니다.
  7. <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)잔차 (%)
15168.933.785.00−0.03
210324.132.419.76−2.40
325826.333.0525.17+0.67
4501641.832.8450.18+0.37
51003262.032.6299.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 일 때의 면적. 이상적으로는 05.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² 만 보고 끝내지 않습니다

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-011582.448.36296.7범위 안
SMP-COLA-D2-021592.048.66297.3범위 안
SMP-ENERGY-D2-015226.8160.22(320.3)**범위 초과 >Cal**
SMP-ENERGY-D2-025231.5160.32(320.6)**범위 초과 >Cal**
CHK-CAF-50-011645.250.29150.3확인 표준 +0.6 %
  • 콜라는 2회 평균 97.0 mg/L 입니다. 두 주입의 차이는 0.6 % 로 작습니다.
  • 에너지 음료의 면적 5226.8 은 가장 높은 표준물(3262.0)보다 큽니다. 시뮬레이터는 Amount 칸에 >Cal 표시를 붙이고 로그에 경고를 남깁니다. 괄호 안의 값은 직선을 범위 밖으로 늘려서(외삽) 얻은 값이라 보고하지 않습니다.
  • 확인 표준 CHK-CAF-50 이 50.3 mg/L 로 나와 시퀀스 처음과 끝 사이에 감도가 거의 변하지 않았음을 보여 줍니다.
검량 범위 5 ~ 100 mg/L 0 25 50 75 100 바이알 농도 (mg/L) 0 1000 2000 3000 면적 (mAU*s) L1 L2 L3 L4 L5 콜라 × 2 → 48.5 mg/L × 2 = 97.0 에너지 × 10 → 32.0 × 10 = 320.1 에너지 × 2 면적 5226.8 >Cal (범위 초과) 외삽값 보고 안 함 다시 묽혀 재분석 y = 32.596 x + 5.964 R² = 0.99998
카페인 검량선 — 5개 수준(●)과 맞춘 직선, 콜라(◆)는 범위 안, 에너지 음료 × 2 희석(▲)은 범위 밖이라 × 10 으로 다시 묽혀(◆) 범위 안에서 정량

검량 범위 밖 시료의 처리 — 희석 후 재분석#

범위를 넘은 시료는 숫자를 고치는 것이 아니라 시료를 다시 준비합니다.

  1. 목표 바이알 농도를 범위 가운데(약 30 ~ 50 mg/L)로 잡고 필요한 희석 배수를 정합니다. 에너지 음료는 대략 320 mg/L 로 짐작되므로 × 10 ~ × 20 이 알맞습니다.
  2. <b>편집 › 표준용액 조제</b> 시료 탭에서 탈기 · 여과한 DRINK-ENERGY 1.00 mL → 10 mL(× 10)로 묽혀 SMP-ENERGY-D10 을 만듭니다. × 2 바이알을 다시 × 5 로 묽혀도 같은 배수가 되지만, 희석 단계가 늘수록 피펫 오차가 쌓이므로 원 시료에서 한 번에 묽히는 편이 낫습니다.
  3. <b>편집 › 바이알 준비</b>에서 위치 10 에 놓고, 시퀀스 SEQ_QUANT_CAF 를 <b>Sequence › Save As</b> 로 SEQ_QUANT_CAF_RE 로 복사합니다. 블랭크 1줄, SMP-ENERGY-D10(희석 배수 10, 주입 2), CHK-CAF-50 1줄만 남기고 <kbd>F6</kbd> 로 실행합니다.
  4. 같은 날 · 같은 메서드 · 같은 처리 조건이므로 기존 검량 표를 그대로 씁니다. 결과는 면적 1047.6 · 1051.3 → 바이알 31.96 · 32.07 mg/L → × 10 → 319.6 · 320.7 mg/L, 평균 320.1 mg/L 입니다.
  5. 실험 노트에는 × 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 영역과 같은 값이 나오는지 확인해 봅니다.

python시뮬 실행 가능
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-MIX × 6) 테오필린 카페인 Δt = tR₂ − tR₁ 6회 겹침 RT · 면적 RSD Rs = 1.18 Δt / (w½₁ + w½₂) ③ 대칭도 T · ④ 이론단수 N 5 % 높이: W0.05 f T = W0.05 / (2f) (1 = 완전 대칭) N = 5.54 (tR / w½)² 꼬리 (T > 1) ⑤ S/N = 2H / h 노이즈 구간 4.00 ~ 4.60 min h (peak-to-peak) H STD-CAF-L1: H 16.4 mAU, h 0.060 mAU S/N ≈ 547 · LOD (S/N 3) · LOQ (S/N 10) ⑥ 캐리오버 STD-CAF-L5 (100 mg/L) → BLANK-2 면적 3262.0 면적 1.9 캐리오버 % = 1.9 / 3262.0 × 100 ≈ 0.058 % (기준 ≤ 0.1 % 예시) 블랭크 확대
SST 에서 재는 값 — 6회 겹친 크로마토그램(반복성), 테오필린 · 카페인 분리도, 5 % 높이에서 잰 대칭도, 노이즈 구간의 h 와 피크 높이 H 로 구하는 S/N, 진한 표준물 뒤 블랭크의 캐리오버

따라하기 — SST 6회 주입과 판정#

  1. <b>편집 › 표준용액 조제</b> 혼합 희석 탭에서 STOCK-TPH 20 mg/L + STOCK-CAF 50 mg/L, 10 mL, 용매 이동상 A70/B30 → SST-MIX 를 만들고 위치 11 에 놓습니다.
  2. 시퀀스 SEQ_SST_CAF 를 만듭니다: 1번 줄 BLANK-1(위치 1, 1회), 2번 줄 SST-MIX(위치 11, 종류 Standard, 수준 칸 비움, 주입 6회). 수준을 비워 두면 검량 표에 들어가지 않습니다.
  3. 실제 분석에서는 SST 시퀀스를 1교시 정량 시퀀스보다 먼저 돌립니다. <kbd>F6</kbd> 로 실행합니다.
  4. 데이터 분석 화면에서 SEQ_SST_CAF 폴더에 처리 조건을 Apply to All 하고 <kbd>F8</kbd> 로 적분합니다.
  5. <b>Report › System Suitability</b>(<kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Y</kbd>)를 엽니다. 기준 설정 탭에서 위 표의 기준 다섯 개와 "가장 나쁜 값으로 판정" 을 넣고, 기준 피크 Caffeine, 이웃 피크 Theophylline 을 고릅니다.
  6. 결과 탭에 6회 주입의 RT · 면적 · Rs · T · N 과 평균 · RSD, 항목별 PASS / FAIL 이 나옵니다. 아래 예시와 비교합니다.
  7. S/N 탭에서 노이즈 구간을 4.00 ~ 4.60 min(피크가 없는 평평한 구간)으로 정하고 STD-CAF-L1 의 S/N 을 봅니다.
  8. <b>파일 › 결과 내보내기 › CSV</b> 로 SST 결과를 SEQ_SST_CAF_sst.csv 로 저장합니다(실습 C).
주입카페인 RT (min)카페인 면적Rs (TPH–CAF)TN
15.1611643.53.211.125690
25.1581650.23.181.145650
35.1641638.73.241.115720
45.1601646.93.191.135610
55.1661655.13.221.125680
65.1591641.03.201.155700
판정RSD 0.06 % → PASSRSD 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) 높이 H16.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 가 바로 이것을 보는 줄입니다.

  1. 데이터 분석 화면에서 BLANK-1 · STD-CAF-L5 · BLANK-2 를 <kbd>Ctrl</kbd>+클릭으로 고르고 <kbd>O</kbd> 로 겹칩니다.
  2. 5.0 ~ 5.4 min · 0 ~ 1 mAU 를 끌어 확대합니다. BLANK-1 에는 없던 작은 피크가 BLANK-2 의 5.16 min 에 보입니다.
  3. 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번).

결과 보고서 구성#

보고서는 다른 사람이 같은 결과를 다시 만들 수 있을 만큼 적어야 합니다. 이 강좌의 한 장 보고서는 다섯 칸입니다.

카페인 정량 결과 보고서 QUANT_SST_1P ① 분석 조건 메서드 PRAC_ISO_W70M30_v01 · C18 · 물:MeOH 70:30 1 mL/min · 30 °C · 273 nm · 10 µL · 시퀀스 · 일시 · 분석자 ② 크로마토그램 Caffeine 5.16 ③ 검량선 R² 0.99998 ④ 결과 표 시료 면적 희석 결과 (mg/L) SMP-COLA-D2 1587.2 2 97.0 SMP-ENERGY-D10 1049.4 10 320.1 ⑤ 판정 · 결론 · 서명 SST 5항목 PASS · 캐리오버 0.058 % · CHK +0.6 % 결론: 시스템 적합, 결과 유효 분석자 ______ 검토자 ______ 날짜 ______ 다시 할 수 있게 (버전까지) 표준물 1 + 시료 1 피크 이름 · RT 면적 · 희석 배수 · 최종 농도 기준과 함께 PASS / FAIL 식 · 범위 · 잔차 LOD · LOQ (방법 표기) 범위 밖 결과는 재분석 값만 원자료 위치 · 감사 추적 참조
한 장 결과 보고서 구성 — ① 분석 조건, ② 대표 크로마토그램, ③ 검량선, ④ 결과 표, ⑤ SST 판정과 결론 · 서명
칸넣을 것이번 실습 예
① 조건메서드 이름 · 버전, 컬럼, 이동상, 유속, 온도, 파장, 주입량, 시퀀스 이름, 분석 일시, 분석자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 을 붙이는 것" 입니다. 기준을 딕셔너리 하나로 모아 두면 기준이 바뀌어도 코드는 그대로 둘 수 있습니다.

python시뮬 실행 가능
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 과 "가장 나쁜 값" 규칙을 더해 판정표를 완성합니다.

실습 (시뮬레이터)#

실습 A — numpy.polyfit 으로 검량선을 만들고 음료 2종의 카페인 함량 구하기 (1교시)

시뮬레이터에서 내보낸 SEQ_QUANT_CAF_results.csv 와 같은 형식의 글자를 아래 코드 위쪽에 넣어 두었습니다(직접 내보낸 파일이 있으면 그 내용으로 바꿔도 됩니다).

  1. CSV 를 읽어 Standard 줄의 (농도, 면적)으로 np.polyfit 직선을 맞추고 기울기 · 절편 · R² 를 출력합니다.
  2. 수준별 역산 농도와 잔차(%)를 출력하고, ±15 % 를 넘는 수준이 있으면 확인 필요 를 붙입니다.
  3. Sample 줄마다 바이알 농도와 희석 배수를 곱한 원 시료 농도를 구하고, 시료 이름별 평균을 출력합니다.
  4. 면적이 가장 낮은 표준물보다 작거나 가장 높은 표준물보다 크면 결과 대신 <Cal / >Cal 을 출력합니다.
python시뮬 실행 가능
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: 시료 이름별 평균 출력
풀이
python시뮬 실행 가능
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 입니다.

실습 B — 범위 밖 시료의 재희석 계획 세우기 (1교시)

범위를 벗어난 시료를 "몇 배로 다시 묽혀야 하는지" 계산하는 함수를 만듭니다.

  1. plan_dilution(area, a, b, now_dil, target=40, choices=(2, 5, 10, 20, 50, 100)) 는 지금 면적 · 검량식 · 지금 희석 배수로 원 시료 농도를 어림하고, 바이알 농도가 target 에 가장 가깝게 되는 희석 배수를 choices 에서 고릅니다.
  2. 고른 배수로 묽혔을 때의 예상 바이알 농도가 검량 범위(5 ~ 100) 안인지 확인해 출력합니다.
  3. 에너지 음료 × 2(면적 5226.8)와, 가상의 진한 커피 추출액 × 2(면적 21500.0)에 적용해 봅니다.
python시뮬 실행 가능
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)
풀이
python시뮬 실행 가능
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 에 가까움). 어림값은 외삽이라 틀릴 수 있으므로, 예상 바이알 농도가 범위 가운데에 오도록 고르는 것이 안전합니다.

확인 문제 1

검량식이 y = 32.6x + 6.0 (x: mg/L, y: mAU*s, 범위 5 ~ 100 mg/L)입니다. 시료를 × 5 로 묽혀 주입했더니 면적이 984.0 이었습니다. 원 시료의 카페인 농도로 가장 알맞은 것은?

  1. 30.0 mg/L
  2. 150.0 mg/L
  3. 151.0 mg/L
  4. 검량 범위를 넘으므로 보고할 수 없다
정답 보기② — 바이알 농도 = (984.0 − 6.0) / 32.6 = 30.0 mg/L 로 범위(5 ~ 100) 안이고, × 5 를 곱하면 150.0 mg/L 입니다. ① 은 희석 배수를 빠뜨린 값, ③ 은 절편을 빼지 않고 984.0 / 32.6 × 5 로 계산한 값입니다. ④ 범위 판정은 원 시료가 아니라 **바이알 농도(면적)** 로 합니다.
확인 문제 2

5점 검량선의 R² 가 0.9995 로 나왔는데, 수준별 잔차가 L1 +38 %, 나머지는 ±2 % 안입니다. 가장 알맞은 대응은?

  1. R² 가 0.999 를 넘으므로 그대로 시료 계산에 쓴다
  2. L1 점을 아무 이유 없이 빼고 4점으로 다시 맞춘다
  3. L1 의 조제 기록 · 바이알 위치 · 앞 주입의 캐리오버를 확인하고, 원인을 찾아 L1 을 다시 조제 · 주입한다
  4. 원점 강제(0, 0)로 바꿔 R² 를 높인다
정답 보기③ — R² 는 높은 농도 점에 끌려 낮은 농도의 큰 오차를 숨깁니다. L1 이 +38 % 라면 중간 희석 실수, 바이알 혼동, 앞 주입 캐리오버 같은 원인이 있을 가능성이 큽니다. 원인 없이 점을 빼는 것(②)이나 숫자를 좋게 보이려고 모델을 바꾸는 것(④)은 데이터 무결성에 어긋납니다.
실습 C — 6회 반복 주입 데이터로 SST 판정표 만들기 (2교시)

SEQ_SST_CAF_sst.csv 와 같은 형식의 데이터 두 벌(Day 1 · Day 2)이 있습니다. 같은 함수로 두 날을 판정합니다.

  1. 기준을 LIMITS 딕셔너리 하나에 모읍니다 (RT RSD ≤ 1.0, Area RSD ≤ 2.0, Rs ≥ 2.0, T ≤ 2.0, N ≥ 2000).
  2. sst_table(rows) 는 항목마다 판정에 쓸 값(RSD 또는 가장 나쁜 값)을 계산하고 (항목, 값, 기준, PASS/FAIL) 목록을 돌려줍니다.
  3. 판정표를 줄 맞춰 출력하고, 마지막 줄에 종합: 적합 또는 종합: 부적합 (FAIL 항목 이름) 을 출력합니다.
  4. Day 2 가 부적합이면 어떤 원인을 먼저 의심해야 할지 주석으로 적습니다.
python시뮬 실행 가능
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: 종합 판정 출력
풀이
python시뮬 실행 가능
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)은 통과했지만 주입마다 계속 나빠지는 경향이 보이는 것도 기록해 둡니다.

실습 D — LOD · LOQ · 캐리오버를 계산하고 보고서 한 장(텍스트)으로 정리하기 (2교시)

1교시 · 2교시 결과를 모아 보고서의 ① ~ ⑤ 칸을 텍스트로 출력합니다.

  1. S/N = 2H / h 로 STD-CAF-L1 의 S/N 을 구하고, 비례 가정으로 LOD(S/N 3) · LOQ(S/N 10) 농도를 어림합니다.
  2. 검량선 잔차 표준편차 σ 로 ICH 방식 LOD = 3.3σ/a, LOQ = 10σ/a 도 구해 나란히 출력합니다.
  3. 캐리오버(%) = BLANK-2 면적 ÷ STD-CAF-L5 면적 × 100 을 구하고 ≤ 0.1 % 기준으로 판정합니다.
  4. 조건 · 검량선 · 결과 표 · 판정을 한 화면(약 30줄)에 담는 보고서를 출력합니다.
python시뮬 실행 가능
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)
풀이
python시뮬 실행 가능
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)과 같습니다.

확인 문제 3

SST 6회 주입의 면적이 1640, 1645, 1650, 1642, 1648, 1560 입니다. 기준이 면적 RSD ≤ 2.0 % 일 때 올바른 판단은?

  1. 1560 은 튀는 값이므로 빼고 5회로 RSD 를 계산해 적합으로 판정한다
  2. RSD 가 약 2.1 % 로 기준을 넘어 부적합이다. 6회째 주입의 원인(공기 흡입 · 바이알 액량 등)을 로그로 확인하고, 고친 뒤 SST 를 다시 한다
  3. 평균이 1600 을 넘으므로 적합이다
  4. RSD 대신 n 으로 나눈 모표준편차로 계산하면 기준 안이므로 적합이다
정답 보기② — 표본 표준편차로 RSD ≈ 2.1 % 로 2.0 % 를 넘습니다. 기록된 원인 없이 값을 빼는 것(①)은 골라 쓰기이고, 계산 방식을 바꿔 기준에 맞추는 것(④)도 허용되지 않습니다. 부적합이면 원인을 찾아 고친 뒤 SST 전체를 다시 하고, 그 전에 얻은 시료 결과는 보고하지 않습니다.
확인 문제 4

100 mg/L 표준물(면적 3262.0) 바로 다음 블랭크의 카페인 자리 면적이 4.9 mAU*s 입니다. 이 강좌 기준(≤ 0.1 %)으로 판단하고 가장 먼저 할 일로 알맞은 것은?

  1. 캐리오버 약 0.15 % 로 부적합 — 니들 세척 방식 · 세척 바이알의 용매와 액량을 확인하고 블랭크를 다시 주입한다
  2. 캐리오버 약 0.015 % 로 적합 — 그대로 진행한다
  3. 블랭크는 판정 대상이 아니므로 무시한다
  4. 블랭크 파일을 지우고 다시 주입한다
정답 보기① — 4.9 ÷ 3262.0 × 100 ≈ 0.15 % 로 기준을 넘습니다. 캐리오버의 흔한 원인은 니들 바깥에 묻은 시료이므로 ch04 의 니들 세척 설정부터 확인합니다. ② 는 소수점 자리를 잘못 옮긴 값이고, ④ 원자료를 지우는 것은 데이터 무결성 위반입니다.

자주 나는 오류와 해결#

1) 검량 표에서 한 수준만 크게 벗어남#

text
[Calibration] Caffeine: R2 = 0.98712, Level 3 residual +94.6 % (> 15 %)
  • 원인: 시퀀스 표의 수준 · 농도 칸을 엇갈려 넣었거나(예: L3 줄에 농도 25 대신 L4 바이알 위치 5), 바이알 위치가 바뀌어 다른 농도를 주입했습니다.
  • 해결: 시퀀스 표의 해당 줄(위치 · 수준 · 농도)과 <b>편집 › 바이알 준비</b>의 트레이 배치를 대조합니다. 칸만 틀렸으면 시퀀스 표를 고치고 <b>Calibration › Process Sequence</b> 를 다시 합니다(수정 이력 남음). 바이알을 잘못 놓았으면 그 수준을 다시 주입합니다.

2) 시료 결과 칸이 비어 있음 — 화합물을 찾지 못함#

text
[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 표시와 범위 초과 경고#

text
[Quant] Warning: SMP-ENERGY-D2-01 area 5226.8 above highest level (3262.0) - result extrapolated
  • 원인: 시료가 가장 높은 표준물보다 진합니다.
  • 해결: 외삽값을 보고하지 않습니다. 실습 B 처럼 희석 배수를 다시 정해 시료를 새로 묽히고, 시퀀스에 희석 배수를 실제 조제 기록대로 넣어 재분석합니다. 반대로 <Cal 이면 덜 묽히거나, 낮은 수준 표준물을 추가해 범위를 넓힙니다.

4) 결과가 정확히 2배(또는 1/2)로 나옴#

text
SMP-COLA-D2  Final = 194.0 mg/L   (예상 약 97 mg/L)
  • 원인: 희석 배수를 두 번 곱했습니다(시퀀스 표에 2 를 넣었는데 Python · 엑셀에서 Final 에 또 × 2). 반대로 1/2 이면 시퀀스 표 희석 배수 칸을 비워 두고 Amount(바이알 농도)를 최종값으로 썼습니다.
  • 해결: 시뮬레이터 결과 CSV 에서 Amount(바이알) 와 Final(× 희석) 칸을 구분해 읽습니다. 희석 배수는 한 곳(시퀀스 표)에서만 적용합니다.

5) SST 면적 RSD 부적합#

text
[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) 진한 표준물 뒤 블랭크에 카페인 피크 — 캐리오버 초과#

text
[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 근처로 나옴#

text
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 판정표도 함께 냅니다.

  1. 외부표준법에서 결과의 정확도를 가장 직접적으로 좌우하는 장비 성능은? ① 컬럼 온도 표시 정밀도 ② 주입량 재현성 ③ 램프 사용 시간 ④ 폐액 통 크기
  2. 검량 범위가 5 ~ 100 mg/L 이고 × 2 로 묽힌 시료의 바이알 농도가 160 mg/L(외삽)로 나왔습니다. 알맞은 처리는? ① 320 mg/L 로 보고 ② 100 mg/L 로 보고 ③ × 10 정도로 다시 묽혀 재분석 ④ 검량선을 2차식으로 바꿔 계산
  3. a, b = np.polyfit(conc, area, 1) 에서 a 와 b 는? ① 절편, 기울기 ② 기울기, 절편 ③ R², 기울기 ④ 평균, 표준편차
  4. SST 에서 테오필린–카페인 분리도를 보는 이유는? ① 카페인 면적을 크게 만들기 위해 ② 가장 가까운 이웃 피크와 섞이지 않아 면적이 정확한지 확인하기 위해 ③ 이론단수를 계산하기 위해 ④ 캐리오버를 확인하기 위해
  5. 데이터 무결성 원칙에 맞는 행동은? ① 재분석 후 첫 데이터를 지운다 ② 노트의 틀린 값을 수정액으로 지운다 ③ 틀린 값에 한 줄을 긋고 고친 값 · 날짜 · 서명 · 이유를 적는다 ④ SST 에서 나쁜 주입을 빼고 계산한다
정답 보기1-② 외부표준법은 표준물과 시료의 주입량이 같다고 믿고 면적을 비교하므로 주입 재현성이 핵심입니다(SST 면적 RSD). 2-③ 범위 밖 값은 보고하지 않고, 바이알 농도가 범위 가운데(약 32 mg/L)에 오도록 다시 묽힙니다. 3-② polyfit 은 높은 차수 계수부터 돌려주므로 1차식은 [기울기, 절편] 입니다. 4-② 이웃 피크와 겹치면 적분 경계가 흔들려 면적이 틀어집니다. 5-③ 원래 값이 보이게 남기고 수정 이력을 적습니다(ALCOA).
도전 과제 — 브래킷 검량과 감도 변화 (선택, 2인 1조)

시퀀스 끝의 확인 표준 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 · Python statistics: https://docs.python.org/3/library/statistics.html · csv: https://docs.python.org/3/library/csv.html
  • 이 사이트의 도구: Python Playground · 3D 뷰어 · 3D 시뮬레이터
  • 이전 차시: ch06 첫 분석: 카페인 표준물 · 다음 차시: ch08 실무 실험: 조건 최적화와 방법 개발