我有一个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)
但它不起作用,我们将非常感谢指导。 谢谢
答案 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的行数(包括当前行)。然后使用subset
将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))
答案 2 :(得分:2)
以下是使用base R
,table
和rowSums
的另一种%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
中的计数就可以获得所需的结果