2016-06-21 56 views
0

我有一个不平衡面板(对于大约800k个人有700万观察值)。我想创建一个等于1的新变量,如果该人曾对另一个问题回复yes。具体来说,我想创建一个假人,如果个人吸烟经常吸烟,就等于1。R:如何按组扩展条件

所以我们可以说我的数据集看起来是这样的(其中ID是因人而异的,有的人被采访了很多时间和其他人只有一次):

ID Smoke 
1  No 
1  No 
1  Yes 
1  No 
2  No 
2  No 
3  Yes 
3  No 

我想要生成可变Ever_Smoked,看起来是这样的:

ID Smoke Ever_Smoked 
1  No   1 
1  No   1 
1  Yes   1 
1  No   1 
2  No   0 
2  No   0 
3  Yes   1 
3  No   1 

如何做到这一点任何想法? 在此先感谢!

+0

@Angustin Indaco您可以按照您尝试的方式使用4个空格作为缩进来格式化表格(这使得它们成为源代码)。 – geekoverdose

+0

谢谢,我试图弄清楚如何做到这一点。很长一段时间,StackOverflow的狂热读者,第一次发布一些东西。 –

回答

4

下面是使用ave()一个基础R溶液:

df$Ever_Smoked <- ave(+(df$Smoke=='Yes'),df$ID,FUN=max); 
df; 
## ID Smoke Ever_Smoked 
## 1 1 No   1 
## 2 1 No   1 
## 3 1 Yes   1 
## 4 1 No   1 
## 5 2 No   0 
## 6 2 No   0 
## 7 3 Yes   1 
## 8 3 No   1 

数据

df <- data.frame(ID=c(1L,1L,1L,1L,2L,2L,3L,3L),Smoke=c('No','No','Yes','No','No','No','Yes', 
'No'),stringsAsFactors=F); 

巧合的是,today I did some benchmarking on different ways of converting a logical vector to an integer vector,发现一元加号是最快的。这就是为什么我选择它作为我在这里的答案。

当然,对ave()的调用会产生显着的性能影响,至少与data.table的索引实现相关。因此,为了获得最佳性能,我建议您使用rafa的data.table实现,但使用unary plus解决方案将逻辑转换为整数。

+0

是的,这非常快。而且我似乎可以很好地工作。谢谢! –

1

您可以沿着ID分割你的数据和相应的值分配给每个单独的子集:

d <- data.frame(ID = c(rep(1,4), rep(2,2), rep(3,2)), Smoke=c('No', 'No', 'Yes', rep('No', 3), 'Yes', 'No')) 

library(plyr) 
d2 <- ldply(split(d, d$ID), function(d_tmp) { 
    d_tmp$Ever_Smoked <- ifelse(all(d_tmp$Smoke=='No'), 0, 1) 
    d_tmp 
})[,-1] 

...这应该是你打算要获取的内容:

> print(d2) 

    ID Smoke Ever_Smoked 
1 1 No   1 
2 1 No   1 
3 1 Yes   1 
4 1 No   1 
5 2 No   0 
6 2 No   0 
7 3 Yes   1 
8 3 No   1 
5

鉴于大小的数据集,基于data.table的解决方案可能是最好的/最快的替代方案

library(data.table) 

setDT(df)[, Ever_Smoked := as.numeric(any(Smoke=="Yes")), by = ID] 
使用由@bgoldst提供的样本数据

性能测试

df <- data.frame(ID=c(1L,1L,1L,1L,2L,2L,3L,3L),Smoke=c('No','No','Yes','No','No','No','Yes','No'),stringsAsFactors=F) 

# make it a 8 million row dataset 
df <- df[rep(seq_len(nrow(df)), 1000000), ] 

system.time(setDT(df)[, Ever_Smoked := as.numeric(any(Smoke=="Yes")), by = ID]) 

#> user system elapsed 
#> 0.27 0.01 0.32 
+0

我同意这一点 - 猜我应该建议更多的'data.table'解决方案; – geekoverdose

+2

虽然这并没有给出正确的结果,因为并非所有Ever_Smoked案件最终成为一个组。你需要像setDT(df)[,Ever_Smoked:= as.numeric(any(Smoke ==“Yes”)),by = ID]'来代替。 – thelatemail

+0

好眼@thelatemail。 5 upvoters(包括我)没有注意到它... – bgoldst