按计数进行子集

时间:2018-04-17 02:22:27

标签: r count dplyr subset

我有一个data.frame

library(dplyr)

ID <- c(1,1,1,1,2,2,3,3,3,3,4,4,5)
Score <- c(20,22,34,56,78,98,56,43,45,33,24,54,22)
Quarter <- c("Q1","Q2","Q3","Q4","Q1","Q2","Q1","Q2","Q3","Q4","Q1","Q2","Q1")
df <- data.frame(ID,Score,Quarter)

我只想处理所有4个季度的数据(Q1,Q2,Q3,Q4在“Quarters”栏目中)。我认为可以做到这一点的一种方法是当ID出现4次时的子集,因为它在每个季度重复出现。我很难对ID计数进行子设置。我试过了:

filter(df, count(df, vars = ID)==4)

但它不起作用,我们将非常感谢指导。 谢谢

4 个答案:

答案 0 :(得分:3)

我们可以做的一种方法是使用n_distinct为每个ID获取唯一值,并过滤包含所有4个值的组。

library(dplyr)
df %>%
   group_by(ID) %>%
   filter(n_distinct(Quarter) == 4)


#    ID Score Quarter
#  <dbl> <dbl> <fct>  
#1  1.00  20.0 Q1     
#2  1.00  22.0 Q2     
#3  1.00  34.0 Q3     
#4  1.00  56.0 Q4     
#5  3.00  56.0 Q1     
#6  3.00  43.0 Q2     
#7  3.00  45.0 Q3     
#8  3.00  33.0 Q4     

使用ave的等效基础R实现将是

df[as.numeric(ave(df$Quarter, df$ID, FUN = function(x) length(unique(x)))) == 4, ]

答案 1 :(得分:3)

以下是一些替代方案。最后三个是基本解决方案。

#1是一个SQL解决方案,它创建一个单列数据框df0,只有那些具有4个季度的ID然后加入df,从而消除所有其他ID。

#2是一个dplyr解决方案,可过滤仅保留4行的组。

#3是一个data.table解决方案,它返回那些具有4行且其他组为NULL的ID组的行。这具有消除其他群体的效果。

#4是动物园解决方案,它将df转换为宽格式动物园对象,其顶部为四分之一,ID为时间索引。然后它删除具有NA的任何行,并使用fortify.zoo重新整形回原始行,同时重新排序回排序顺序。如果行顺序无关紧要,则可以省略解决方案的最后一行。有趣的是,它没有使用数字4的知识。

#5是一个基本解决方案,它将df拆分为数据帧列表,每个ID一个,然后使用Filter提取具有4行的数据帧。最后它把它们全部重新组合在一起。

#6是一个基本解决方案,它创建一个向量,每行df包含一个元素,其中包含该行中具有ID的行数(包括当前行)。然后使用subsetdf缩减为该向量等于4的行。

#7是一个基本解决方案,它将df拆分为一个数据帧列表,每个ID一个,然后使用Reduce迭代它将当前数据帧附加到我们目前为止的数据帧如果它有4行或者只保留我们迄今为止所拥有的行。

# 1
library(sqldf)
sqldf("with df0 as (
  select ID from df group by ID having count(*) = 4
)
select * from df join df0 using (ID)")

# 2
library(dplyr)
df %>% group_by(ID) %>% filter(n() == 4) %>% ungroup

# 3 
library(data.table)
as.data.table(df)[, if (nrow(.SD) == 4) .SD, by = ID]

# 4
library(zoo)
z <- read.zoo(df, split = "Quarter")
df2 <- fortify.zoo(na.omit(z), melt = TRUE, names = names(df)[c(1, 3:2)])
df2 <- df2[order(df2$ID, df2$Quarter), ]

# 5
do.call("rbind", Filter(function(x) nrow(x) == 4, split(df, df$ID)))

# 6
subset(df, ave(ID, ID, FUN = length) == 4)

# 7
Reduce(function(x, y) if (nrow(y) == 4) rbind(x, y) else x, split(df, df$ID))

答案 2 :(得分:2)

以下是使用base RtablerowSums的另一种%in%方法。我们得到{ID','Quarter'列的频率计数为table,将其转换为逻辑matrix,其中0值为TRUE,其他所有值为FALSE(!table(...)),得到行sum(rowSums),转换为逻辑vector,获取TRUE元素的names,并使用%in%subset创建与ID的比较数据集

subset(df, ID %in% names(which(!rowSums(!table(df[c(1,3)])))))
#   ID Score Quarter
#1   1    20      Q1
#2   1    22      Q2
#3   1    34      Q3
#4   1    56      Q4
#7   3    56      Q1
#8   3    43      Q2
#9   3    45      Q3
#10  3    33      Q4

答案 3 :(得分:1)

我发现我也能做到这一点:

df[df$ID %in% names(table(df$ID))[table(df$ID)==4],]

仅使用ID

中的计数就可以获得所需的结果