TMB(肿瘤突变负荷) panel基因筛选

TMB背景知识介绍

免疫疗法是近年来肿瘤治疗的热点,肿瘤突变负荷(Tumor Mutational Burden,TMB)可以作为免疫疗法的biomarker,TMB高的患者更有可能在免疫治疗中获益。
全外显子测序(WES)是评价TMB的金标准,但是价格还较高,限制了其在临床上的应用。目前已有研究和商品基于panel内基因,发现其和WES水平的TMB具有高度的一致性,可以用来评价临床的TMB,但是这些报道均没有提及如何从人类近30000个基因中筛选出几百个基因组成panel。我们的目的就在于建立一种有效的基因筛选方法,使得到的panel能够很好的评价TMB。

TMB计算

从TCGA数据库下载了555例肺癌患者的WES突变数据,整理后数据结构如下:

Sample Gene1 Gene2 ... Genep total_gene_num
sample1 3 2 ... 0 113
sample2 2 0 ... 4 96
... ... ... ... ... ...
samplen 1 5 ... 9 1412

统计每一个样本中每一个基因的突变数目,它在WES水平上的突变数目就是所有基因突变数目求和,即total_gene_num。

因此我们的任务就是:从p个基因中挑选出一定数目的基因,通过这些基因能够很好地预测total_gene_num,现有的研究一般是选择两者的相关系数作为衡量的指标。比如:选择了Gene1、Gene2和Genen,对这3个基因的突变数目求和,然后计算r2_score([5, 6, 15], [113, 96, 1412]),如果R^2值高,这说明选择的基因好。

机器学习

对于一组data,可以看作是以下形式:
Y = f(X) + \epsilon
其中Y是响应变量,X是自变量,\epsilon是随机误差,f是真实的关系

而机器学习能做的是:
\bar{Y} = h(X)
其中\bar{Y}Y的预测,h是假设的模型
我们要做的就是找到一个合适的h,来进行学习。

Lasso

一般来说,f是复杂的,未知的,不管是为了求解方便,还是我们的能力有限,h都和真实的f有差距。“所有的模型都是错的,但有的模型是有用的”,我们不一定能真正地找到一个正确的h,但是我们能够做出较好地预测,这就够了。
幸运的是,在我们要研究的TMB panel基因筛选的问题中,真实的f是已知的!!!
在上面对TMB计算的描述中:
{total\_gene\_num}_{1} = 1 \times Gene_{1} + 1 \times Gene_{2} + ... + 1 \times Gene_{p}
{total\_gene\_num}_{2} = 1 \times Gene_{1} + 1 \times Gene_{2} + ... + 1 \times Gene_{p}
{total\_gene\_num}_{n} = 1 \times Gene_{1} + 1 \times Gene_{2} + ... + 1 \times Gene_{p}
真实的模型就是一个截距项(intercept)为0,所有系数(coefficient)为1的线性模型,由于特征数p >> 样本数n,所以使用最小二乘法是无法对方程组求解的。

我们的目的是对这个模型做特征选择,尽可能多地去掉特征,仅保留几百个基因。由此自然就想到了L_1正则化的Lasso模型。通过调节\alpha,它可以尽可能地将更多的coef压缩为0,达到特征筛选的目的。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from sklearn import linear_model
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.model_selection import train_test_split, StratifiedShuffleSplit
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GridSearchCV

读入数据

file = "E:/patent/mutation.num.stat.xls"
df = pd.read_csv(file, sep="\t")

分层划分数据集

new_df = df.copy()
new_df["cat"] = np.ceil(new_df["MutNum_exome"] / 1.5)
new_df["cat"].where(new_df["cat"] < 5, 5.0, inplace=True)
split = StratifiedShuffleSplit(n_splits=1, test_size=0.2, random_state=1412)
for train_index, test_index in split.split(new_df, new_df["cat"]):
    strat_train_set = new_df.iloc[train_index]
    strat_test_set = new_df.iloc[test_index]

