머신러닝으로 단백질 용해도 예측하기

0. 단백질 용해도 예측

유전자 재조합을 통해 특정 단백질을 과발현시키는 것은 생명과학에서 아주 중요한 일입니다. 그러나 대부분의 과발현 단백질은 inclusion bodies를 형성해 용해도가 매우 낮은 문제가 있습니다. 그렇기 때문에 단백질 용해도를 예측하는 일은 실험의 시행착오를 줄이는데 도움이 됩니다.

여기서는 대장균에서 발현되는 단백질 데이터를 가지고 기계학습으로 용해도를 예측하는 문제를 풀어보겠습니다.

1. eSOL 데이터셋 설명

분석에 사용한 eSOL(Solubility database of all E.coli proteins) 데이터셋은 Targeted Proteins Research Project을 통해 http://tp-esol.genes.nig.ac.jp/ 에 공개 되어있습니다.

공식 페이지에서는 해당 데이터셋에 대해 다음과 같이 설명 합니다.

eSOL is a database on the solubility of entire ensemble E.coli proteins (Niwa T, Ying BW, Saito K, Jin W, Takada S, Ueda T and Taguchi H, 2009) individually synthesized by PURE system that is chaperone free.

대장균(E.coli) 단백질의 용해도(Solubility)는 아래의 공식을 통해 계산되었습니다.

$$ Solubility = \frac{Supernatant fraction}{uncentrifuged fraction} * 100 $$

따라서 주의해야 할 것은 용해도가 동일해도 전체 발현량은 동일하지 않을 수 있습니다.

2. 데이터셋 불러오기

pandas 라이브러리를 사용해 데이터셋을 불러옵니다. 원본 데이터셋은 tsv형식으로 되어 있지만, 편의를 위해 csv형식으로 변경하고 단백질 서열 정보를 추가했습니다.

In [1]:
import pandas as pd

df = pd.read_csv("E_coli_protein.csv")
df.tail()
Out[1]:
Number Gene Solubility Sequence
3143 3148 ytfG 106 MIAITGATGQLGHYVIESLMKTVPASQIVAIVRNPAKAQALAAQGI...
3144 3149 ytfN 32 MSLWKKISLGVVIVILLLLGSVAFLVGTTSGLHLVFKAADRWVPGL...
3145 3150 yzcX 27 MNDSEFHRLADQLWLTIEERLDDWDGDSDIDCEINGGVLTITFENG...
3146 3151 yzfA 88 MRIFVYGSLRHKQGNSHWMTNAQLLGDFSIDNYQLYSLGHYPGAVP...
3147 3152 yzgL 30 MQNRKWILTSLVMTFFGIPILAQFLAVVIAMLGVGLAGIIEVCNIL...

불러온 데이터셋의 마지막 5개 데이터는 위와 같습니다. 데이터셋에는 총 3147개의 단백질의 유전자이름과 서열정보 그리고 용해도값이 들어 있습니다.

3. 추가 데이터 열 만들기

단백질 서열은 상직적(symbolic) 정보입니다. 따라서 를 포함하고 있습니다. 따라서 다양한 분석법을 통해 기계학습에 유용한 특성(feature)를 만들 수 있습니다.

먼저 전체 단백질 서열에서 각각의 아미노산의 비율에 대한 데이터를 추가해봅니다. 파이썬 기본 함수인 count()를 사용합니다.

3.1. 아미노산의 비율

In [2]:
AA = [
    "A",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "V",
    "W",
    "Y",
]

for i in AA:
    df[i] = [x.count(i) / len(x) for x in df["Sequence"]]

이제 Biopython 라이브러리에 있는 도구를 사용해 추가적인 데이터를 만들어 보겠습니다.

3.2. 단백질의 분자량

Biopython.ProteinAnalysis 객체를 사용합니다.

In [3]:
from Bio.SeqUtils.ProtParam import ProteinAnalysis

df["molecular_weight"] = [ProteinAnalysis(x).molecular_weight() for x in df["Sequence"]]

