0

我正在开发一个用于生存分析的审查因变量。我的目标是找到某人在调查中回答问题的最后时间(“时间”)(例如,“q.time”被编码为“1”,“q.time+1”和 q随后的时间被编码为“0”)。

按照这个逻辑,最后回答的问题应该编码为“1”(q.time)。第一个未回答的问题 (q.time+1) 应编码为“0”。第一个未回答的问题之后的所有问题都应编码为“NA”。然后我想从我的数据集中删除 DV=NA 的所有行。

一位非常慷慨的同事帮助我开发了以下代码,但他现在正在休假,需要更多的爱。代码如下:

library(plyr)  # for ddply 
library(stats)  # for reshape(...) 
# From above 
dat <- data.frame( 
  id=c(1, 2, 3, 4), 
  q.1=c(1, 1, 0, 0), 
  q.2=c(1, 0, 1, 0), 
  dv.1=c(1, 1, 1, 1), 
  dv.2=c(1, 1, 0, 1)) 
# From above 
  long <- reshape(dat, 
                direction='long', 
                varying=c('q.1', 'q.2', 'dv.1', 'dv.2')) 
   ddply(long, .(id), function(df) { 
# figure out the dropoff time 
answered <- subset(df, q == 1) 
last.q = max(answered$time) 
subs <- subset(df, time <= last.q + 1) 
# set all the dv as desired 
new.dv <- rep(last.q,1) 
if (last.q < max(df$time)) new.dv <- c(0,last.q) 
subs$dv <- new.dv 
subs 
})

不幸的是,这会产生错误消息:

"Error in `$<-.data.frame`(`*tmp*`, "dv", value = c(0, -Inf)) : 
 replacement has 2 rows, data has 0"

有任何想法吗?问题似乎出在“rep”命令中,但我是 R 的新手。非常感谢!

更新:请参阅下面的解释,然后参考后续问题

您好-我完全关注您,非常感谢您花时间帮助我。我回到我的数据中并在一个虚拟 Q 中编码,其中所有受访者的值都为“1” - 但是,发现错误可能真正出在哪里。在我的真实数据集中,我有 30 个问题(即,长格式 30 次)。在我更改数据集后,确保所有 id 变量的 q==1,错误消息变为说

"Error in `$<-.data.frame`(`*tmp*`, "newvar", value = c(0, 29)) : replacement has 2 rows, data has 31"

如果问题出在分配给 subs 的行数上,那么错误的根源来自...

subs <- subset(df, time <= last.q + 1) 

即,$time <= last.q + 1$将行数设置为等于 last.q+1 的值?

更新 2:理想情况下,我希望我的新变量看起来像什么!

 id  time q  dv   
 1    1   1   1
 1    2   1   1
 1    3   1   1
 1    4   1   1
 1    5   0   0
 1    6   0   NA
 2    1   1   1
 2    2   1   1
 2    3   0   0
 2    4   0   NA
 2    5   0   NA
 2    6   0   NA

请注意,“q”可以随时间在“0”或“1”之间变化(参见时间=2 时 id=1 的观察结果),但由于生存分析的性质,“dv”不能。我需要做的是创建一个变量,找出“q”在“1”和“0”之间变化的最后时间,然后进行相应的审查。在第 4 步之后,我的数据应如下所示:

 id  time q  dv   
 1    1   1   1
 1    2   1   1
 1    3   1   1
 1    4   1   1
 2    1   1   1
 2    2   1   1
 2    3   0   0
4

3 回答 3

5

简而言之:错误是因为没有q == 1when id == 4


检查这里发生了什么的一个好方法是单独重写函数,并手动测试 ddply 正在处理的每个块。

所以首先像这样重写你的代码:

myfun <- function(df) { 
  # figure out the dropoff time 
  answered <- subset(df, q == 1) 
  last.q = max(answered$time) 
  subs <- subset(df, time <= last.q + 1) 
  # set all the dv as desired 
  new.dv <- rep(last.q,1) 
  if (last.q < max(df$time)) new.dv <- c(0,last.q) 
  subs$dv <- new.dv 
  subs
}
ddply(long, .(id), myfun )

当然,这仍然会出错,但至少现在我们可以手动检查 ddply 正在做什么。

ddply(long, .(id), myfun )真正意思:

  1. 取名为 long 的数据框
  2. 创建多个子集数据帧(每个不同的 id 一个)
  3. 将函数 myfun 应用于每个子集数据帧
  4. 将结果重新组合到单个数据帧中

因此,让我们尝试手动执行 ddply 自动执行的操作。

    > myfun(subset(long, id == 1))
        id time q dv
    1.1  1    1 1  2
    1.2  1    2 1  2
    > myfun(subset(long, id == 2))
        id time q dv
    2.1  2    1 1  0
    2.2  2    2 0  1
    > myfun(subset(long, id == 3))
        id time q dv
    3.1  3    1 0  2
    3.2  3    2 1  2
    > myfun(subset(long, id == 4))
    Error in `$<-.data.frame`(`*tmp*`, "dv", value = c(0, -Inf)) : 
      replacement has 2 rows, data has 0
    In addition: Warning message:
    In max(answered$time) : no non-missing arguments to max; returning -Inf
    > 

因此,错误似乎来自 ddply 将函数应用于 id == 4 的步骤。

现在让我们把代码放在函数之外,这样我们就可以检查每个块。

