获取出现在另一个数据框中的特定值的计数

时间:2017-09-14 14:04:04

标签: r dplyr match countif

这个问题听起来可能与其他问题相似,但我希望它与众不同。 我想获取一个特定的值列表,并计算它们出现在另一个值列表中的频率,其中非出现值被重新调整为“0”。

我有一个数据框(df1),其中包含以下值:

Items <- c('Carrots','Plums','Pineapple','Turkey')   
df1<-data.frame(Items)

>df1
Items
1   Carrots
2     Plums
3 Pineapple
4    Turkey

第二个数据框(df2),其中包含一个名为“Thing”的列:

> head(df2,n=10)
  ID       Date     Thing
1  58150 2012-09-12  Potatoes
2  12357 2012-09-28   Turnips
3  50788 2012-10-04   Oranges
4  66038 2012-10-11  Potatoes
5  18119 2012-10-11   Oranges
6  48349 2012-10-14   Carrots
7  23328 2012-10-16   Peppers
8  66038 2012-10-26 Pineapple
9  32717 2012-10-28   Turnips
10 11345 2012-11-08   Oranges

我知道“土耳其”这个词只出现在df1而不是df2中。我想返回df1中出现的频率表或项目数,df2中出现的项目,并返回“0”表示土耳其的数量。

如何使用另一个值中的值汇总数据框列的值?我得到的最接近的是:

df2%>% count (Thing) %>% filter(Thing %in% df1$Items,)

但是这会返回在df1和df2之间过滤的项目列表,因此“土耳其”被排除在外。太近了!

> df2%>% count (Thing) %>% filter(Thing %in% df1$Items,)
# A tibble: 3 x 2
      Thing     n
     <fctr> <int>
1   Carrots    30
2 Pineapple    30
3     Plums    38

我希望我的输出看起来像这样:

1   Carrots    30
2 Pineapple    30
3     Plums    38
4    Turkey     0

我对R很新,对dplyr来说是全新的。

4 个答案:

答案 0 :(得分:0)

我一直都在使用这种东西。我确信有一种更精明的编码方式,但这就是我得到的:

item <- vector()
count <- vector()
items <- list(unique(df1$Items))

for (i in 1:length(items)){
    item[i] <- items[i]
    count[i] <- sum(df2$Thing == item)
}

df3 <- data.frame(cbind(item, count))

希望这有帮助!

答案 1 :(得分:0)

Stephen的解决方案稍作修改,在count [i]行末尾将[i]添加到项目中。见下文:

item <- vector() 
count <- vector()

for (i in 1:length(unique(Items))){ 
    item[i] <- Items[i] 
    count[i]<- sum(df2$Thing == item[i])
} 

df3 <- data.frame(cbind(item, count))

> df3
       item count
1   Carrots    30
2     Plums    38
3 Pineapple    30
4    Turkey     0

答案 2 :(得分:0)

dplyr 会丢弃0个计数行,并且您会增加一个复杂性,即Thing的可能类别在您的两个数据集之间有所不同。

如果您将因子级别从df1添加到df2,则可以使用 tidyr 中的complete,这是{{3}的常用方式}。

我正在使用名为df1的包 forcats 中的便捷函数将因子级别从df2添加到fct_expand

library(dplyr)
library(tidyr)
library(forcats)

df2 %>% 
    mutate(Thing = fct_expand(Thing, as.character(df1$Item) ) ) %>%
    count(Thing) %>% 
    complete(Thing, fill = list(n = 0) ) %>%
    filter(Thing %in% df1$Items,)

答案 3 :(得分:0)

另一种方法是首先汇总df2,然后与df1进行正确联接(以选择df1的所有行),并将NA替换为零。< / p>

library(dplyr)
df2 %>% 
  count(Thing) %>% 
  right_join(unique(df1), by = c("Thing" = "Items")) %>% 
  mutate(n = coalesce(n, 0L))
# A tibble: 4 x 2
      Thing     n
      <chr> <int>
1   Carrots     1
2     Plums     0
3 Pineapple     1
4    Turkey     0
Warning message:
Column `Thing`/`Items` joining factors with different levels, coercing to character vector

data.table中的相同方法:

library(data.table)
setDT(df2)[, .N, by = Thing][unique(setDT(df1)), on = .(Thing = Items)][is.na(N), N := 0L][]
       Thing N
1:   Carrots 1
2:     Plums 0
3: Pineapple 1
4:    Turkey 0

请注意,在两个实现中,unique(df1)用于避免在连接后出现意外的重复行。

如果df2很大且df1只包含少量Items,则首先加入然后合并可能更有效:

library(dplyr)
df2 %>% 
  right_join(unique(df1), by = c("Thing" = "Items")) %>% 
  group_by(Thing) %>% 
  summarise(n = sum(!is.na(ID)))
# A tibble: 4 x 2
      Thing     n
      <chr> <int>
1   Carrots     1
2 Pineapple     1
3     Plums     0
4    Turkey     0
Warning message:
Column `Thing`/`Items` joining factors with different levels, coercing to character vector

data.table syntax中的相同内容:

library(data.table)
setDT(df2)[unique(setDT(df1)), on = .(Thing = Items)][, .(N = sum(!is.na(ID))), by = Thing][]
       Thing N
1:   Carrots 1
2:     Plums 0
3: Pineapple 1
4:    Turkey 0