ddply()脚本故障排除

时间:2012-07-12 01:32:17

标签: r

我正在开发一个审查的因变量,用于生存分析。我的目标是找到有人在调查中回答问题的最后时间(“时间”)(例如,“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变量,FOR SURE 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) 

即,$时间&lt; = last.q + 1 $将行数设置为EQUAL到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

3 个答案:

答案 0 :(得分:5)

plyr中的

.(id)相当于

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

你的问题出在第4次分裂。你参考

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

将是结果

答案 1 :(得分:5)

简而言之:错误是因为q == 1时没有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. 的数据框
  3. 创建多个子集数据帧(每个不同的ID一个)
  4. 将函数myfun应用于每个子集化数据框
  5. 将结果重新组合为单个数据框
  6. 因此,让我们尝试手动执行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 == 1时没有id == 4

    最终数据框架中是否没有id == 4的任何内容?问题的答案实际上取决于当q==1没有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更长?

答案 2 :(得分: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)
})

感谢网上的所有人,感谢您的耐心和帮助。