我应该将哪种曲线(或模型)拟合到百分比数据?


15

我正在尝试创建一个显示病毒拷贝与基因组覆盖率(GCC)之间关系的图。这是我的数据:

病毒载量与GCC

起初,我只是绘制了线性回归图,但是我的主管告诉我这是不正确的,并尝试使用S形曲线。所以我使用geom_smooth做到了:

library(scales)
ggplot(scatter_plot_new, aes(x = Copies_per_uL, y = Genome_cov, colour = Virus)) +
    geom_point() +
    scale_x_continuous(trans = log10_trans(), breaks = trans_breaks("log10", function(x) 10^x), labels = trans_format("log10", math_format(10^.x))) +
        geom_smooth(method = "gam", formula = y ~ s(x), se = FALSE, size = 1) +
    theme_bw() +
    theme(legend.position = 'top', legend.text = element_text(size = 10), legend.title = element_text(size = 12), axis.text = element_text(size = 10), axis.title = element_text(size=12), axis.title.y = element_text(margin = margin (r = 10)), axis.title.x = element_text(margin = margin(t = 10))) +
    labs(x = "Virus copies/µL", y = "GCC (%)") +
    scale_y_continuous(breaks=c(25,50,75,100))

病毒载量与GCC-geom_smooth

但是,我的主管说这也是不正确的,因为曲线使GCC看起来可以超过100%,而事实并非如此。

我的问题是:显示病毒副本与GCC之间关系的最佳方法是什么?我想明确地说,A)低病毒拷贝=低GCC,并且B)在一定数量的病毒后拷贝GCC平稳期。

我研究了很多不同的方法-GAM,黄土,物流,分段-但是我不知道如何分辨什么是我的数据的最佳方法。

编辑:这是数据:

>print(scatter_plot_new)  
Subsample   Virus   Genome_cov  Copies_per_uL
1   S1.1_RRAV   RRAV    100 92500
2   S1.2_RRAV   RRAV    100 95900
3   S1.3_RRAV   RRAV    100 92900
4   S2.1_RRAV   RRAV    100 4049.54
5   S2.2_RRAV   RRAV    96.9935 3809
6   S2.3_RRAV   RRAV    94.5054 3695.06
7   S3.1_RRAV   RRAV    3.7235  86.37
8   S3.2_RRAV   RRAV    11.8186 84.2
9   S3.3_RRAV   RRAV    11.0929 95.2
10  S4.1_RRAV   RRAV    0   2.12
11  S4.2_RRAV   RRAV    5.0799  2.71
12  S4.3_RRAV   RRAV    0   2.39
13  S5.1_RRAV   RRAV    4.9503  0.16
14  S5.2_RRAV   RRAV    0   0.08
15  S5.3_RRAV   RRAV    4.4147  0.08
16  S1.1_UMAV   UMAV    5.7666  1.38
17  S1.2_UMAV   UMAV    26.0379 1.72
18  S1.3_UMAV   UMAV    7.4128  2.52
19  S2.1_UMAV   UMAV    21.172  31.06
20  S2.2_UMAV   UMAV    16.1663 29.87
21  S2.3_UMAV   UMAV    9.121   32.82
22  S3.1_UMAV   UMAV    92.903  627.24
23  S3.2_UMAV   UMAV    83.0314 615.36
24  S3.3_UMAV   UMAV    90.3458 632.67
25  S4.1_UMAV   UMAV    98.6696 11180
26  S4.2_UMAV   UMAV    98.8405 12720
27  S4.3_UMAV   UMAV    98.7939 8680
28  S5.1_UMAV   UMAV    98.6489 318200
29  S5.2_UMAV   UMAV    99.1303 346100
30  S5.3_UMAV   UMAV    98.8767 345100

6
似乎逻辑回归最好,因为它的范围是0到100%。
mkt-恢复莫妮卡

1
尝试(2)分段(线性)模型。
user158565 '19

3
尝试method.args=list(family=quasibinomial))geom_smooth()原始ggplot代码中添加参数。
Ben Bolker

4
附言:我鼓励您不要使用抑制标准错误se=FALSE。总是很高兴向人们展示不确定性实际上有多大……
Ben Bolker

2
您在过渡区域中没有足够的数据点,无法向任何权威断言存在平滑曲线。我可以很轻松地将Heaviside函数适合您向我们展示的要点。
卡尔·威索夫特

Answers:


6