3.3. 다양한 계산값

단백질의 방향족(aromaticity) 아미노산 비율, 불안정성(instability index), 등전점(isoelectric point)등에 관한 값을 추가합니다.

In [4]:
df["aromaticity"] = [ProteinAnalysis(x).aromaticity() for x in df["Sequence"]]
df["instability_index"] = [
    ProteinAnalysis(x).instability_index() for x in df["Sequence"]
]
df["isoelectric_point"] = [
    ProteinAnalysis(x).isoelectric_point() for x in df["Sequence"]
]
df["gravy"] = [ProteinAnalysis(x).gravy() for x in df["Sequence"]]

3.4. 단백질 2차 구조

단백질 2차 구조의 비율에 대한 값을 추가합니다. 현재 단백질 서열을 통해 2차 구조를 예측하는 것은 상당히 정확한 편입니다.

In [5]:
df["structure_fraction_helix"] = [
    ProteinAnalysis(x).secondary_structure_fraction()[0] for x in df["Sequence"]
]
df["structure_fraction_turn"] = [
    ProteinAnalysis(x).secondary_structure_fraction()[1] for x in df["Sequence"]
]
df["structure_fraction_sheet"] = [
    ProteinAnalysis(x).secondary_structure_fraction()[2] for x in df["Sequence"]
]

3.5. 흡광 계수

단백질의 흡광 계수는 주로 농도를 계산하는데 사용되는 값으로 소수성 아미노산과 시스테인(cysteine)의 비율과 관련있습니다.

In [6]:
df["extinction_coefficient_reduced"] = [
    ProteinAnalysis(x).molar_extinction_coefficient()[0] for x in df["Sequence"]
]
df["extinction_coefficient_non_reduced"] = [
    ProteinAnalysis(x).molar_extinction_coefficient()[1] for x in df["Sequence"]
]

3.6. 단백질 유연성 정보

단백질 서열의 각 위치에 대한 아미노산 유연성을 계산합니다. 그런다음 평균값과 표준편차, 중간값을 추가합니다.

In [8]:
import numpy as np

