计算每组的行数并将结果添加到原始数据框

Count number of rows per group and add result to original data frame

提问人:Uri Laserson 提问时间:9/17/2011 最后编辑:HenrikUri Laserson 更新时间:10/24/2023 访问量:125156

问:

假设我有一个对象:data.frame

df <- data.frame(name=c('black','black','black','red','red'),
                 type=c('chair','chair','sofa','sofa','plate'),
                 num=c(4,5,12,4,3))

现在我想计算 和 的每个组合的行数(观测值)。这可以像这样完成:nametype

table(df[ , c("name","type")])

或者也可能是 ,(尽管我不确定如何)。plyr

但是,如何将结果合并到原始数据框中?因此,结果将如下所示:

df
#    name  type num count
# 1 black chair   4     2
# 2 black chair   5     2
# 3 black  sofa  12     1
# 4   red  sofa   4     1
# 5   red plate   3     1

现在存储聚合的结果。count

学习的解决方案也可能很有趣,尽管我想看看这是如何使用基础 R 完成的。plyr

计数 聚合 R-FAQ

评论


答:

8赞 joran 9/17/2011 #1

您可以这样做:

> ddply(df,.(name,type),transform,count = NROW(piece))
   name  type num count
1 black chair   4     2
2 black chair   5     2
3 black  sofa  12     1
4   red plate   3     1
5   red  sofa   4     1

或者更直观地说,

> ddply(df,.(name,type),transform,count = length(num))
   name  type num count
1 black chair   4     2
2 black chair   5     2
3 black  sofa  12     1
4   red plate   3     1
5   red  sofa   4     1
31赞 Joshua Ulrich 9/17/2011 #2

您可以使用:ave

df$count <- ave(df$num, df[,c("name","type")], FUN=length)

评论

1赞 David Arenburg 2/5/2019
也可以做得更干净一些,也许使用或transform(df, count = ave(num, name, type, FUN = length))with
0赞 luchonacho 10/19/2021
如果您有大量数据,则此命令为 SUPERSLOW
86赞 Ramnath 9/17/2011 #3

使用 data.table

library(data.table)
dt = as.data.table(df)

# or coerce to data.table by reference:
# setDT(df)

dt[ , count := .N, by = .(name, type)]

有关预备选项,请参阅编辑历史记录。data.table 1.8.2


使用 dplyr

library(dplyr)
df %>%
  group_by(name, type) %>%
  mutate(count = n())

或者简单地说:

add_count(df, name, type)

使用 plyr

plyr::ddply(df, .(name, type), transform, count = length(num))

评论

0赞 skan 12/14/2016
你需要“setkeyv(dt, c('name', 'type'))”吗?
-2赞 Uri Laserson 2/20/2013 #4

另一种概括更多的方式:

df$count <- unsplit(lapply(split(df, df[c("name","type")]), nrow), df[c("name","type")])

评论

11赞 smci 7/18/2013
请解释一下这如何进一步概括?
3赞 Mark Miller 1/14/2017 #5

基本函数将使用单行代码获取计数,但将这些计数添加回原始计数似乎需要一些处理。Raggregatedata.frame

df <- data.frame(name=c('black','black','black','red','red'),
                 type=c('chair','chair','sofa','sofa','plate'),
                 num=c(4,5,12,4,3))
df
#    name  type num
# 1 black chair   4
# 2 black chair   5
# 3 black  sofa  12
# 4   red  sofa   4
# 5   red plate   3

rows.per.group  <- aggregate(rep(1, length(paste0(df$name, df$type))),
                             by=list(df$name, df$type), sum)
rows.per.group
#   Group.1 Group.2 x
# 1   black   chair 2
# 2     red   plate 1
# 3   black    sofa 1
# 4     red    sofa 1