解决此问题的另一种方法是使用贝叶斯公式,一开始可能会比较繁琐,但它往往使表达问题的细节变得容易得多,并且可以更好地了解“不确定性”的位置是

Stan是Monte Carlo采样器,具有相对易于使用的编程接口,库可用于R和其他但我在这里使用Python

我们像其他所有人一样使用S型曲线:它具有生化的动机,并且在数学上使用起来非常方便。这个任务的一个很好的参数化是:

import numpy as np

def sigfn(x, alpha, beta):
    return 1 / (1 + np.exp(-(x - alpha) * beta))

其中alpha定义了S形曲线的中点(即它穿过50%的位置)并beta定义了斜率,接近零的值更平坦

为了显示这是什么样子,我们可以提取您的数据并使用以下方法进行绘制:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_table('raw_data.txt', delim_whitespace=True)
df.columns = ['subsample', 'virus', 'coverage', 'copies']
df.coverage /= 100

x = np.logspace(-1, 6, 201)
plt.semilogx(x, sigfn(np.log(x), 5.5, 3), label='sigfn', color='C2')

sns.scatterplot(df.copies, df.coverage, hue=df.virus, edgecolor='none')

其中raw_data.txt包含您提供的数据,我将覆盖范围转换为更有用的内容。系数5.5和3看起来不错,并且给出了与其他答案非常相似的图:

绘图数据和手动拟合

为了使用Stan来“拟合”此函数,我们需要使用它自己的语言(在R和C ++之间混合使用)来定义模型。一个简单的模型如下所示:

data {
    int<lower=1> N;  // number of rows
    vector[N] log_copies;
    vector<lower=0,upper=1>[N] coverage;
}
parameters {
    real alpha;
    real beta;
    real<lower=0> sigma;
}
model {
    vector[N] mu;
    mu = 1 ./ (1 + exp(-(log_copies - alpha) * beta));

    sigma ~ cauchy(0, 0.1);
    alpha ~ normal(0, 5);
    beta ~ normal(0, 5);

    coverage ~ normal(mu, sigma);
}

希望读为OK。我们有一个data块定义了我们在对模型parameters进行采样时期望的数据,定义了要采样的事物以及model定义了似然函数。您告诉Stan“编译”模型,这需要一些时间,然后您可以从中采样一些数据。例如:

import pystan

model = pystan.StanModel(model_code=code)
model.sampling(data=dict(
    N=len(df),
    log_copies=np.log(df.copies),
    coverage=df.coverage,
), iter=10000, chains=4, thin=10)

import arviz
arviz.plot_trace(fit)

arviz 轻松绘制漂亮的诊断图,同时打印拟合为您提供漂亮的R风格参数摘要:

4 chains, each with iter=10000; warmup=5000; thin=10; 
post-warmup draws per chain=500, total post-warmup draws=2000.

        mean se_mean     sd   2.5%    25%    50%    75%  97.5%  n_eff   Rhat
alpha   5.51  6.0e-3   0.26   4.96   5.36   5.49   5.64   6.12   1849    1.0
beta    2.89    0.04   1.71   1.55   1.98   2.32   2.95   8.08   1698    1.0
sigma   0.08  2.7e-4   0.01   0.06   0.07   0.08   0.09    0.1   1790    1.0
lp__   57.12    0.04   1.76   52.9   56.1  57.58  58.51  59.19   1647    1.0

标准偏差大beta表示数据确实没有提供有关此参数的太多信息。在模型拟合中给出10个以上有效数字的一些答案也有些夸大其词

因为一些答案指出每种病毒可能都需要自己的参数,所以我扩展了模型,以允许“病毒” alphabeta因“病毒”而异。一切都变得有些怪异,但是两种病毒几乎可以肯定具有不同的alpha值(即,对于相同的覆盖范围,您需要更多份/μLRRAV),并且显示如下图:

数据和MC样本图

数据与以前相同,但我绘制了40个后验样本的曲线。 UMAV看起来相对确定,而RRAV可能遵循相同的斜率并需要更高的副本数量,或者具有更陡的斜率和相似的副本数量。大多数后部肿块需要更高的复制计数,但是这种不确定性可能解释了其他答案中发现不同事物的一些差异

我主要用于回答这个作为一个练习来提高我的斯坦知识,我已经把这个一Jupyter笔记本这里万一有人有兴趣/希望复制这一点。


14

(编辑时考虑了以下注释。感谢@BenBolker和@WeiwenNg的帮助。)