将数据集转换为ndarray

X_train = strat_train_set[df.columns[3:]]
y_train = strat_train_set["MutNum_exome"]

X_test = strat_test_set[df.columns[3:]]
y_test = strat_test_set["MutNum_exome"]

网格搜索确定参数

param_grid = [
    {
        "fit_intercept": [False, True],
        "positive": [True, False],
        "selection": ["random"],
        "alpha": [0.1, 0.2, 0.3, 0.4, 0.5],
        "max_iter": [3000],
    }
]

model = linear_model.Lasso()

grid_search = GridSearchCV(
    model,
    param_grid,
    cv=10,
    scoring="neg_mean_squared_error"
)

grid_search.fit(X_train, y_train)
F:\Program_Files\Anaconda\lib\site-packages\sklearn\model_selection\_search.py:841: DeprecationWarning: The default of the `iid` parameter will change from True to False in version 0.22 and will be removed in 0.24. This will change numeric results when test-set sizes are unequal.

  DeprecationWarning)

GridSearchCV(cv=10, error_score='raise-deprecating',
      estimator=Lasso(alpha=1.0, copy_X=True, fit_intercept=True, max_iter=1000,
  normalize=False, positive=False, precompute=False, random_state=None,
  selection='cyclic', tol=0.0001, warm_start=False),
      fit_params=None, iid='warn', n_jobs=None,
      param_grid=[{'fit_intercept': [False, True], 'positive': [True, False], 'selection': ['random'], 'alpha': [0.1, 0.2, 0.3, 0.4, 0.5], 'max_iter': [3000]}],
      pre_dispatch='2*n_jobs', refit=True, return_train_score='warn',
      scoring='neg_mean_squared_error', verbose=0)

最优参数

grid_search.best_params_
{'alpha': 0.1,
'fit_intercept': False,
'max_iter': 3000,
'positive': True,
'selection': 'random'}

从最优参数可以看出,模型是符合我们预期的:不需要拟合截距,所有的系数都为正值。

拟合结果

y_test_pred = grid_search.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_test_pred))
best_alpha = grid_search.best_estimator_.alpha

nonzero_coef = sum(grid_search.best_estimator_.coef_ != 0)
print(
    f"最优alpha: {best_alpha}\n"
    f"RMSE: {rmse}\n"
    f"非零特征数: {nonzero_coef}" 
)
最优alpha: 0.1
RMSE: 60.47737241913124
非零特征数: 273
new_df["MutNum_exome"].mean()
121.3963963963964

可以看到,整个数据集中total_gene_num的均值为121.39,而模型的RMSE达到60.47,几乎是均值的一半了,这是不太能接受的结果。

尝试调整alpha获得更优的结果,我们注意到目前最优alpha为0.1,是我们候选的alpha中的端点值,所以继续向下筛选。

LassoCV确定最优alpha

cadidate_alphas = np.linspace(0.01, 0.1, 1000)
model = linear_model.LassoCV(
    alphas=cadidate_alphas,
    fit_intercept=False,
    positive=True,
    selection="random",
    max_iter=3000,
    cv=10
)

model.fit(X_train, y_train)
LassoCV(alphas=array([0.01  , 0.01009, ..., 0.09991, 0.1    ]), copy_X=True,
    cv=10, eps=0.001, fit_intercept=False, max_iter=3000, n_alphas=100,
    n_jobs=None, normalize=False, positive=True, precompute='auto',
    random_state=None, selection='random', tol=0.0001, verbose=False)
y_test_pred = model.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_test_pred))
best_alpha = model.alpha_
r2 = r2_score(y_test, y_test_pred)

nonzero_coef = sum(model.coef_ != 0)
print(
    f"最优alpha: {best_alpha}\n"
    f"RMSE: {rmse}\n"
    f"R2: {r2}\n"
    f"非零特征数: {nonzero_coef}" 
)
最优alpha: 0.01
RMSE: 57.024710487728775
R2: 0.9119547827475557
非零特征数: 405