> #################
> # set the problem chunk to "df" so we 
> # can examine what the function does
> # step by step
> ################
> df <- subset(long, id == 4)
> 
> ###################
> # run the bits of function separately
> ###################
> answered <- subset(df, q == 1) 
> answered
[1] id   time q    dv  
<0 rows> (or 0-length row.names)
> last.q = max(answered$time) 
Warning message:
In max(answered$time) : no non-missing arguments to max; returning -Inf
> last.q
[1] -Inf
> subs <- subset(df, time <= last.q + 1) 
> subs
[1] id   time q    dv  
<0 rows> (or 0-length row.names)
> # set all the dv as desired 
> new.dv <- rep(last.q,1) 
> new.dv
[1] -Inf
> if (last.q < max(df$time)) new.dv <- c(0,last.q)  
> subs$dv <- new.dv 
Error in `$<-.data.frame`(`*tmp*`, "dv", value = c(0, -Inf)) : 
  replacement has 2 rows, data has 0
> subs
[1] id   time q    dv  
<0 rows> (or 0-length row.names)
> 

因此,您得到的错误来自subs$dv <- new.dv因为 new.dv 的长度为 2(即两个值 - (0, -Inf))但 sub$dv 的长度为 0。如果 dv 是一个简单的向量,那将不是问题,但是因为它在sub列都有两行的数据框中,所以 sub$dv 也必须有两行。

行数为零的原因sub是因为没有q == 1when id == 4

最终的数据框应该没有任何内容id == 4吗?q==1您的问题的答案实际上取决于在没有for 的情况下您想要发生的事情id。请告诉我们,我们可以帮助您编写代码。

更新:

您得到的错误是因为subs$dv其中有 31 个值并且其中new.dv有两个值。

在 R 中,当您尝试将较长的向量分配给较短的向量时,它总是会抱怨。

> test <- data.frame(a=rnorm(100),b=rnorm(100))
> test$a <- rnorm(1000)
Error in `$<-.data.frame`(`*tmp*`, "a", value = c(-0.0507065994549323,  : 
  replacement has 1000 rows, data has 100
> 

但是,当您将较短的向量分配给较长的向量时,只有较短的向量不是较长向量的偶数倍时才会抱怨。(例如 3 不均匀地进入 100)

> test$a <- rnorm(3)
Error in `$<-.data.frame`(`*tmp*`, "a", value = c(-0.897908251650798,  : 
  replacement has 3 rows, data has 100

但是如果你尝试这个,它不会抱怨,因为 2 均匀地进入 100。

> test$a <- rnorm(2)
>

尝试这个:

 > length(test$a)
[1] 100
> length(rnorm(2))
[1] 2
> test$a <- rnorm(2)
> length(test$a)
[1] 100
>

它所做的是默默地重复较短的向量以填充较长的向量。

再一次,您如何解决错误(即使两个向量具有相同的长度)将取决于您要实现的目标。你做new.dv的更短,还是subs$dv更长?

于 2012-07-12T02:19:22.670 回答
5

.(id)在 plyr 中相当于

> dum<-split(long,long$id)
> dum[[4]]
    id time q dv
4.1  4    1 0  1
4.2  4    2 0  1

你的问题是在你的第四次分裂。你参考

answered <- subset(df, q == 1)

在你的功能中。这是一个空集,因为没有dum[[4]]$q取值 1

如果您只想忽略此拆分,则类似

ans<-ddply(long, .(id), function(df) { 
# figure out the dropoff time 
answered <- subset(df, q == 1) 
if(length(answered$q)==0){return()}
last.q = max(answered$time) 
subs <- subset(df, time <= last.q + 1) 
# set all the dv as desired 
new.dv <- rep(last.q,1) 
if (last.q < max(df$time)) new.dv <- c(0,last.q) 
subs$dv <- new.dv 
subs 
})

> ans
  id time q dv
1  1    1 1  2
2  1    2 1  2
3  2    1 1  0
4  2    2 0  1
5  3    1 0  2
6  3    2 1  2

将是结果

于 2012-07-12T02:03:02.710 回答
0

首先,在信用到期时给予信用,下面的代码不是我的。它是与另一位非常慷慨的同事(和工程师)合作生成的,他帮助我解决了我的问题(几个小时!)。

我认为其他负责从调查数据构建删失变量的分析师可能会发现此代码很有用,因此我将解决方案传递下去。

library(plyr)
#A function that only selects cases before the last time "q" was coded as "1"
slicedf <- function(df.orig, df=NULL) {
if (is.null(df)) {
    return(slicedf(df.orig, df.orig))
}
if (nrow(df) == 0) {
    return(df)
}
target <- tail(df, n=1)
   #print(df)
   #print('--------')
   if (target$q == 0) {
       return(slicedf(df.orig, df[1:nrow(df) - 1, ]))
   }
if (nrow(df.orig) == nrow(df)) {
    return(df.orig)
}
return(df.orig[1:(nrow(df) + 1), ])
}
#Applies function to the dataset, and codes over any "0's" before the last "1" as "1"
long <- ddply(long, .(id), function(df) {
df <- slicedf(df)
if(nrow(df) == 0) {
return(df)
}
q <- df$q
if (tail(q, n=1) == 1) {
df$q <- rep(1, length(q))
} else {
df$q <- c(rep(1, length(q) - 1), 0)
}
return(df)
})

感谢所有在线评论您的耐心和帮助的人。

于 2012-07-12T20:12:21.773 回答