通过对 R 中的聚类数据中的值数求和来进行子集化

Subsetting by summing number of values in clustered data in R

我正在尝试解决数据格式问题。 我有一个数据框,其中变量分为学校和学生。例如:

Schools  Students

SchoolA  Student1
SchoolA  Student2
SchoolA  Student3

SchoolB  Student4
SchoolB  Student5

SchoolC  Student6

我想创建我的数据集的一个子集,只保留学生人数超过 X(例如,至少 2 人)的学校。 在 R 中有一种简单的方法可以做到这一点吗? 我怎样才能用每所学校的学生人数创建一个额外的变量(列),以便我可以基于它进行子集化? 预先感谢您的帮助!

编辑 - - - - - -

非常感谢您的回复。我创建了一个示例以供将来参考。

 # creating a dataset
 Schools <- c('SchA','SchA','SchA','SchA','SchA',
   'SchB','SchB','SchB','SchB','SchB','SchB',
   'SchC','SchC')
 Students <- c('st1','st2','st3','st4','st5','st6',
          'st7','st8','st9', 'st10', 'st11', 'st12', 'st13')
 df <- data.frame(Schools, Students)

 install.packages('data.table')
 library(data.table)
 setDT(df)[, if(.N > 4) .SD, Schools] # only schools A & B

 df[ with(df, as.numeric(ave(as.character(Students), Schools, FUN=length))) >2, ]

有没有办法创建一个附加变量(学校规模)来保存每所学校的学生人数(特定学校独有)? 如果我可以有这样的变量,我可以基于它进行子集化。

这成功了:

 dat[ with(dat, as.numeric(ave(as.character(Students), Schools, FUN=length))) >2, ]

第一个测试用例只有你的例子,但我注意到 ave 函数周围没有 as.numeric,我得到的是一个字符结果,担心它可能无法通过 " 120" > "20" 将返回 FALSE。添加 as.numeric 解决了这个问题。对于 Arenberg 对学生姓名可能重复的担忧,我们可以肯定地将 unique() 包裹在 ave-FUN 中的长度参数周围。

这将创建分类变量:

dat$snum <-with(dat, as.numeric( ave( as.character(Students), Schools, FUN=length))) 

使用data.table

library(data.table)#v1.9.5+
setDT(df)[, if(.N > 1) .SD, Schools]

也使用@flodel 在重复问题中的回答作为灵感:

dat[table(Students)[Students] >2]

如果条件是根据长度unique个'Students'个'Schools'

setDT(df)[, if(uniqueN(Students) > 1) .SD, Schools]

使用 dplyr

的类似方法
library(dplyr)
df %>% 
   group_by(Schools) %>% 
   filter(n_distinct(Students) > 1)
   #or depending on the condition
   #filter(n() > 1)

更新

如果您需要在子集化之前创建列(使用新数据集)

setDT(df)[, no.of.students := .N, Schools][, if(.N  > 4) .SD, Schools]
#     Schools Students no.of.students
# 1:    SchA      st1              5
# 2:    SchA      st2              5
# 3:    SchA      st3              5
# 4:    SchA      st4              5
# 5:    SchA      st5              5
# 6:    SchB      st6              6
# 7:    SchB      st7              6
# 8:    SchB      st8              6
# 9:    SchB      st9              6
#10:    SchB     st10              6
#11:    SchB     st11              6

df %>% 
   group_by(Schools) %>% 
   mutate(no.of.students=n()) %>%
   filter(n()>4)

使用lapply()

 student.count = 2 # depends on your choice 
 out = do.call(rbind, 
       lapply(split(df, f = df$Schools), 
       function(x){ 
       x$no.of.students = length(x$Students);
       x = subset(x, no.of.students > student.count)
       }))

#> out
#        Schools Students no.of.students
#SchA.1     SchA      st1              5
#SchA.2     SchA      st2              5
#SchA.3     SchA      st3              5
#SchA.4     SchA      st4              5
#SchA.5     SchA      st5              5
#SchB.6     SchB      st6              6 
#SchB.7     SchB      st7              6
#SchB.8     SchB      st8              6
#SchB.9     SchB      st9              6
#SchB.10    SchB     st10              6
#SchB.11    SchB     st11              6