R 按计数分组

R 按计数分组,r,count,dplyr,subset,R,Count,Dplyr,Subset,我有一个数据框 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) 但它并没有起作用,我们将非

我有一个数据框

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)
但它并没有起作用,我们将非常感谢它的指导。
谢谢

我们可以做的一种方法是使用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     
下面是另一个使用table、rowSums和%的base R方法。我们通过表获得“ID”、“Quarter”列的频率计数,将其转换为逻辑矩阵,其中0值为真,其他值为假!表…,获取行和行和,转换为逻辑向量,获取为TRUE的元素的名称,并使用%in%创建与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

我想我也能做到:

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

它只使用ID中的计数就可以得到所需的结果。这里有一些替代方法。最后三个是基本解决方案

1是一个SQL解决方案,它创建一个单列数据帧df0,其中只有那些ID有4个四分之一,然后将这些ID连接到df,从而消除所有其他ID

2是一个dplyr解决方案,它过滤只保留4行的组

3是一个data.table解决方案,它为那些具有4行的ID组返回行,为其他组返回NULL。这会消除其他组的影响

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

5是一个基本解决方案,它将df拆分为一个数据帧列表,每个ID一个,然后使用过滤器提取具有4行的数据帧。最后,它把一切都重新组合起来

6是一个基本解决方案,它创建一个向量,每行df有一个元素,包含行数,包括该行中ID为的当前行。然后使用子集将df减少到向量等于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))
df[df$ID %in% names(table(df$ID))[table(df$ID)==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))