컬럼의 DBC 측정하기

1. DBC(dynamic binding capacity)란 무엇인가?

DBC는 단백질 정제에서 크로마토그래피 컬럼의 특정 실험 조건에 결합 할 수 있는 표적 단백질의 최대 양을 나타냅니다. 그러나 DBC는 유속이나 버퍼의 조성 및 단백질 시료에 따라 달라지기 때문에 실험 조건에 따라 달라 집니다.

그렇기 때문에 단백질 정제에서 적합한 레진을 선택하기 위해서는 DBC에 대한 정보가 매우 중요합니다. 보통 레진의 제조사는 일반적인 단백질 정제 조건에서의 DBC 정보를 제공하고는 있지만 특정 단백질에 대한 최상의 레진 및 실험 조건을 찾기 위해 직접 DBC를 측정해야 할 일 이 생깁니다.

1.1. breakthrough 곡선

image from cytiva

breakthrough 곡선은 컬럼에 결합하지 못하고 흘러 나온 단백질의 양을 그래프로 표현한 것입니다. 그리고 전체의 10%의 단백질이 흘러나온 경우를 QB10 값이라 합니다.

1.2. DBC 계산법

image from cytiva

위 그래프의 파란색 영역은 지연 볼륨(delay volume)으로 사용한 컬럼의 볼륨과 AKTA 시스템에 의해 발생합니다. 주황색 영역은 비 결합 단백질의 양을 나타냅니다. 파란색 곡선은 단백질 breakthrough를 나타내는 것으로 녹색 영역은 X% breakthrough에서 누출 된 단백질의 양을 나타냅니다. X% 에서의 총 DBC는 회색 영역으로 표시됩니다.

위 그림을 예시로 Aoffset은 50 mAu이고 Amax는 1000 mAu 이라는 것을 알 수 있습니다.

50 ml(Vx)에서 측정된 UV 흡광도는 126 mAu 이지만 아래의 계산을 통해 실제 값은 76 mAu라고 생각할 수 있습니다.

A- Aoffset = 126 – 50 = 76 mAu

그리고 최대 UV 흡광도도 마찬가지로 950 mAu가 될 것입니다.

Amax - Aoffset = 1000 – 50 = 950 mAu

따라서 breakthrough의 비율은

(A - Aoffset) / (Amax - Aoffset) = Percentage of breakthrough

를 통해 계산합니다. 결과적으로 50ml에서 breakthrough의 비율은 8%가 됩니다.

% breakthrough = 76 / 950 = 8%

2. DBC 계산하기

실제 실험을 통해 얻은 데이터를 통해 DBC(QB10)을 계산해보도록 하겠습니다.

2.1. 실험 데이터 불러오기

In [1]:
import pandas as pd
import matplotlib.pyplot as plt

%matplotlib inline
import numpy as np
from scipy.optimize import curve_fit
from pynverse import inversefunc

delayed_vol = 2.0  # 컬럼을 거치는데 필요한 부피(ml)
protein_conc = 3.97  # 사용한 단백질의 농도(mg/ml)

df = pd.read_csv("0827_ProteinA_DBC.csv")
df
Out[1]:
Residence_time Volume(ml) Breakthrough(%)
0 3 13.498873 2.998795
1 3 17.998690 4.517550
2 3 22.498837 12.963373
3 3 26.998789 29.624571
4 3 31.499197 49.684577
5 3 35.999281 60.876402
6 3 40.499182 75.469976
7 3 44.999430 82.386165
8 3 49.499411 87.710916
9 3 53.999578 87.831578
10 4 13.498536 2.192587
11 4 17.998536 2.716336
12 4 22.498784 5.259035
13 4 26.999043 14.780687
14 4 31.498960 31.632741
15 4 35.999253 48.004374
16 4 40.499124 65.935281
17 4 44.999433 77.252233
18 4 49.499291 84.863446
19 4 53.999555 88.617977
In [2]:
# 테이블 두개로 분리하기
df1 = df[df["Residence_time"] == 3]
df2 = df[df["Residence_time"] == 4]
df1
Out[2]:
Residence_time Volume(ml) Breakthrough(%)
0 3 13.498873 2.998795
1 3 17.998690 4.517550
2 3 22.498837 12.963373
3 3 26.998789 29.624571
4 3 31.499197 49.684577
5 3 35.999281 60.876402
6 3 40.499182 75.469976
7 3 44.999430 82.386165
8 3 49.499411 87.710916
9 3 53.999578 87.831578

