对组的组使用setdiff()而不进行循环

时间:2017-05-03 08:59:05

标签: r dataframe

我有一个数据框df

    id year groupid
1   A1 2000      G1
2   A1 2000      G1
3   A1 2000      G1
4   A2 2000      G2
5   A1 2001      G1
6  A12 2001      G1
7  A13 2001      G1
8   A3 2001      G2
9  A33 2001      G2
10  A4 2001      G3
11  A4 2002      G3
12  A5 2002      G3
13  A5 2003      G2
14  A6 2003      G4

我想要做的是在两个连续两年相同setdiff()的值之间使用groupid

示例:

对于2000年,G1有一个idA1。对于20001年,G1有三个不同的idA1A12A13。因此,当在这两者之间应用setdiff()时,它将返回2。在2001年至2002年期间,由于2002年没有G1,因此将给出0的价值。对于相同的groupid,如果两年不连续,则给定的值将是0如果该组在第二年不存在或者是不同的id。< / p>

预期结果:

   year groupid newid
1  2000    G1    1
2  2000    G2    1
3  2000    G3    0
4  2000    G4    0
5  2001    G1    2
6  2001    G2    2
7  2001    G3    1
8  2001    G4    0
9  2002    G1    0
10 2002    G2    0
11 2002    G3    2
12 2002    G4    0
13 2003    G1    0
14 2003    G2    1
15 2003    G3    0
16 2003    G4    1

我使用for loop,一些if()dplyr函数完成了此操作,但由于我有很多行,所以需要花费太多时间(大约5分钟)。所以我正在寻找用一些dplyr函数或data.table函数替换循环,以便在更短的时间内完成此任务。

数据:

structure(list(id = c("A1", "A1", "A1", "A2", "A1", "A12", "A13", 
"A3", "A33", "A4", "A4", "A5", "A5", "A6"), year = c(2000, 2000, 
2000, 2000, 2001, 2001, 2001, 2001, 2001, 2001, 2002, 2002, 2003, 
2003), groupid = c("G1", "G1", "G1", "G2", "G1", "G1", "G1", 
"G2", "G2", "G3", "G3", "G3", "G2", "G4")), .Names = c("id", 
"year", "groupid"), row.names = c(NA, -14L), class = "data.frame")

编辑:修改了示例

1 个答案:

答案 0 :(得分:3)

此解决方案是我使用基座R tidyr使用dplyr@jogo以及aggregate(drop = FALSE)(现已删除)答案的初始解决方案的组合:

df <- read.table(header = T, stringsAsFactors = F, text = 
"    id year groupid
1   A1 2000      G1
2   A1 2000      G1
3   A1 2000      G1
4   A2 2000      G2
5   A1 2001      G1
6  A12 2001      G1
7  A13 2001      G1
8   A3 2001      G2
9  A33 2001      G2
10  A4 2001      G3
11  A4 2002      G3
12  A5 2002      G3
13  A5 2003      G2
14  A6 2003      G4")

library(dplyr)
df %>% 
  aggregate(id ~ year + groupid, ., unique, drop = FALSE) %>% 
  group_by(groupid) %>% 
  arrange(year) %>% 
  mutate(new_ids = mapply(setdiff, id, lag(id)),
         newid = lapply(new_ids, length)) %>% 
  ungroup() %>% 
  arrange(year, groupid) %>% 
  as.data.frame()

#    year groupid           id  new_ids newid
# 1  2000      G1           A1       A1     1
# 2  2000      G2           A2       A2     1
# 3  2000      G3                           0
# 4  2000      G4                           0
# 5  2001      G1 A1, A12, A13 A12, A13     2
# 6  2001      G2      A3, A33  A3, A33     2
# 7  2001      G3           A4       A4     1
# 8  2001      G4                           0
# 9  2002      G1                           0
# 10 2002      G2                           0
# 11 2002      G3       A4, A5       A5     1
# 12 2002      G4                           0
# 13 2003      G1                           0
# 14 2003      G2           A5       A5     1
# 15 2003      G3                           0
# 16 2003      G4           A6       A6     1  

编辑:解释,回答评论:

  • , .,部分是df应该是aggregate()的第二个参数,而不是第一个(参见help("%>%"))。但实际上它是为了缩短它,你最好使用更长,更健壮的形式和命名参数,例如:aggregate(formula = id ~ year + groupid, data = ., FUN = unique, drop = FALSE)

  • 添加group_by()是使lag()一致使用所必需的。如果没有群组,我们会承担将2000, G2的ID与2003, G1的ID(aggregate()之后的连续行)进行比较的风险。我们真正想要的是setdiff(..., lag(...groupid组内发生,行按year排序(因此可能不需要arrange(year))。缺少此group_by()对此示例没有任何影响,但可能会在您的真实数据中生成一个。