在R中优化类似sumif的查询

时间:2016-11-14 18:32:48

标签: r sumifs

我有一个包含180万行的数据帧,每行包含一个或多个30,000个标记。我正在尝试查找有多少行包含每个标记的实例。有些项目包含多达25个标签,每个标签都在自己的列中。没有行包含任何标记的多个实例:

ItemNo <- c(1, 2, 3, 4)  
Tag1 <- c("ZZZ", "AAA", "BBB", "YYY")  
Tag2 <- c("YYY2", "ZZZ", "AAA", "XXX")  
Tag3 <- c("", "YYY2", "AAA2", "XXX3")  
Tag4 <- c("", "", "", "AAA")  
Tag5 <- c("", "", "", "ZZZ")  
Tag6 <- c("", "", "", "YYY2")  
Items <- data.frame(ItemNo, Tag1, Tag2, Tag3, Tag4, Tag5, Tag6)  
Items

  ItemNo Tag1 Tag2 Tag3 Tag4 Tag5 Tag6
1      1  ZZZ YYY2       
2      2  AAA  ZZZ YYY2   
3      3  BBB  AAA AAA2   
4      4  YYY  XXX XXX3  AAA  ZZZ YYY2

标签世界位于一个单独的数据集中:

Code <- c("AAA", "BBB", "XXX", "ZZZ", "AAAA", "XXX3", "YYY2")  
COUNT <- c(0, 0, 0, 0, 0, 0, 0)  
tags <- data.frame(Code, COUNT) 
tags 

  Code COUNT  
1  AAA     0  
2  BBB     0  
3  XXX     0  
4  ZZZ     0  
5 AAAA     0  
6 XXX3     0  
7 YYY2     0  

我想结束这样的事情:

   Code COUNT  
1  AAA     3  
2  BBB     1  
3  XXX     1  
4  ZZZ     3  
5 AAAA     0  
6 XXX3     1  
7 YYY2     3   

我可以通过for循环获得良好的结果,这需要大约3个小时来运行数据集:

for (i in 1:nrow(tags)) {tags[i,2] <- sum(Items[,2:7] ==
as.character(tags[i,1]), na.rm = TRUE)}

是否有更有效或更优雅的方法来计算此数据集中每个标记的实例?

2 个答案:

答案 0 :(得分:1)

让我们举一个与你的规模接近的例子:

n = 1e6
ncol = 25
ItemNo <- 1:n
tags = c("", do.call(paste0, expand.grid(LETTERS, LETTERS, LETTERS, stringsAsFactors = FALSE)))
item_tags = sample(tags, size = n * ncol, replace = T)

Items <- cbind.data.frame(ItemNo, matrix(item_tags, ncol = ncol)) 

上面有25个标记列,100万行,26 ^ 3 + 1 = 17577个唯一标记。

system.time(table(unlist(Items[-1])))
#   user  system elapsed 
# 15.077   1.001  16.277 

在除了第一列(未列出)之外的所有内容上运行table,我的笔记本电脑上大约需要15秒。输出应该是可用的:

tt = table(unlist(Items[-1]))
head(tt)
#       AAA  AAB  AAC  AAD  AAE 
# 1421 1451 1456 1479 1440 1449 

转换为数据框以匹配您想要的输出:

dd = data.frame(tt)
head(dd)
#   Var1 Freq
# 1      1421
# 2  AAA 1451
# 3  AAB 1456
# 4  AAC 1479
# 5  AAD 1440
# 6  AAE 1449

请注意,它还会计算空白 - 您可能希望将这些空白分组。

修改:要获得更快的速度,您可以使用tabulate而不是tabletable有我们在这种情况下不需要的选项 - tabulate是一个精简版本。在我的计算机上使用tabulate而不是table在10秒内运行。

答案 1 :(得分:0)

我无法测试大数据集的速度,但我猜这会更快,因为它使用apply代替for循环:

Sums <- data.frame(Code = Code,
                   COUNT = unlist(lapply(Code, function(i)
                             sum(apply(Items, 1, function(x) any(i %in% x))))))

COUNT =行的中心开始,这种方法:

  • 沿着行使用apply来获得一个逻辑,指示可能代码的向量中的给定项是否出现在该行中。
  • 在调用apply的调用中调用lapply,将该流程应用于代码矢量(Codes)中的每个项目并对结果求和。
  • 取消列出结果列表,使其成为矢量。
  • 将该矢量放在数据框中,旁边显示相关代码。

结果:

> Sums
  Code COUNT
1  AAA     3
2  BBB     1
3  XXX     1
4  ZZZ     3
5 AAAA     0
6 XXX3     1
7 YYY2     3