将分数逻辑回归拟合到数据。它非常适合范围在0到100%之间的百分比数据,并且在生物学的许多领域中在理论上都是合理的。

请注意,您可能必须将所有值除以100才能适应它,因为程序经常希望数据在0到1之间。并且,如Ben Bolker所建议,要解决二项分布对方差的严格假设所引起的可能问题,请使用而是准二项分布。

我已经根据您的代码做出了一些假设,例如您感兴趣的是2种病毒,它们可能显示不同的模式(即,病毒类型和份数之间可能存在相互作用)。

首先,模型拟合:

dat <- read.csv('Book1.csv')
dat$logcopies <- log10(dat$Copies_per_uL)
dat$Genome_cov_norm <- dat$Genome_cov/100

fit <- glm(Genome_cov_norm ~ logcopies * Virus, data = dat, family = quasibinomial())
summary(fit)


Call:
glm(formula = Genome_cov_norm ~ logcopies * Virus, family = quasibinomial(), 
    data = dat)

Deviance Residuals: 
     Min        1Q    Median        3Q       Max  
-0.55073  -0.13362   0.07825   0.20362   0.70086  

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)  
(Intercept)          -5.9702     2.8857  -2.069   0.0486 *
logcopies             2.3262     1.0961   2.122   0.0435 *
VirusUMAV             2.6147     3.3049   0.791   0.4360  
logcopies:VirusUMAV  -0.6028     1.3173  -0.458   0.6510  
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for quasibinomial family taken to be 0.6934319)

    Null deviance: 30.4473  on 29  degrees of freedom
Residual deviance:  2.7033  on 26  degrees of freedom

如果您信任p值,则输出不会表明这两种病毒之间存在显着差异。这与下面的@NickCox结果相反,尽管我们使用了不同的方法。无论如何,我都不会对30个数据点充满信心。

二,绘图:

编写一种可以自己可视化输出的方法并不难,但是似乎有一个ggPredict包可以为您完成大部分工作(不能保证,我自己也没有尝试过)。该代码将类似于:

library(ggiraphExtra)
ggPredict(fit) + theme_bw(base_size = 20) + geom_line(size = 2) 

更新:我不再推荐一般的代码或ggPredict函数。在尝试之后,我发现绘制的点并不能完全反映输入数据,而是出于某些奇怪的原因而进行了更改(某些绘制的点大于1且小于0)。因此,我建议您自己编写代码,尽管这需要更多工作。


7
我支持这个答案,但我想澄清一点:我称这种分数为Logistic回归。我认为这个词会得到更广泛的认可。当大多数人听到“逻辑回归”时,我敢打赌他们会想到0/1因变量。一个很好的答案Stackexchange处理这个术语是在这里:stats.stackexchange.com/questions/216122/...
何伟文伍

2
@teaelleceecee显然您必须先将覆盖率除以100。
尼克·考克斯

4
用于family=quasibinomial()避免警告(以及过于严格的方差假设的潜在问题)。采纳@mkt关于其他问题的建议。
本·博克

2
这可能有效,但是我想提醒大家安装功能之前,您应该先假设一个前提,即您的数据实际上应该遵循该功能。否则,当您选择拟合函数时,您几乎是随机拍摄的,结果可能会让您迷惑。
卡尔·威索夫特

6
@CarlWitthoft我们听到了布道,但在组织之外是罪人。是什么前提导致您在其他评论中提出了Heaviside功能?这里的生物学与急剧转变的过渡并不相似。据我了解,这里的研究事实是形式理论比数据薄弱。我同意:如果人们认为步进功能有意义,那么他们应该适合。
尼克·考克斯

11

这与@mkt没什么不同,但是特别是图表不适合注释。我首先在Stata中拟合逻辑曲线(在记录预测变量之后)以获取所有数据,并获得此图

在此处输入图片说明

一个方程是

100 invlogit(-4.192654 + 1.880951 log10Copies))

现在,在最简单的病毒定义指标变量的情况下,我针对每种病毒分别拟合曲线。作为记录,这里是一个Stata脚本:

