根据列值为唯一(A,B)或共享(两个)组添加新列,并按ID分组

时间:2015-10-15 00:21:25

标签: r group-by compare aggregate

我的数据表格式如下:

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

我想制作一个按 id 分组的新列,其值反映相应的 source 值(即。 A, B, or Both)如果 both idA相对应,则会使用B

我希望输出如此:

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.

,则可获得奖励

3 个答案:

答案 0 :(得分: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列属于因子类。

答案 1 :(得分: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替换为具有两行的组。

答案 2 :(得分:2)

或使用

'url' => env('APP_URL'),

事实上,理查德的这种数据方法将比dplyr / ifelse或dplyr / if-else甚至数据表版本更快。

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
相关问题