R 按属性分组行

R 按属性分组行,r,filter,dataframe,R,Filter,Dataframe,我有一个数据框,其中包含关于学生上课迟到的数据。每行包含有关迟到学生及其班级的数据:班级日期和时间、班级名称、班级规模、迟到分钟数以及学生性别。为了得到所有班级迟到学生的总百分比,我需要计算行数(迟到学生),并将其与上课的学生总数进行比较 我不能简单地计算所有行的班级人数;这将对给定班级的学生进行多次统计,每班迟到一名学生统计一次。相反,我只需要为班级的每次会议计算一次班级人数 例子 关键:迟到分钟数、班级名称、出席人数、迟到学生性别、迟到分钟数 11/12/10 Stats 30 M 1 11

我有一个数据框,其中包含关于学生上课迟到的数据。每行包含有关迟到学生及其班级的数据:班级日期和时间、班级名称、班级规模、迟到分钟数以及学生性别。为了得到所有班级迟到学生的总百分比,我需要计算行数(迟到学生),并将其与上课的学生总数进行比较

我不能简单地计算所有行的班级人数;这将对给定班级的学生进行多次统计,每班迟到一名学生统计一次。相反,我只需要为班级的每次会议计算一次班级人数

例子 关键:迟到分钟数、班级名称、出席人数、迟到学生性别、迟到分钟数

11/12/10 Stats 30 M 1
11/12/10 Stats 30 M 1
11/12/10 Stats 30 M 1
11/15/10 Stats 40 F 3
11/15/10 Stats 40 F 3
11/15/10 Stats 40 F 3
11/16/10 Radar 22 M 2
11/16/10 Radar 22 M 2
11/16/10 Radar 22 M 2
11/16/10 Radar 22 M 2
11/16/10 Radar 22 M 2

在这种情况下,有三个不同的班会和11个迟到的学生。如何确保每次班会的班级人数只计算一次?

对于迟到总数和班级人数有不同的函数。需要使用“粘贴”策略创建数据和类名的唯一组合:

>  sum_late <- tapply( tst$V5, paste(tst$V1, tst$V2, sep="_"), length)
>  csize <- tapply( tst$V3, paste(tst$V1, tst$V2, sep="_"), head,1)
> pct_late <- 100*sum_late/csize
> pct_late
11/12/10_Stats 11/15/10_Stats 11/16/10_Radar 
      10.00000        7.50000       22.72727 
>sum\u late csize pct\u late pct\u late
11/12/10_统计11/15/10_统计11/16/10_雷达
10.00000        7.50000       22.72727 
或与骨料:

>  dfcount <- aggregate( tst$V5, list(tst$V1, tst$V2), length)
> dfcount$pct <- 100*aggregate( tst$V5, list(tst$V1, tst$V2), length)$x/aggregate( tst$V3, list(tst$V1, tst$V2), head,1)$x
> dfcount
   Group.1 Group.2 x      pct
1 11/16/10   Radar 5 22.72727
2 11/12/10   Stats 3 10.00000
3 11/15/10   Stats 3  7.50000
>dfcount dfcount$pct dfcount
第1组,第2组
1 2010年11月16日雷达5 22.72727
2010年11月12日统计数据310.00000
2010年11月15日统计数据37.50000

如果我正确理解了您的需求,那么使用plyr软件包比使用tapply或by更容易做到这一点,因为它了解多变量分组的含义。例如:

ddply(df,.(日期,类别),转换,延迟百分比=长度(延迟分钟数)/类别大小)

此处length的参数可以是任何列名。ddply将为日期和类别因子级别的每个组合拆分数据帧。每个迷你数据框中的行数应与迟到学生的数量相对应(因为每个迟到学生都有一个条目)。这就是长度(任何变量)的作用。将其除以分数的“类大小”列。

编辑:我的解决方案可以简单得多,方法是首先计算每行延迟的微小百分比,然后使用
聚合()
按日期和类对这些百分比求和:

> df2 <- within(df, pcLate <- 100 * (1 / Size)) 
> df2
         Date Class Size Sex MinsLate   pcLate
1  2010-11-12 Stats   30   M        1 3.333333
2  2010-11-12 Stats   30   M        1 3.333333
3  2010-11-12 Stats   30   M        1 3.333333
4  2010-11-15 Stats   40   F        3 2.500000
5  2010-11-15 Stats   40   F        3 2.500000
6  2010-11-15 Stats   40   F        3 2.500000
7  2010-11-16 Radar   22   M        2 4.545455
8  2010-11-16 Radar   22   M        2 4.545455
9  2010-11-16 Radar   22   M        2 4.545455
10 2010-11-16 Radar   22   M        2 4.545455
11 2010-11-16 Radar   22   M        2 4.545455
> with(df2, aggregate(pcLate, by = list(Date = Date, Class = Class), sum))
        Date Class        x