clear 
input id str9 Subsample   str4 Virus   Genome_cov  Copies_per_uL
1   S1.1_RRAV   RRAV    100 92500
2   S1.2_RRAV   RRAV    100 95900
3   S1.3_RRAV   RRAV    100 92900
4   S2.1_RRAV   RRAV    100 4049.54
5   S2.2_RRAV   RRAV    96.9935 3809
6   S2.3_RRAV   RRAV    94.5054 3695.06
7   S3.1_RRAV   RRAV    3.7235  86.37
8   S3.2_RRAV   RRAV    11.8186 84.2
9   S3.3_RRAV   RRAV    11.0929 95.2
10  S4.1_RRAV   RRAV    0   2.12
11  S4.2_RRAV   RRAV    5.0799  2.71
12  S4.3_RRAV   RRAV    0   2.39
13  S5.1_RRAV   RRAV    4.9503  0.16
14  S5.2_RRAV   RRAV    0   0.08
15  S5.3_RRAV   RRAV    4.4147  0.08
16  S1.1_UMAV   UMAV    5.7666  1.38
17  S1.2_UMAV   UMAV    26.0379 1.72
18  S1.3_UMAV   UMAV    7.4128  2.52
19  S2.1_UMAV   UMAV    21.172  31.06
20  S2.2_UMAV   UMAV    16.1663 29.87
21  S2.3_UMAV   UMAV    9.121   32.82
22  S3.1_UMAV   UMAV    92.903  627.24
23  S3.2_UMAV   UMAV    83.0314 615.36
24  S3.3_UMAV   UMAV    90.3458 632.67
25  S4.1_UMAV   UMAV    98.6696 11180
26  S4.2_UMAV   UMAV    98.8405 12720
27  S4.3_UMAV   UMAV    98.7939 8680
28  S5.1_UMAV   UMAV    98.6489 318200
29  S5.2_UMAV   UMAV    99.1303 346100
30  S5.3_UMAV   UMAV    98.8767 345100
end 

gen log10Copies = log10(Copies)
gen Genome_cov_pr = Genome_cov / 100
encode Virus, gen(virus)
set seed 2803 
fracreg logit Genome_cov_pr log10Copies i.virus, vce(bootstrap, reps(10000)) 

twoway function invlogit(-5.055519 + 1.961538 * x), lc(orange) ra(log10Copies)      ///
|| function invlogit(-5.055519 + 1.233273 + 1.961538 * x), ra(log10Copies) lc(blue) ///
|| scatter Genome_cov_pr log10Copies if Virus == "RRAV", mc(orange) ms(Oh)          ///
|| scatter Genome_cov_pr log10Copies if Virus == "UMAV", mc(blue) ms(+)             ///
legend(order(4 "UMAV" 3 "RRAV") pos(11) col(1) ring(0))                             ///
xla(-1 "0.1" 0 "1" 1 "10" 2 "100" 3 "10{sup:3}" 4 "10{sup:4}" 5 "10{sup:5}")        ///
yla(0 .25 "25" .5 "50" .75 "75" 1 "100", ang(h))                                    ///
ytitle(Genome coverage (%)) xtitle(Genome copies / {&mu}L) scheme(s1color) 

这对一个很小的数据集造成了很大的压力,但是病毒的P值似乎有助于共同拟合两条曲线。

Fractional logistic regression                  Number of obs     =         30
                                                Replications      =     10,000
                                                Wald chi2(2)      =      48.14
                                                Prob > chi2       =     0.0000
Log pseudolikelihood = -6.9603063               Pseudo R2         =     0.6646

-------------------------------------------------------------------------------
              |   Observed   Bootstrap                         Normal-based
Genome_cov_pr |      Coef.   Std. Err.      z    P>|z|     [95% Conf. Interval]
--------------+----------------------------------------------------------------
  log10Copies |   1.961538   .2893965     6.78   0.000     1.394331    2.528745
              |
        virus |
        UMAV  |   1.233273   .5557609     2.22   0.026     .1440018    2.322544
        _cons |  -5.055519   .8971009    -5.64   0.000    -6.813805   -3.297234
-------------------------------------------------------------------------------

在此处输入图片说明


3

试试S形函数。有许多这种形状的公式,包括逻辑曲线。双曲正切是另一个流行的选择。

给定这些图,我也不能排除一个简单的阶跃函数。恐怕您将无法区分阶跃函数和任何数量的S型规格。您没有任何观察到百分比在50%范围内的信息,因此简单的步骤公式化可能是最明智的选择,其效果不会比更复杂的模型差


值得注意的是,双曲正切与S形函数密切相关。。σ(x)=12(1+tanhx2)
JG

2
就我而言,@ JG“ sigmoid”是S曲线的通用术语,但您正确地指出了S型的两个规格之间的联系
Aksakal

2