RMSE有所改善,但是选出的alpha依然是端点值,尝试继续缩小alpha

继续缩小alpha,减对模型的限制

cadidate_alphas = np.linspace(0.001, 0.01, 1000)
# max_iter should increase
model2 = linear_model.LassoCV(
    alphas=cadidate_alphas,
    fit_intercept=False,
    positive=True,
    selection="random",
    max_iter=5000,
    cv=10
)

model2.fit(X_train, y_train)
LassoCV(alphas=array([0.001  , 0.00101, ..., 0.00999, 0.01  ]), copy_X=True,
    cv=10, eps=0.001, fit_intercept=False, max_iter=5000, n_alphas=100,
    n_jobs=None, normalize=False, positive=True, precompute='auto',
    random_state=None, selection='random', tol=0.0001, verbose=False)
y_test_pred = model2.predict(X_test)
rmse = np.sqrt(mean_squared_error(y_test, y_test_pred))
best_alpha = model2.alpha_
r2 = r2_score(y_test, y_test_pred)

nonzero_coef = sum(model2.coef_ != 0)
print(
    f"最优alpha: {best_alpha}\n"
    f"RMSE: {rmse}\n"
    f"R2: {r2}\n"
    f"非零特征数: {nonzero_coef}" 
)
最优alpha: 0.001
RMSE: 56.6309058203357
R2: 0.913166639680416
非零特征数: 523

越小的alpha越接近普通的线性模型,但是我们注意到在alpha=0.01时,非零的特征数为405,我们的训练集中样本数为444,已经非常接近了,如果再减小alpha的话会出现n < p的情况,普通的线性模型已经不适合了。
所以在当前样本量的情况下,alpha=0.01已经基本是极限情况了,57.02的RMSE也基本是最优的情况,想要改善的话需要增加样本量。
如果我们持续放松对模型的限制,也就失去了特征筛选的意义。

基因数量与TMB的关系

从直观的角度就能理解,基因数目越大,panel和WES的R^2就越接近1。
下图展示了随机选取一定数目基因,二者相关性的情况:

TCGARanR2.png

可以看出随着基因数目增加,相关性也稳定增大,我们似乎不需要做特征筛选,只需要保证一定数目的基因就好了。
这篇文章讨论了基因数目对R^2的影响,微信公众号_从基因Panel测序数据评估15种肿瘤的TMB分布及与基因间的关系也做了描述了该种现象:做TMB评价要选择大panel,但他们没有阐述背后的原因。

这种情况可以从“总体均值和样本均值”的角度来解释:
TMB的计算方法是“体细胞突变数目/对应区域的长度”,即每Mb碱基的体细胞突变数,是一个均值。对于WES水平的TMB就是总体均值,而panel水平的TMB就是样本均值。我们知道,样本均值是总体均值的无偏估计,随着样本数量增加,样本均值的也越接近总体均值(标准误差减小)。

结论

如果以机器学习的观点来看TMB panel基因筛选,它就是一个真实模型为线性模型的学习问题,且真实模型没有intercept,coef均为正值。
由于p >> n,所以如果要做特征选择,可以选用Lasso模型。
受限于样本量,alpha只能松弛到一定程度:使得非零系数\approx样本数,得到RMSE是有一个最小值的
因此,基因筛选主要取决于两点:

  1. 样本量
    样本量越大,可以筛选出的基因数越多
  2. 基因数目
    基因数肯定是越大越准确,但也就失去了筛选的意义,所以可以在RMSE无明显增大的情况下,增大alpha,获得更小的基因数。
    基因数目的影响远大于具体选哪些基因的影响

在该问题的研究中,应该转换思路:不是从所有基因中选出一定数量的基因组成panel,这个panel可以评价TMB;而是,我已经有一个panel,再来评价该panel计算得到TMB是否和WES的一致

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容