计算值 A 在没有值 B 的情况下出现的次数,反之亦然

Count the amount of times value A occurs without value B and vice versa

我无法弄清楚如何做与这个问题的答案相反的事情(在 R 中不是 python)。

基本上我有一个包含很多列对组合的数据框,如下所示:

df <- data.frame(id1 = c("1","1","1","1","2","2","2","3","3","4","4"),
                 id2 = c("2","2","3","4","1","3","4","1","4","2","1"))

我想计算一下,A 列中的所有值在整个数据框中出现的频率,而没有 B 列中的值。因此,这个小示例的结果将是以下输出:

df_result <- data.frame(id1 = c("1","1","1","2","2","2","3","3","4","4"),
                        id2 = c("2","3","4","1","3","4","1","4","2","1"),
                        count = c("4","5","5","3","5","4","2","3","3","3"))

这方面的重要标准是,最终结果数据帧按对折叠(因此在我的示例中,第 1 行和第 2 行是重复的,并且它们被折叠并按总频率求和,观察到 1 而没有观察到 2) .为了计算出现次数,检查两列很重要。 IE。列的顺序对于计算频率无关紧要 - 如果 A 列有 1,B 有 2,这与 A 列有 2,B 有 1 的计数相同。

我可以通过对每一对进行过滤来非常缓慢地执行此操作,但对于我有很多不同对的真实数据来说,这并不真正可行。

非常感谢任何指导。

首先paste将两个id列拼起来id12,以便后面匹配。然后使用 sapply 遍历所有行以查看 id1 出现在 id12id2 没有出现的记录。 sum 那个值,只输出 distinct 条记录。最后,删除 id12 列。

library(dplyr)

df %>% mutate(id12 = paste0(id1, id2),
              count = sapply(1:nrow(.), 
                             function(x) 
                               sum(grepl(id1[x], id12) & !grepl(id2[x], id12)))) %>% 
  distinct() %>% 
  select(-id12)

或完全以 R 为基础:

id12 <- paste0(df$id1, df$id2)
df$count <- sapply(1:nrow(df), function(x) sum(grepl(df$id1[x], id12) & !grepl(df$id2[x], id12)))
df <- df[!duplicated(df),]

输出

   id1 id2 count
1    1   2     4
2    1   3     5
3    1   4     5
4    2   1     3
5    2   3     5
6    2   4     4
7    3   1     2
8    3   4     3
9    4   2     3
10   4   1     3

完整 tidyverse 版本:

library(tidyverse)
df %>% 
  mutate(id = paste(id1, id2),
         count = map(cur_group_rows(), ~ sum(str_detect(id, id1[.x]) & str_detect(id, id2[.x], negate = T))))

一种更有效的方法是使用表格格式:

tab = crossprod(table(rep(seq_len(nrow(df)), ncol(df)), c(df$id1, df$id2)))
#tab
#   
#    1 2 3 4
#  1 7 3 2 2
#  2 3 6 1 2
#  3 2 1 4 1
#  4 2 2 1 5

所以,现在,我们有了每个值与另一个值一起出现的时间(不管它们在两列中的顺序如何)。在这里,我们需要一种方法将上述 table 按每对进行子集化,并从每个 id 的总外观值中减去它们的共现值。

制作所有组合的网格:

gr = expand.grid(id1 = colnames(tab), id2 = rownames(tab), stringsAsFactors = FALSE)

创建 2 列矩阵以对 table:

进行子集化
id1.ij = cbind(match(gr$id1, colnames(tab)), 
               match(gr$id1, rownames(tab)))
id2.ij = cbind(match(gr$id1, colnames(tab)), 
               match(gr$id2, rownames(tab)))

减去各自的值:

cbind(gr, count = tab[id1.ij] - tab[id2.ij])
#   id1 id2 count
#1    1   1     0
#2    2   1     3
#3    3   1     2
#4    4   1     3
#5    1   2     4
#6    2   2     0
#7    3   2     3
#8    4   2     3
#9    1   3     5
#10   2   3     5
#11   3   3     0
#12   4   3     4
#13   1   4     5
#14   2   4     4
#15   3   4     3
#16   4   4     0

当然,如果我们不需要完整的网格值,我们可以设置:

gr = unique(df)

这导致:

#   id1 id2 count
#1    1   2     4
#3    1   3     5
#4    1   4     5
#5    2   1     3
#6    2   3     5
#7    2   4     4
#8    3   1     2
#9    3   4     3
#10   4   2     3
#11   4   1     3