这是受约束和不受约束的4PL(4参数对数)拟合,根据CA Holstein,M。Griffin,J.Hong,PD Sampson的公式,“确定和比较生物测定检测限的统计方法”,Anal 。化学 87(2015)9795-9801。在两个图中都显示了4PL方程,参数含义如下:a =下渐近线,b =斜率因子,c =拐点,d =上渐近线。

图1将a约束为等于0%,将d约束为等于100%:

图1约束a和d

图2对4PL公式中的4个参数没有约束:

图2无约束

这很有趣,我不假装不了解任何生物学信息,看到它们如何稳定下来将很有趣!


谢谢,这真的很有帮助。只是想知道,您是否在MATLAB中使用fit函数来执行此操作?
teaelleceecee

1
我将Igor Pro与图中所示的用户定义的用户功能一起使用。自1988年以来,我就一直使用Igor Pro及其前身(Igor),但是许多其他程序也可以进行曲线拟合,例如Origin Pro和非常便宜的Kaleidagraph。看来您具有R和(可能吗?)对Matlab的访问权限,除了它们极有能力,我对它们都不了解。最好的成功方法,希望您下次与主管讨论问题时得到好消息!另外,感谢您发布数据!
Ed V

2

我从散点图中提取了数据,我的方程式搜索将3参数逻辑类型方程式作为了很好的候选者:“ y = a /(1.0 + b * exp(-1.0 * c * x))”,其中“ x“是每个图的对数底数10。对于我提取的数据,拟合参数为a = 9.0005947126706630E + 01,b = 1.2831794858584102E + 07和c = 6.6483431489473155E + 00,如果重新拟合,则对数(log 10 x)的原始数据的拟合将产生相似的结果使用我的值作为初始参数估算值的原始数据。我的参数值在提取的数据上得出R平方= 0.983,RMSE = 5.625。

情节

编辑:现在已经对问题进行了编辑,以包括实际数据,这是使用上述3参数方程式和初始参数估计值的图表。

情节2


数据提取似乎出现了错误:您有一堆负百分比值。另外,您的最大值约为90%,而不是原始图中的100%。由于某种原因,您可能会将所有内容抵消约10%。
mkt-恢复莫妮卡

h-这是半手动提取的数据,需要原始数据。通常,这对于方程式搜索就足够了,当然对于最终结果来说还不够,这就是为什么我说要使用我的O拟合参数值作为对原始数据的初始参数估计的原因。
詹姆斯·菲利普斯

请注意,由于实际数据已添加到帖子中,因此我已使用更新后的数据更新了此答案。
詹姆斯·菲利普斯

只是重申一下:例如,应用Heaviside函数可能会产生相似的误差值。
卡尔·威索夫特

1
@JamesPhillips我将尝试这样做(Heaviside –> errorbars或等效工具)
Carl

2

由于我不得不对Heaviside敞开心mouth,因此得出了以下结果。我将转换点设置为log10(viruscopies)= 2.5。然后,我计算了数据集的两半的标准偏差-也就是说,Heaviside假设任一侧的数据都具有所有导数= 0。

右手侧标准差= 4.76
右手侧标准差= 7.72

由于事实证明每批次有15个样本,因此总体标准差是平均值或6.24。

假设其他答案中引用的“ RMSE”总体上是“ RMS错误”,那么Heaviside函数似乎至少可以满足大多数“ Z曲线”(从照相响应命名法借用)的要求,甚至要好于大多数。这里。

编辑

无用图,但在注释中要求:

配重曲线拟合


抱歉,您需要发布模型和散点图,就像其他答案中所做的一样?我最好奇地看到这些结果并进行比较。请同时添加RMSE和R平方值以进行比较。我个人从未使用过Heaviside函数,却发现这非常有趣。
James Phillips

R2

我的意思是绘制与其他答案相似的情节,以便与这些答案进行直接比较。
詹姆斯·菲利普斯

2
@JamesPhillips,您还有两个愿望。明智地选择:-)
卡尔·威索夫特

谢谢你的情节。我观察到在其他答案的所有图中,绘制的方程均遵循右上角数据的弯曲形状-您不这样做,就像Heaviside函数的性质一样。从视觉上看,这与您的说法“ Heaviside函数将要执行以及在其他答案中发布的方程式相同”的说法相矛盾-这就是为什么我以前要求RMSE和R平方值的原因,我怀疑Heaviside函数不会遵循形状区域中的数据,对于那些拟合统计量可能会产生更差的值。
詹姆斯·菲利普斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.