Logistic回归功效分析的仿真-设计的实验


39

这个问题是对@Greg Snow给出的答案的回应,该答案是我提出的有关使用Logistic回归和SAS进行功率分析的问题Proc GLMPOWER

如果我正在设计一个实验并将对结果进行析因逻辑回归分析,该如何使用模拟(和此处)进行功效分析?

这是一个简单的示例,其中有两个变量,第一个具有三个可能的值{0.03,0.06,0.09},第二个是虚拟指示器{0,1}。对于每种组合,我们估计每种组合的响应率(响应者数量/投放市场的人数)。此外,我们希望因子的第一个组合的数量是其他因子的3倍(可以认为是相等的),因为该第一个组合是我们尝试过的真实版本。这种设置类似于链接问题中提到的SAS课程中给出的设置。

在此处输入图片说明

用于分析结果的模型将是具有主要影响和相互作用(响应为0或1)的逻辑回归。

mod <- glm(response ~ Var1 + Var2 + I(Var1*Var2))

如何模拟用于此模型的数据集进行功率分析?

当我通过SAS运行时Proc GLMPOWER(使用STDDEV =0.05486016 对应于sqrt(p(1-p))其中p是显示的响应率的加权平均值):

data exemplar;
  input Var1 $ Var2 $ response weight;
  datalines;
    3 0 0.0025  3
    3 1 0.00395 1
    6 0 0.003   1
    6 1 0.0042  1
    9 0 0.0035  1
    9 1 0.002   1;
run;

proc glmpower data=exemplar;
  weight weight;
  class Var1 Var2;
  model response = Var1 | Var2;
  power
    power=0.8
    ntotal=.
    stddev=0.05486016;
run;

注意:GLMPOWER仅将使用类(标称)变量,因此上面的3、6、9被视为字符,并且可能是低,中,高或任何其他三个字符串。进行实际分析时,Var1将使用数字(并且我们将包括多项式Var1 * Var1)来说明任何曲率。

SAS的输出是

在此处输入图片说明

因此,我们看到需要762,112作为样本大小(Var2主效应最难估计),功效等于0.80,alpha等于0.05。我们将它们分配为基线组合的3倍(即0.375 * 762112),而其余的恰好等于其他5种组合。


在R中很容易做到这一点。第一个问题:我是否要纠正,您希望所有案例的75%为{var1 = .03,var2 = 0}和其他所有组合的25%,而不是每1个3个单位其他连击中的每个单位(即37.5%)?第二个问题,您可以指定您要检测的效果吗?即,1对0的对数赔率是多少?如果var1上升.01,成功的对数几率应该如何变化?您是否认为可能存在互动(如果有,互动有多大)?(注意,这些Q很难回答,一种策略是指定您认为每个组合中1的比例。)
gung-恢复Monica

1st:基准情况的权重3是{var1 = 0.03,var2 = 0}的情况的3倍。因此,SAS得出的结果(表示我们需要762,112个总样本量才能具有80%的抑制主效应var2 = 0的能力,因此这是我们需要的总样本量)将分配给此基准情况37.5%。
B_Miner 2012年

第二:好吧,我们所获得的只是回应率(这是成功次数与试验次数的预期比率)。因此,如果我们发送1,000个字母,其中Var1 = 0.03和Var2 = 0,这可能对应于信用卡直接邮件报价的利率报价0.03(3%),并且信封上没有标签(其中Var2 = 1表示有不干胶标签),我们期望有1000 * 0.0025的回复。
B_Miner 2012年

第二点:我们确实期待互动-因此反应率。请注意,根据Var1的值,对于Var2 = 0会有不同的响应率。我不确定如何将这些转换为对数赔率,然后转换为模拟数据集。
B_Miner 2012年

最后一件事。我注意到当var2 = 0时,对var1的响应率是线性的(即.25%、. 30%、. 35%)。您打算将其设为线性效果还是曲线效果?您应该知道,概率对于其范围的一小部分子集看起来是线性的,但实际上却不是线性的。Logistic回归的对数赔率是线性的,而不是概率(我在这里的答案中讨论类似的东西)。
gung-恢复莫妮卡