my.summary <- do.call(data.frame, rows.per.group)
colnames(my.summary) <- c(colnames(df)[1:2], 'rows.per.group')
my.data <- merge(df, my.summary, by = c(colnames(df)[1:2]))
my.data
#    name  type num rows.per.group
# 1 black chair   4              2
# 2 black chair   5              2
# 3 black  sofa  12              1
# 4   red plate   3              1
# 5   red  sofa   4              1
5赞 Palash Jhamb 2/24/2017 #6

这应该做你的工作:

df_agg <- aggregate(num~name+type,df,FUN=NROW)
names(df_agg)[3] <- "count"
df <- merge(df,df_agg,by=c('name','type'),all.x=TRUE)
1赞 lmo 9/5/2017 #7

两行替代方案是生成一个 0 的变量,然后用 、 填充它,如下所示:split<-splitlengths

# generate vector of 0s
df$count <-0L

# fill it in
split(df$count, df[c("name", "type")]) <- lengths(split(df$num, df[c("name", "type")]))

这将返回所需的结果

df
   name  type num count
1 black chair   4     2
2 black chair   5     2
3 black  sofa  12     1
4   red  sofa   4     1
5   red plate   3     1

从本质上讲,RHS 计算每个名称类型组合的长度,返回一个长度为 6 的命名向量,其中 “red.chair” 和 “black.plate” 的 0 个。这被馈送到 LHS,LHS 使用它获取向量并在其给定点中适当地添加值。这基本上就是这样,正如你所看到的,倒数第二行是split <-aveave

split(x, g) <- lapply(split(x, g), FUN)

但是,是 的优化版本。lengthssapply(list, length)

1赞 RobertF 2/24/2018 #8

您距离将行计数合并到基础数据集中仅一步之遥。

使用包中的函数,将频率表转换为数据帧和内部连接:tidy()broomdf

df <- data.frame(name=c('black','black','black','red','red'),
                         type=c('chair','chair','sofa','sofa','plate'),
                         num=c(4,5,12,4,3))
library(broom)
df <- merge(df, tidy(table(df[ , c("name","type")])), by=c("name","type"))
df
   name  type num Freq
1 black chair   4    2
2 black chair   5    2
3 black  sofa  12    1
4   red plate   3    1
5   red  sofa   4    1
2赞 zx8754 12/4/2018 #9

使用 sqldf 包:

library(sqldf)

sqldf("select a.*, b.cnt
       from df a,
           (select name, type, count(1) as cnt
            from df
            group by name, type) b
      where a.name = b.name and
            a.type = b.type")

#    name  type num cnt
# 1 black chair   4   2
# 2 black chair   5   2
# 3 black  sofa  12   1
# 4   red  sofa   4   1
# 5   red plate   3   1
1赞 gaspar 9/7/2020 #10

基数 R 中的一行简单:

df$count = table(interaction(df[, (c("name", "type"))]))[interaction(df[, (c("name", "type"))])]

为了清晰/高效,两行相同:

fact = interaction(df[, (c("name", "type"))])
df$count = table(fact)[fact]
2赞 Quinten 9/12/2022 #11

另一个选项使用 add_tally from .下面是一个可重现的例子:dplyr

df <- data.frame(name=c('black','black','black','red','red'),
                 type=c('chair','chair','sofa','sofa','plate'),
                 num=c(4,5,12,4,3))
library(dplyr)
df %>%
  group_by(name, type) %>%
  add_tally(name = "count")
#> # A tibble: 5 × 4
#> # Groups:   name, type [4]
#>   name  type    num count
#>   <chr> <chr> <dbl> <int>
#> 1 black chair     4     2
#> 2 black chair     5     2
#> 3 black sofa     12     1
#> 4 red   sofa      4     1
#> 5 red   plate     3     1

创建于 2022-09-11 with reprex v2.0.2

0赞 Maël 10/24/2023 #12

在 中,使用 .fcount 明显比任何其他选项都快。collapsefcount

library(collapse)
df |> 
  fcount(name, type, add = TRUE, name = "count")

#    name  type num count
# 1 black chair   4     2
# 2 black chair   5     2
# 3 black  sofa  12     1
# 4   red  sofa   4     1
# 5   red plate   3     1