1 2010-11-16 Radar 22.72727
2 2010-11-12 Stats 10.00000
3 2010-11-15 Stats  7.50000
这给了我们这个起点

> head(summ)
        Date Class nLate
1 2010-11-16 Radar     5
2 2010-11-12 Stats     3
3 2010-11-15 Stats     3
然后形成班级规模:

summ$Size <- with(df, aggregate(Size, by = list(Date = Date, Class = Class),
                                FUN = unique)$x)
然后计算延迟的百分比:

summ <- within(summ, pcLate <- 100 * (nLate / Size))
如果您需要经常这样做,请将其包装到函数中

tardiness <- function(df) {
    out <- with(df, aggregate(MinsLate, by = list(Date = Date, Class = Class),
                              FUN = length))
    names(out)[3] <- "nLate"
    out$Size <- with(df, aggregate(Size, by = list(Date = Date, Class = Class),
                                   FUN = unique)$x)
    out <- within(out, pcLate <- 100 * (nLate / Size))
    out
}

要继续@Gavin关于冗余输出的评论,请使用摘要:

df.out <- ddply(x, .(DATE, CLASS), summarise    
    , NLATE = length(c(DATE, CLASS)) / 2
    , SIZE = unique(CLASS.SIZE)
    , PCLATE = 100 * (length(c(DATE, CLASS)) / 2 )/ unique(CLASS.SIZE)
    )
> df.out
      DATE CLASS NLATE SIZE PCLATE
1 11/12/10 Stats     3   30  10.00
2 11/15/10 Stats     3   40   7.50
3 11/16/10 Radar     5   22  22.73
df.out df.out
日期类NLATE大小PCLATE
2010年11月12日统计数据33010.00
2010年11月15日统计数据3407.50
2010年11月16日雷达5 22.73

注意,第5列(
tst$V5
在您的示例中)是每个学生迟到的分钟数,而不是迟到的学生数。因此,您希望在
tapply
调用中使用
length
而不是
sum
。请注意,有11名迟到的学生,您的结果显示有22名迟到的学生。回答不错(+1)。我对plyr套餐不熟悉。是否有一种方法可以使用plyr(即修改您的答案)根据我的答案提供聚合到类/日期级别的输出?我的回答涉及几个步骤,但给出了(IHMO;-)更简洁的输出。您的回答简明扼要,但输出包含冗余的数据复制。[这不是挖苦-如果plyr能做得比我的答案更简单,我真的很感兴趣。]@Gavin-功能
摘要
可能就是您在这里寻找的。从帮助页面“Summary以一种分析的方式进行转换,除了不向现有数据框添加列之外,它会创建一个新的数据框。”感谢Chase在下面的评论和回答。当我从桌子下面出来时,我必须仔细看看plyr。@Gavin-另一个可能更清楚的选择是在@frannkc的答案周围加上一个
unique()
summ <- within(summ, pcLate <- 100 * (nLate / Size))
> head(summ)
        Date Class nLate Size   pcLate
1 2010-11-16 Radar     5   22 22.72727
2 2010-11-12 Stats     3   30 10.00000
3 2010-11-15 Stats     3   40  7.50000
tardiness <- function(df) {
    out <- with(df, aggregate(MinsLate, by = list(Date = Date, Class = Class),
                              FUN = length))
    names(out)[3] <- "nLate"
    out$Size <- with(df, aggregate(Size, by = list(Date = Date, Class = Class),
                                   FUN = unique)$x)
    out <- within(out, pcLate <- 100 * (nLate / Size))
    out
}
> tardiness(df)
        Date Class nLate Size   pcLate
1 2010-11-16 Radar     5   22 22.72727
2 2010-11-12 Stats     3   30 10.00000
3 2010-11-15 Stats     3   40  7.50000
df.out <- ddply(x, .(DATE, CLASS), summarise    
    , NLATE = length(c(DATE, CLASS)) / 2
    , SIZE = unique(CLASS.SIZE)
    , PCLATE = 100 * (length(c(DATE, CLASS)) / 2 )/ unique(CLASS.SIZE)
    )
> df.out
      DATE CLASS NLATE SIZE PCLATE
1 11/12/10 Stats     3   30  10.00
2 11/15/10 Stats     3   40   7.50
3 11/16/10 Radar     5   22  22.73