Answers:


43

初步资料:

  • 正如G * Power手册中所讨论的,根据您要解决的问题,有几种不同类型的功率分析。(也就是说,,效果大小,和功效彼此相关;指定其中任意三个即可解决第四个问题。) Ë 小号αNESα

    • 在您的描述中,您想知道适当的来捕获您用指定的响应率,功率= 80%。这是先验的力量α = 0.05Nα=.05
    • 我们可以从事后功率(确定功率给定,响应率和alpha)开始,因为这在概念上更简单,然后再向上移动N
  • 除了@GregSnow的出色文章之外,还可以在此处找到关于基于仿真的CV功率分析的另一个非常好的指南:计算统计功率。总结基本思想:

    1. 找出您想要能够检测到的效果
    2. 从可能的世界中生成N个数据
    3. 运行您打算对这些人造数据进行的分析
    4. 根据您选择的alpha存储结果是否“显着”
    5. 重复多次()次并使用%的“显着性”作为该(事后)功效的估计值ñBN
    6. 要确定先验幂,请搜索可能的来找到产生所需幂的值 N
  • 您是否会发现特定迭代的重要性可以理解为伯努利试验的结果,概率为(其中是幂)。在次迭代中找到的比例使我们能够近似真实的。为了获得更好的近似值,我们可以增加,尽管这也会使仿真花费更长的时间。 p p ppBpB

  • 在R中,以给定的“成功”概率生成二进制数据的主要方式是

    • 例如,要从10个概率为p的伯努利试验中获得成功的次数,代码将为rbinom(n=10, size=1, prob=p),(您可能希望将结果分配给变量以进行存储)
    • 您还可以使用?runif来不太优雅地生成此类数据,例如,ifelse(runif(1)<=p, 1, 0)
    • 如果您认为结果是由潜在的高斯变量介导的,则可以将潜在变量生成为带有?rnorm的协变量的函数,然后将其转换为概率,pnorm()并在rbinom()代码中使用它们。
  • 您声明将“包括多项式项Var1 * Var1)以解决任何曲率”。这里有些混乱;多项式项可以帮助我们解释曲率,但这是一个交互项,它不会以这种方式帮助我们。但是,您的回复率要求我们在模型中同时包括平方项和交互项。具体来说,您的模型将需要包括:,和,而不是基本术语。 v a r 1 * v a r 2 v a r 1 2 * v a r 2var12var1var2var12var2

  • 尽管是在另一个问题的上下文中编写的,但我在这里的回答是:logit模型和概率模型之间的差异具有许多关于这些类型的模型的基本信息。
  • 就像在存在多个假设时(例如,每个对比错误率家庭错误率每个家庭错误率)有不同类型的I型错误率一样,也存在不同类型的功效 *(例如,对于一个单个预先指定的效果任何效果所有效果)。您还可以寻求检测特定效果组合的能力,或者寻求对整个模型进行同时测试的能力。根据您对SAS代码的描述,我的猜测是它正在寻找后者。但是,根据您对情况的描述,我假设您希望至少检测到交互作用。

  • 以另一种方式思考与电源相关的问题,请在此处查看我的答案:如何在证明样本量合理的情况下报告估计关联性的一般精度。

R中逻辑回归的简单事后功效:

假设您的假设答复率代表了世界的真实情况,并且您已发出10,000封信。检测这些影响的力量是什么?(请注意,我以编写“通常效率不高”的代码而闻名,以下内容旨在易于遵循,而不是针对效率进行优化;实际上,它相当慢。)

set.seed(1)

repetitions = 1000
N = 10000
n = N/8
var1  = c(   .03,    .03,    .03,    .03,    .06,    .06,    .09,   .09)
var2  = c(     0,      0,      0,      1,      0,      1,      0,     1)
rates = c(0.0025, 0.0025, 0.0025, 0.00395, 0.003, 0.0042, 0.0035, 0.002)

var1    = rep(var1, times=n)
var2    = rep(var2, times=n)
var12   = var1**2
var1x2  = var1 *var2
var12x2 = var12*var2

significant = matrix(nrow=repetitions, ncol=7)

