2015-10-15 18 views
1

我的数据表是以下形式:基于列的值,并通过ID分组唯一的(A,B)或共享(两者)组添加新的列

id source 
1  A 
1  B 
2  A 
3  B 
4  A 
4  B 

我想使一个新的这是由id分组并具有反射凡both如果id对应于两个AB将被用于相应的source值(即A, B, or Both)的值的列。

我所要的输出是这样:

id source source_group 
1  A   both 
1  B   both 
2  A   A 
3  B   B 
4  A   both 
4  B   both 

奖金,如果你可以把它的通用处理额外值source,如A, B, C, D, ... etc.

回答

5

仅供参考,这里是一个可以说是比较合适的基准:

library(data.table) 
library(dplyr) 
library(microbenchmark) 

DT = data.table(id=seq(1e5))[, 
    .(source = c(if (runif(1) > .5) "A", if (runif(1) > .5)"B")), by=id] 
DF = data.frame(DT) 

microbenchmark(
dplyr = 
    DF %>% group_by(id) %>% mutate(gr = if(n()>1) "both" else as.character(source)), 
dplyr_dt = 
    DT %>% group_by(id) %>% mutate(gr = if(n()>1) "both" else as.character(source)), 
ave = DF$gr <- 
    ave(as.character(DF$source), DF$id, FUN = function(x) if(length(x) > 1) "both" else x), 
dt = DT[, gr := if (.N > 1) "both" else as.character(source), by=id], 
dt2 = DT[, 
    gr := as.character(source)][ DT[, if (.N > 1) 1, by=id][, V1 := NULL], 
    gr := "both", on = "id"], 
    times=10) 

结果:

Unit: milliseconds 
    expr  min   lq  mean  median   uq  max neval 
    dplyr 1200.13579 1215.56997 1328.73931 1245.81556 1252.66023 1828.02921 10 
dplyr_dt 38.43108 41.58004 47.98858 43.89661 49.27464 68.64005 10 
     ave 149.67549 153.03421 167.09148 163.19261 181.60074 191.22481 10 
     dt 32.31500 33.60741 41.00644 35.80188 37.60350 65.76292 10 
     dt2 25.99567 26.44592 28.11141 28.19138 28.55474 31.42691 10 

我不知道为什么ave做得更糟在这里。也许这是因为@bunk说,ave不能很好地扩展到很多团体。 Dplyr在data.frame上速度较慢,但​​在使用data.table后端(如所宣传的)时速度更快。

对于它的价值,我的data.table解决方案是一个有点不同(证明一个单独的答案?):

DT[, 
    gr := as.character(source) 
][DT[, if (.N > 1) 1, by=id][, V1 := NULL], 
    gr := "both" 
, on = "id"] 

首先,它gr等于给source,然后将其与both代替它那些有两排的组。

+1

nvm我说过,''ave'运行时间的一半以上都花在'交互'上 – jenesaisquoi

+0

对于任何人都注意:对此感到抱歉。我有一个或两个错误。除了现在我们可以看到dplyr在data.frame上很慢之外,结果没有实质性改变。正如dplyr人们所说,如果你需要速度,你可以使用带有data.table“后端”的dplyr。 – Frank

5

你可以使用ave()

df$source_group <- with(df, { 
    ave(as.character(source), id, FUN=function(x) if(length(x) > 1) "both" else x) 
}) 

其中给出

df 
# id source source_group 
# 1 1  A   both 
# 2 1  B   both 
# 3 2  A   A 
# 4 3  B   B 
# 5 4  A   both 
# 6 4  B   both 

或者像大卫建议,我们可以使用data.table

library(data.table) 
setDT(df)[, source_group := if(.N > 1) "both" else as.character(source), by = id] 

这给

df 
# id source source_group 
# 1: 1  A   both 
# 2: 1  B   both 
# 3: 2  A   A 
# 4: 3  B   B 
# 5: 4  A   both 
# 6: 4  B   both 

注意,这两个假设source列是要素类。

+3

我更想的是比较'df'中的整体唯一值,比如'library(data.table); setDT(df)[,source_group:= if(setequal(unique(source),unique(df $ source)))“both”else as.character(source),by = id]'作为更一般的解决方案。 –

2

或者使用

library(dplyr) 
library(tidyr) 
tab%>% 
    group_by(id)%>% 
    mutate(gr = ifelse(length(source)==2, 'both', source)) 

    id source gr 
1 1  A both 
2 1  B both 
3 2  A A 
4 3  B B 
5 4  A both 
6 4  B both 

事实上理查德的AVE方法,有了这些数据,会比dplyr/ifelse或dplyr/ifelse甚至DataTable的版本速度更快。

library(microbenchmark) 
bench <- microbenchmark(
    Dplyr=tab%>% 
    group_by(id)%>% 
    mutate(gr = ifelse(length(source)==2, 'both', source)), 
    Dplyr2=tab%>% 
    group_by(id)%>% 
    mutate(gr = if(length(source)==2) 'both' else source), 
    Ave = tab$source_group <- with(tab, { 
    source <- as.character(source) 
    ave(source, id, FUN = function(x) if(length(x) > 1) "both" else x) 
    }), 
    DT=setDT(tab)[, source_group := if(setequal(unique(source) 
               , unique(tab$source))) "both" else as.character(source) 
       , by = id] 
    , times=1000L) 

>bench 
Unit: microseconds 
    expr  min  lq  mean median  uq  max neval 
    Dplyr 2576.491 2917.6415 4172.977 3213.224 4582.343 21174.76 1000 
Dplyr2 2568.691 2899.7835 3972.390 3191.671 4613.338 16547.67 1000 
    Ave 668.344 792.3245 1134.236 854.109 1293.993 18753.86 1000 
    DT 1083.801 1201.2130 1622.587 1285.577 1779.035 10232.48 1000 
+1

速度差异可能是由于'ifelse' vs Richard的'if' /'else',尽管我希望看到'ave'更快,是的。 – Frank

+1

哦,顺便说一句,你可能想要在比较几行的大表上做这样的基准测试。对于'Dplyr2',你可以使用'n()'代替'length(源)' – Frank

+0

对于60000行数据帧,dplyr会更快。 –

相关问题