2.2. 전체 데이터 시각화

시각화를 통해 전체 데이터의 모습을 확인합니다.

In [3]:
plt.plot(df1["Volume(ml)"], df1["Breakthrough(%)"], label="Residence_time=3")
plt.plot(df2["Volume(ml)"], df2["Breakthrough(%)"], label="Residence_time=4")
plt.ylabel("Breakthrough(%)")
plt.xlabel("Volume(ml)")
plt.legend()
Out[3]:
<matplotlib.legend.Legend at 0x7eff93892670>
No description has been provided for this image

2.3. 그래프 피팅하기

시그모이드 함수 정의하고 curvefit으로 피팅하기

In [5]:
def sigmoid(x, L, x0, k, b):
    y = L / (1 + np.exp(-k * (x - x0))) + b
    return y


def fitting(xdata, ydata):
    p0 = [max(ydata), np.median(xdata), 1, min(ydata)]
    popt, pcov = curve_fit(sigmoid, xdata, ydata, p0, method="dogbox")
    residuals = ydata - sigmoid(xdata, *popt)
    ss_res = np.sum(residuals**2)
    ss_tot = np.sum((ydata - np.mean(ydata)) ** 2)
    r_squared = 1 - (ss_res / ss_tot)
    return xdata, ydata, popt, r_squared


Run1 = fitting(df1["Volume(ml)"], df1["Breakthrough(%)"])
Run2 = fitting(df2["Volume(ml)"], df2["Breakthrough(%)"])

2.3.1. Residence time 3분 DBC 계산

피팅이 올바른지 그림으로 확인한다.

In [8]:
x_fit = np.linspace(1, 60, 100)
y_fit = sigmoid(x_fit, *Run1[2])
plt.plot(Run1[0], Run1[1], "o", label="data", color="k")
plt.plot(x_fit, y_fit, label="fit", color="r")
plt.text(40, 20, s=f"r_squared={Run1[3]:.3f}", fontsize=10)
plt.ylabel("Breakthrough(%)")
plt.xlabel("Volume(ml)")
plt.legend()
Out[8]:
<matplotlib.legend.Legend at 0x7eff93853520>
No description has been provided for this image
In [9]:
qb10_run1 = inversefunc(sigmoid, args=tuple(Run1[2]), y_values=10)
print(f"10% breakthrough 일때 fraction(ml)의 값 {qb10_run1:.3f} ml 이고")
dbc_run1 = (qb10_run1 - delayed_vol) * protein_conc
print(f"따라서 DBC(QB10)의 값은 {dbc_run1:.3f} mg/ml 이다.")
10% breakthrough 일때 fraction(ml)의 값 20.363 ml 이고
따라서 DBC(QB10)의 값은 72.902 mg/ml 이다.

2.3.1. Residence time 4분 DBC 계산

앞서 사용한 방법과 동일하게 진행한다.

In [10]:
x_fit = np.linspace(1, 60, 100)
y_fit = sigmoid(x_fit, *Run2[2])
plt.plot(Run2[0], Run2[1], "o", label="data", color="k")
plt.plot(x_fit, y_fit, label="fit", color="r")
plt.text(40, 20, s=f"r_squared={Run2[3]:.3f}", fontsize=10)
plt.ylabel("Breakthrough(%)")
plt.xlabel("Volume(ml)")
plt.legend()
Out[10]:
<matplotlib.legend.Legend at 0x7eff93881640>
No description has been provided for this image
In [11]:
qb10_run2 = inversefunc(sigmoid, args=tuple(Run2[2]), y_values=10)
print(f"10% breakthrough 일때 fraction(ml)의 값 {qb10_run2:.3f} ml 이고")
dbc_run2 = (qb10_run2 - delayed_vol) * protein_conc
print(f"따라서 DBC(QB10)의 값은 {dbc_run2:.3f} mg/ml 이다.")
10% breakthrough 일때 fraction(ml)의 값 24.400 ml 이고
따라서 DBC(QB10)의 값은 88.928 mg/ml 이다.

3. 결론

Residence time은 평균적으로 단백질 시료가 정제 컬럼에 체류하는 시간의 값 입니다. 따라서 평균 체류 시간이 길수록 DBC는 증가할 것으로 예측할 수 있습니다. 위의 예시를 통해서도 3분일때는 72.902 mg/ml, 4분일때는 88.928 mg/ml 이라는 확인 할 수 있습니다.

3.1. 참고 자료