startT = proc.time()[3]
for(i in 1:repetitions){
  responses          = rbinom(n=N, size=1, prob=rates)
  model              = glm(responses~var1+var2+var12+var1x2+var12x2, 
                           family=binomial(link="logit"))
  significant[i,1:5] = (summary(model)$coefficients[2:6,4]<.05)
  significant[i,6]   = sum(significant[i,1:5])
  modelDev           = model$null.deviance-model$deviance
  significant[i,7]   = (1-pchisq(modelDev, 5))<.05
}
endT = proc.time()[3]
endT-startT

sum(significant[,1])/repetitions      # pre-specified effect power for var1
[1] 0.042
sum(significant[,2])/repetitions      # pre-specified effect power for var2
[1] 0.017
sum(significant[,3])/repetitions      # pre-specified effect power for var12
[1] 0.035
sum(significant[,4])/repetitions      # pre-specified effect power for var1X2
[1] 0.019
sum(significant[,5])/repetitions      # pre-specified effect power for var12X2
[1] 0.022
sum(significant[,7])/repetitions      # power for likelihood ratio test of model
[1] 0.168
sum(significant[,6]==5)/repetitions   # all effects power
[1] 0.001
sum(significant[,6]>0)/repetitions    # any effect power
[1] 0.065
sum(significant[,4]&significant[,5])/repetitions   # power for interaction terms
[1] 0.017

因此,我们发现10,000个字母并不能真正实现80%的检测任何响应率的能力。(我对SAS代码的工作方式还不确定,无法解释这些方法之间的明显差异,但是从概念上讲,这段代码很简单-如果很慢,我花了一些时间对其进行检查,我认为这些结果是合理的。)

基于逻辑的逻辑回归的先验能力:

从这里开始,想法是简单地搜索可能的,直到找到一个可以产生您感兴趣的功率类型的期望水平的值。您可以对此进行编码的任何搜索策略都可以(在理论)。鉴于要捕获如此小的效果需要,因此值得思考如何更有效地做到这一点。我的典型方法只是蛮力,即评估我可能合理考虑的每个(但是请注意,我通常只会考虑一个很小的范围,并且我通常使用非常小的来工作-至少与此相比。) N N NNNNN

相反,我在这里的策略是将可能的括起来以了解幂的范围。因此,我选择了500,000 的,然后重新运行代码(启动相同的种子,运行一个半小时才完成)。结果如下: NNN

sum(significant[,1])/repetitions      # pre-specified effect power for var1
[1] 0.115
sum(significant[,2])/repetitions      # pre-specified effect power for var2
[1] 0.091
sum(significant[,3])/repetitions      # pre-specified effect power for var12
[1] 0.059
sum(significant[,4])/repetitions      # pre-specified effect power for var1X2
[1] 0.606
sum(significant[,5])/repetitions      # pre-specified effect power for var12X2
[1] 0.913
sum(significant[,7])/repetitions      # power for likelihood ratio test of model
[1] 1
sum(significant[,6]==5)/repetitions   # all effects power
[1] 0.005
sum(significant[,6]>0)/repetitions    # any effect power
[1] 0.96
sum(significant[,4]&significant[,5])/repetitions   # power for interaction terms
[1] 0.606

从中我们可以看到,效果的大小差异很大,因此您检测效果的能力也有所不同。例如,的影响特别难以检测,即使有100万个字母,也仅占6%的时间。另一方面,作为一个整体,该模型始终比空模型好得多。其他可能性介于两者之间。尽管大多数“数据”在每次迭代时都被丢弃,但仍有可能进行大量探索。例如,我们可以使用矩阵来评估不同变量的概率之间的相关性。 var12significant

最后,我要指出的是,由于您所处情况的复杂性和大,这并不像我最初评论中所怀疑/声称的那样简单。但是,您肯定可以从我在这里获得的信息中大致了解如何完成此操作以及功耗分析中涉及的问题。HTH。 N


Gung-WOW非常感谢您提供如此详尽周到的答案!在编写我自己的代码并使用您的代码时,二次项似乎是问题所在-因为在不考虑模型的情况下,以较小的样本大小即可获得至少80%的功效。
B_Miner 2012年