df["flexibility_mean"] = [
    np.mean(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_std"] = [
    np.std(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_median"] = [
    np.median(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]

각 아미노산의 유연성에 대한 조화평균, 기하평균, 최소값, 최대값, 왜도, 첨도값을 추가합니다.

In [10]:
from scipy.stats.mstats import hmean, gmean
from scipy.stats import skew, kurtosis

df["flexibility_hmean"] = [
    hmean(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_gmean"] = [
    gmean(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_min"] = [
    np.min(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_max"] = [
    np.max(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_skew"] = [
    skew(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]
df["flexibility_kurtosis"] = [
    kurtosis(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
]

추가한 데이터를 확인해 봅니다.

In [12]:
df.tail()
Out[12]:
Number Gene Solubility Sequence A C D E F G ... gravy flexibility_mean flexibility_std flexibility_median flexibility_hmean flexibility_gmean flexibility_min flexibility_max flexibility_skew flexibility_kurtosis
3143 3148 ytfG 106 MIAITGATGQLGHYVIESLMKTVPASQIVAIVRNPAKAQALAAQGI... 0.171329 0.000000 0.052448 0.052448 0.017483 0.087413 ... 0.051748 1.000323 0.021540 0.999869 0.999860 1.000092 0.948167 1.054274 0.116313 -0.504896
3144 3149 ytfN 32 MSLWKKISLGVVIVILLLLGSVAFLVGTTSGLHLVFKAADRWVPGL... 0.066720 0.003177 0.065925 0.054805 0.023828 0.090548 ... -0.182685 1.003100 0.024786 1.002798 1.002490 1.002794 0.932905 1.079036 0.175824 -0.293648
3145 3150 yzcX 27 MNDSEFHRLADQLWLTIEERLDDWDGDSDIDCEINGGVLTITFENG... 0.047170 0.018868 0.113208 0.094340 0.047170 0.084906 ... -0.591509 1.001490 0.022863 1.000571 1.000967 1.001229 0.951655 1.048607 -0.084948 -0.807064
3146 3151 yzfA 88 MRIFVYGSLRHKQGNSHWMTNAQLLGDFSIDNYQLYSLGHYPGAVP... 0.061947 0.000000 0.070796 0.035398 0.017699 0.115044 ... -0.514159 0.995326 0.020807 0.995381 0.994891 0.995108 0.951667 1.043298 -0.000384 -0.537425
3147 3152 yzgL 30 MQNRKWILTSLVMTFFGIPILAQFLAVVIAMLGVGLAGIIEVCNIL... 0.075269 0.021505 0.010753 0.021505 0.086022 0.107527 ... 1.395699 0.969298 0.019198 0.963935 0.968922 0.969109 0.930988 1.022298 0.639452 -0.070879

5 rows × 43 columns

원래 4개의 행으로 구성되어 있던 데이터셋에 39개의 행이 추가되어 총 43개 행을 갖게 되었습니다. 수치로 되어있는 행에 대한 산술 통계값을 살펴봅니다.

In [11]:
df.describe()
Out[11]:
Number Solubility A C D E F G H I ... gravy flexibility_mean flexibility_std flexibility_median flexibility_hmean flexibility_gmean flexibility_min flexibility_max flexibility_skew flexibility_kurtosis
count 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 ... 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000 3148.000000
mean 1574.665502 49.002541 0.093592 0.012910 0.053903 0.061896 0.036498 0.070578 0.023977 0.058930 ... -0.147113 0.997623 0.024610 0.997044 0.997011 0.997317 0.940615 1.060624 0.105105 -0.541406
std 909.158084 33.398919 0.027916 0.011823 0.017879 0.022727 0.016239 0.023758 0.013145 0.020419 ... 0.337310 0.006706 0.002267 0.007453 0.006718 0.006712 0.008393 0.010076 0.174546 0.208449
min 1.000000 0.000000 0.012500 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 ... -1.712281 0.954846 0.015813 0.947970 0.954504 0.954674 0.912095 1.000571 -0.783561 -1.355014
25% 787.750000 18.000000 0.075758 0.005277 0.043011 0.047986 0.025890 0.055265 0.014509 0.044915 ... -0.337482 0.995471 0.023156 0.994565 0.994902 0.995187 0.935369 1.054357 0.010429 -0.664991
50% 1574.500000 43.000000 0.091882 0.011033 0.054054 0.061473 0.034617 0.069767 0.022788 0.057020 ... -0.182439 0.998133 0.024376 0.997607 0.997534 0.997832 0.941119 1.060905 0.104496 -0.552774
75% 2361.250000 79.000000 0.110102 0.017544 0.064695 0.074736 0.045304 0.085745 0.031821 0.070130 ... -0.020990 1.001112 0.025751 1.000841 1.000496 1.000800 0.945979 1.067405 0.198835 -0.435561
max 3152.000000 147.000000 0.308789 0.102857 0.166667 0.197531 0.149254 0.237288 0.132653 0.185185 ... 1.889655 1.035619 0.042443 1.036857 1.035343 1.035481 0.994155 1.100286 1.039824 0.970267

8 rows × 41 columns

위 테이블을 통해 용해도의 표준편차값이 높다는 것과 생성한 데이터들이 음수값도 포함하고 있다는 것을 알 수 있습니다.

3.7. 데이터 행 정렬

데이터 처리의 편의를 위해 Solubility, Sequence 행의 위치를 변경합니다.

In [13]:
df.columns.values
Out[13]:
array(['Number', 'Gene', 'Solubility', 'Sequence', 'A', 'C', 'D', 'E',
       'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T',
       'V', 'W', 'Y', 'molecular_weight', 'aromaticity',
       'instability_index', 'isoelectric_point',
       'structure_fraction_helix', 'structure_fraction_turn',
       'structure_fraction_sheet', 'extinction_coefficient_reduced',
       'extinction_coefficient_non_reduced', 'gravy', 'flexibility_mean',
       'flexibility_std', 'flexibility_median', 'flexibility_hmean',
       'flexibility_gmean', 'flexibility_min', 'flexibility_max',
       'flexibility_skew', 'flexibility_kurtosis'], dtype=object)
In [14]:
column_titles = [
    "Number",
    "Gene",
    "A",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "V",
    "W",
    "Y",
    "molecular_weight",
    "aromaticity",
    "instability_index",
    "isoelectric_point",
    "structure_fraction_helix",
    "structure_fraction_turn",
    "structure_fraction_sheet",
    "extinction_coefficient_reduced",
    "extinction_coefficient_non_reduced",
    "gravy",
    "flexibility_mean",
    "flexibility_std",
    "flexibility_median",
    "flexibility_hmean",
    "flexibility_gmean",
    "flexibility_min",
    "flexibility_max",
    "flexibility_skew",
    "flexibility_kurtosis",
    "Sequence",
    "Solubility",
]

df = df.reindex(columns=column_titles)
In [15]:
df.tail()
Out[15]:
Number Gene A C D E F G H I ... flexibility_std flexibility_median flexibility_hmean flexibility_gmean flexibility_min flexibility_max flexibility_skew flexibility_kurtosis Sequence Solubility
3143 3148 ytfG 0.171329 0.000000 0.052448 0.052448 0.017483 0.087413 0.027972 0.052448 ... 0.021540 0.999869 0.999860 1.000092 0.948167 1.054274 0.116313 -0.504896 MIAITGATGQLGHYVIESLMKTVPASQIVAIVRNPAKAQALAAQGI... 106
3144 3149 ytfN 0.066720 0.003177 0.065925 0.054805 0.023828 0.090548 0.007149 0.050040 ... 0.024786 1.002798 1.002490 1.002794 0.932905 1.079036 0.175824 -0.293648 MSLWKKISLGVVIVILLLLGSVAFLVGTTSGLHLVFKAADRWVPGL... 32
3145 3150 yzcX 0.047170 0.018868 0.113208 0.094340 0.047170 0.084906 0.028302 0.075472 ... 0.022863 1.000571 1.000967 1.001229 0.951655 1.048607 -0.084948 -0.807064 MNDSEFHRLADQLWLTIEERLDDWDGDSDIDCEINGGVLTITFENG... 27
3146 3151 yzfA 0.061947 0.000000 0.070796 0.035398 0.017699 0.115044 0.035398 0.044248 ... 0.020807 0.995381 0.994891 0.995108 0.951667 1.043298 -0.000384 -0.537425 MRIFVYGSLRHKQGNSHWMTNAQLLGDFSIDNYQLYSLGHYPGAVP... 88
3147 3152 yzgL 0.075269 0.021505 0.010753 0.021505 0.086022 0.107527 0.000000 0.118280 ... 0.019198 0.963935 0.968922 0.969109 0.930988 1.022298 0.639452 -0.070879 MQNRKWILTSLVMTFFGIPILAQFLAVVIAMLGVGLAGIIEVCNIL... 30

5 rows × 43 columns

3.8. 데이터셋 파일로 저장

아래 명령어를 통해 데이터셋을 파일로 저장 할 수 있습니다.

In [16]:
# df.to_csv("Engineered_E_coli_protein.csv", mode='w', index=False)

4. 시각화로 EDA 하기

간단한 시각화를 통해 데이터셋이 어떻게 분포되어 있는지 확인합니다.

4.1. Scatter plot

In [17]:
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

sns.scatterplot(
    y="Solubility",
    x="molecular_weight",
    linewidth=0.1,
    alpha=0.5,
    sizes=(1, 8),
    data=df,
)
Out[17]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f3e74620048>
No description has been provided for this image

이미 알려져있는것 처럼 단백질의 크기가 클수록 용해도가 낮다는 것을 시각화를 통해 확인할 수 있습니다. 그러나 Scatter plot에는 겹쳐진 데이터가 너무 많기때문에 더 보기 좋은 그림을 그려봅니다.

4.2. Kernel density estimation plot

In [18]:
sns.jointplot(x="molecular_weight", y="Solubility", data=df, kind="kde")
Out[18]:
<seaborn.axisgrid.JointGrid at 0x7f3e7456be10>
No description has been provided for this image

위 시각화를 통해 대부분의 단백질의 크기는 30kDa 주변에 몰려있으며, 용해도는 20%와 80% 그룹으로 나뉜다는 것을 알 수 있습니다. 또한 상대적으로 높은 용해도의 단백질은 25kDa 이하의 크기입니다.

5. 데이터 전처리

기계학습을 위해 데이터셋을 특성(feature)값과 결과값으로 분리합니다.

In [19]:
df_y = df["Solubility"]

df_x = df.iloc[:, 2:-2]
df_x.columns
Out[19]:
Index(['A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q',
       'R', 'S', 'T', 'V', 'W', 'Y', 'molecular_weight', 'aromaticity',
       'instability_index', 'isoelectric_point', 'structure_fraction_helix',
       'structure_fraction_turn', 'structure_fraction_sheet',
       'extinction_coefficient_reduced', 'extinction_coefficient_non_reduced',
       'gravy', 'flexibility_mean', 'flexibility_std', 'flexibility_median',
       'flexibility_hmean', 'flexibility_gmean', 'flexibility_min',
       'flexibility_max', 'flexibility_skew', 'flexibility_kurtosis'],
      dtype='object')

5.1. 특성값 정규화하기

정규화를 통해 기계 학습 모델이 특정 특성값에 바이어스(Bias)를 갖지 않게 합니다.

In [20]:
from sklearn import preprocessing
from sklearn.model_selection import train_test_split

scaler = preprocessing.StandardScaler().fit(df_x)
X_scaled = scaler.transform(df_x)
X_train, X_test, y_train, y_test = train_test_split(
    X_scaled, df_y, test_size=0.2, random_state=42
)

6. 기계학습하기

6.1. 다양한 기계 모델 비교

간단한 선형모델부터 다양한 기계 학습 모델을 사용해보고 평가 지표를 통해 가장 좋은 모델을 찾습니다. 예측의 정확도를 평가하기 위해 MAPE(Mean absolute percentage error)를 평가지표로 사용했습니다.

In [21]:
from sklearn.linear_model import LinearRegression
import numpy as np

lin_reg = LinearRegression(n_jobs=-1)  # -1 means use all cpu core
lin_reg.fit(X_train, y_train)
Out[21]:
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=-1, normalize=False)
In [23]:
from sklearn.metrics import mean_absolute_error


def evaluate(model, X_test, y_test):
    y_pred = model.predict(X_test)
    mape = mean_absolute_error(y_test, y_pred)
    accuracy = 100 - mape
    return accuracy


accuracy = evaluate(lin_reg, X_test, y_test)
print(f"Accuracy = {accuracy:.2f} %")
Accuracy = 77.47 %

선형모델을 사용했을때 예측의 정확도는 약 77% 입니다.

In [24]:
from sklearn.tree import DecisionTreeRegressor

tree_reg = DecisionTreeRegressor()
tree_reg.fit(X_train, y_train)

accuracy = evaluate(tree_reg, X_test, y_test)
print(f"Accuracy = {accuracy:.2f} %")
Accuracy = 72.87 %
In [25]:
from sklearn.svm import SVR

svm_reg = SVR(kernel="linear")
svm_reg.fit(X_train, y_train)

accuracy = evaluate(svm_reg, X_test, y_test)
print(f"Accuracy = {accuracy:.2f} %")
Accuracy = 77.65 %
In [26]:
from sklearn.ensemble import RandomForestRegressor

forest_reg = RandomForestRegressor(n_estimators=1000, n_jobs=-1)
forest_reg.fit(X_train, y_train)

accuracy = evaluate(forest_reg, X_test, y_test)
print(f"Accuracy = {accuracy:.2f} %")
Accuracy = 79.42 %
In [27]:
from sklearn.ensemble import GradientBoostingRegressor

gbr = GradientBoostingRegressor()
gbr.fit(X_train, y_train)

accuracy = evaluate(gbr, X_test, y_test)
print(f"Accuracy = {accuracy:.2f} %")
# print(f'Score = {gbr.score(X_train, y_train)}') # R^2 socre
Accuracy = 79.48 %
In [28]:
# explicitly require this experimental feature
from sklearn.ensemble import HistGradientBoostingRegressor

hgr = HistGradientBoostingRegressor()
hgr.fit(X_train, y_train)

accuracy = evaluate(hgr, X_test, y_test)
print(f"Accuracy = {accuracy:.2f} %")
# print(f'Score = {hgr.score(X_train, y_train)}') # R^2 socre
Accuracy = 79.95 %

가장 최근에 추가된 기능인 HistGradientBoostingRegressor이 가장 좋은 성능을 보여줍니다.

6.2. 특성의 중요도

이제 데이터의 어느 특성이 모델 예측에 중요한지 살펴보겠습니다. 간단한 시각화로 막대그래프를 그려봅니다.

In [40]:
feature_importances = gbr.feature_importances_
plt.bar(range(len(feature_importances)), feature_importances)
Out[40]:
<BarContainer object of 39 artists>
No description has been provided for this image

위 그림을 통해 예측에 영향을 주는 특성을 알 수 있습니다. 상위 3가지를 알아보면 다음과 같습니다.

  1. 분자량(21번째 특성)
  2. pI값(29번째 특성)
  3. 분광계수(24번째 특성)

7. 모델의 매개 변수 최적화

GridSearchCV를 사용해 가장 좋은 성능을 나타내는 매개변수 값을 찾아봅니다.

In [35]:
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import make_scorer


def evaluate(y_true, y_pred):
    mape = mean_absolute_error(y_true, y_pred)
    accuracy = 100 - mape
    return accuracy


score = make_scorer(evaluate, greater_is_better=True)

param_grid = [
    {
        "learning_rate": [0.01, 0.1],
        "max_depth": [10, 50, 100],
        "min_samples_leaf": [10, 50, 100],
    }
]

hgr_reg = HistGradientBoostingRegressor(random_state=42)
grid_search = GridSearchCV(hgr_reg, param_grid, cv=5, scoring=score, n_jobs=-1)
grid_search.fit(X_train, y_train)
Out[35]:
GridSearchCV(cv=5, error_score='raise-deprecating',
             estimator=HistGradientBoostingRegressor(l2_regularization=0.0,
                                                     learning_rate=0.1,
                                                     loss='least_squares',
                                                     max_bins=256,
                                                     max_depth=None,
                                                     max_iter=100,
                                                     max_leaf_nodes=31,
                                                     min_samples_leaf=20,
                                                     n_iter_no_change=None,
                                                     random_state=42,
                                                     scoring=None, tol=1e-07,
                                                     validation_fraction=0.1,
                                                     verbose=0),
             iid='warn', n_jobs=-1,
             param_grid=[{'learning_rate': [0.01, 0.1],
                          'max_depth': [10, 50, 100],
                          'min_samples_leaf': [10, 50, 100]}],
             pre_dispatch='2*n_jobs', refit=True, return_train_score=False,
             scoring=make_scorer(evaluate), verbose=0)
In [36]:
cvres = grid_search.cv_results_
for mean_score, params in zip(cvres["mean_test_score"], cvres["params"]):
    print(f"Score = {mean_score:.2f}, Params = {params} ")
Score = 77.32, Params = {'learning_rate': 0.01, 'max_depth': 10, 'min_samples_leaf': 10} 
Score = 77.28, Params = {'learning_rate': 0.01, 'max_depth': 10, 'min_samples_leaf': 50} 
Score = 76.85, Params = {'learning_rate': 0.01, 'max_depth': 10, 'min_samples_leaf': 100} 
Score = 77.29, Params = {'learning_rate': 0.01, 'max_depth': 50, 'min_samples_leaf': 10} 
Score = 77.29, Params = {'learning_rate': 0.01, 'max_depth': 50, 'min_samples_leaf': 50} 
Score = 76.85, Params = {'learning_rate': 0.01, 'max_depth': 50, 'min_samples_leaf': 100} 
Score = 77.29, Params = {'learning_rate': 0.01, 'max_depth': 100, 'min_samples_leaf': 10} 
Score = 77.29, Params = {'learning_rate': 0.01, 'max_depth': 100, 'min_samples_leaf': 50} 
Score = 76.85, Params = {'learning_rate': 0.01, 'max_depth': 100, 'min_samples_leaf': 100} 
Score = 80.19, Params = {'learning_rate': 0.1, 'max_depth': 10, 'min_samples_leaf': 10} 
Score = 80.18, Params = {'learning_rate': 0.1, 'max_depth': 10, 'min_samples_leaf': 50} 
Score = 80.45, Params = {'learning_rate': 0.1, 'max_depth': 10, 'min_samples_leaf': 100} 
Score = 80.25, Params = {'learning_rate': 0.1, 'max_depth': 50, 'min_samples_leaf': 10} 
Score = 80.17, Params = {'learning_rate': 0.1, 'max_depth': 50, 'min_samples_leaf': 50} 
Score = 80.39, Params = {'learning_rate': 0.1, 'max_depth': 50, 'min_samples_leaf': 100} 
Score = 80.25, Params = {'learning_rate': 0.1, 'max_depth': 100, 'min_samples_leaf': 10} 
Score = 80.17, Params = {'learning_rate': 0.1, 'max_depth': 100, 'min_samples_leaf': 50} 
Score = 80.39, Params = {'learning_rate': 0.1, 'max_depth': 100, 'min_samples_leaf': 100} 

최고의 성능을 보인 모델의 정확도는 아래와 같습니다.

In [37]:
grid_search.best_score_
Out[37]:
80.4455794771026

매개변수 값이 아래와 같을때 최고의 성능을 보여주었습니다.

In [38]:
grid_search.best_params_
Out[38]:
{'learning_rate': 0.1, 'max_depth': 10, 'min_samples_leaf': 100}

8. 마치며

생성한 기계 학습 모델에 임의의 단백질 서열을 넣어 용해도를 예측해보겠습니다. 임의의 서열 2가지를 csv파일에 넣어 데이터 전처리과정을 동일하게 거칩니다.

In [42]:
df2 = pd.read_csv("sol_predict.csv")
df2
Out[42]:
Number Gene Sequence
0 1 random MAMIHHSAVQLSHGQTIMSGGQREGKSTLVQSIKVLTKQQQYPAPC...
1 2 DPO1_THEAQ MRGMLPLFEPKGRVLLVDGHHLAYRTFHALKGLTTSRGEPVQAVYG...
In [43]:
def feature_eng(df):
    for i in AA:
        df[i] = [x.count(i) / len(x) for x in df["Sequence"]]
    df["molecular_weight"] = [
        ProteinAnalysis(x).molecular_weight() for x in df["Sequence"]
    ]
    df["aromaticity"] = [ProteinAnalysis(x).aromaticity() for x in df["Sequence"]]
    df["instability_index"] = [
        ProteinAnalysis(x).instability_index() for x in df["Sequence"]
    ]
    df["isoelectric_point"] = [
        ProteinAnalysis(x).isoelectric_point() for x in df["Sequence"]
    ]
    df["gravy"] = [ProteinAnalysis(x).gravy() for x in df["Sequence"]]
    df["structure_fraction_helix"] = [
        ProteinAnalysis(x).secondary_structure_fraction()[0] for x in df["Sequence"]
    ]
    df["structure_fraction_turn"] = [
        ProteinAnalysis(x).secondary_structure_fraction()[1] for x in df["Sequence"]
    ]
    df["structure_fraction_sheet"] = [
        ProteinAnalysis(x).secondary_structure_fraction()[2] for x in df["Sequence"]
    ]
    df["extinction_coefficient_reduced"] = [
        ProteinAnalysis(x).molar_extinction_coefficient()[0] for x in df["Sequence"]
    ]
    df["extinction_coefficient_non_reduced"] = [
        ProteinAnalysis(x).molar_extinction_coefficient()[1] for x in df["Sequence"]
    ]
    df["flexibility_mean"] = [
        np.mean(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_std"] = [
        np.std(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_median"] = [
        np.median(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_hmean"] = [
        hmean(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_gmean"] = [
        gmean(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_min"] = [
        np.min(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_max"] = [
        np.max(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_skew"] = [
        skew(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    df["flexibility_kurtosis"] = [
        kurtosis(ProteinAnalysis(x).flexibility()) for x in df["Sequence"]
    ]
    return df


df2 = feature_eng(df2)
df2
Out[43]:
Number Gene Sequence A C D E F G H ... extinction_coefficient_non_reduced flexibility_mean flexibility_std flexibility_median flexibility_hmean flexibility_gmean flexibility_min flexibility_max flexibility_skew flexibility_kurtosis
0 1 random MAMIHHSAVQLSHGQTIMSGGQREGKSTLVQSIKVLTKQQQYPAPC... 0.025000 0.015 0.030000 0.030000 0.035000 0.070000 0.055000 ... 64065 0.995175 0.025666 0.995929 0.994514 0.994844 0.94081 1.061024 0.103835 -0.531963
1 2 DPO1_THEAQ MRGMLPLFEPKGRVLLVDGHHLAYRTFHALKGLTTSRGEPVQAVYG... 0.109375 0.000 0.050481 0.104567 0.032452 0.069712 0.021635 ... 112760 1.000747 0.024468 0.999750 1.000151 1.000448 0.93856 1.077405 0.169728 -0.601805

2 rows × 42 columns

In [49]:
column_titles = [
    "Number",
    "Gene",
    "A",
    "C",
    "D",
    "E",
    "F",
    "G",
    "H",
    "I",
    "K",
    "L",
    "M",
    "N",
    "P",
    "Q",
    "R",
    "S",
    "T",
    "V",
    "W",
    "Y",
    "molecular_weight",
    "aromaticity",
    "instability_index",
    "isoelectric_point",
    "structure_fraction_helix",
    "structure_fraction_turn",
    "structure_fraction_sheet",
    "extinction_coefficient_reduced",
    "extinction_coefficient_non_reduced",
    "gravy",
    "flexibility_mean",
    "flexibility_std",
    "flexibility_median",
    "flexibility_hmean",
    "flexibility_gmean",
    "flexibility_min",
    "flexibility_max",
    "flexibility_skew",
    "flexibility_kurtosis",
    "Sequence",
]

df2 = df2.reindex(columns=column_titles)
df_x2 = df2.iloc[:, 2:-1]
X2_scaled = scaler.transform(df_x2)

이제 기계학습 모델을 사용해 용해도값을 예측해봅니다.

In [61]:
pred_sol = grid_search.best_estimator_.predict(X2_scaled)
gene = ["random", "DPO1_THEAQ"]
for idx, val in enumerate(gene):
    print(f"Predicted solubility of {val} is {pred_sol[idx]:.2f}%")
Predicted solubility of random is 28.88%
Predicted solubility of DPO1_THEAQ is 46.57%

위 코드를 통해 각각의 서열에 대한 용해도값을 예측 할 수 있었지만 실제값과 동일한지는 실제 실험을 해봐야할 것입니다.