1
@B_Miner太好了,这就是您想要执行的操作。而且,这就是我认为基于仿真的方法优于仅散布大量数据的分析软件的原因(R也有此pwr软件包)。这种方法使您有机会更清楚地思考(和/或完善您的想法)关于您将要发生的事情,如何处理该问题等。类似地,如果您设定的利率是正确的,则b / c并非线性的,并且仅凭相互作用就无法捕获曲线关系。
gung-恢复莫妮卡

我认为您应该演示使用polyR而不是向R的新用户展示平方值更容易出错的策略。我认为完整的模型应该是glm(responses~ poly(var1, 2) * var2, family=binomial(link="logit")。这样既不易产生解释方面的统计错误,又更紧凑。当您仅查看整体拟合时,在这种情况下可能并不重要,但可能容易误导那些可能会倾向于查看各个术语的不那么精通的用户。
DWin

@DWin,当我使用R在CV上进行说明时,我以非R的方式进行操作。对于不熟悉w / R的人,此想法应尽可能透明。例如,我没有使用向量化的可能性,没有使用循环=等。人们将更熟悉基本回归中的平方变量类,poly()如果他们不是R用户,则不太了解是什么。
gung-恢复莫妮卡

17

@Gung的答案非常有助于理解。这是我将使用的方法:

mydat <- data.frame( v1 = rep( c(3,6,9), each=2 ),
    v2 = rep( 0:1, 3 ), 
    resp=c(0.0025, 0.00395, 0.003, 0.0042, 0.0035, 0.002) )

fit0 <- glm( resp ~ poly(v1, 2, raw=TRUE)*v2, data=mydat,
    weight=rep(100000,6), family=binomial)
b0 <- coef(fit0)


simfunc <- function( beta=b0, n=10000 ) {
    w <- sample(1:6, n, replace=TRUE, prob=c(3, rep(1,5)))
    mydat2 <- mydat[w, 1:2]
    eta <- with(mydat2,  cbind( 1, v1, 
                v1^2, v2,
                v1*v2,
                v1^2*v2 ) %*% beta )
    p <- exp(eta)/(1+exp(eta))
    mydat2$resp <- rbinom(n, 1, p)

    fit1 <- glm( resp ~ poly(v1, 2)*v2, data=mydat2,
        family=binomial)
    fit2 <- update(fit1, .~ poly(v1,2) )
    anova(fit1,fit2, test='Chisq')[2,5]
}

out <- replicate(100, simfunc(b0, 10000))
mean( out <= 0.05 )
hist(out)
abline(v=0.05, col='lightgrey')

此功能测试v2的整体效果,可以更改模型以查看其他类型的测试。我喜欢将其编写为一个函数,这样,当我想测试不同的东西时,只需更改函数参数即可。您也可以添加进度条,或使用并行程序包来加快速度。

在这里,我只进行了100次复制,通常从这个水平开始,以找到大概的样本大小,然后当我处于正确的停放状态时提高信号强度(当您拥有20%的功率时,无需在10,000次迭代上浪费时间)。


谢谢格雷格!我想知道同样的方法(如果我正确地理解了您所做的事情)。确认:您是否正在创建一个基于“ mydat”的非常大的数据集(实际上是用权重而不是蛮力创建的,以分别创建Var1和Var2的值的记录,然后分别为1和0的记录)。 ,拟合逻辑回归,然后在仿真中使用这些系数从建议的模型中采样?看来,这是得出系数的一种通用方法-就像您对我链接的有序回归幂的反应一样。
B_Miner 2012年

初始模型使用权重来获取要使用的系数,但是在模拟中,它正在创建带有n行的数据框。在函数中权重也可能更有效。
格雷格·斯诺

我是正确的,您最初使用的是数据(为了获得良好的估计而将其放大)是为了获得所使用的系数?
B_Miner 2012年

是的,那么大就可以使比例乘以重量得到一个整数。
格雷格·斯诺

2
@B_Miner,我正在计划撰写一篇文章,我不知道一本书是否足够。但是,谢谢你的鼓励。
格雷格·斯诺
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.