统计每组的行数,并将结果添加到原始数据框中。

67

假设我有一个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现在存储了聚合后的结果。

使用plyr的解决方案也很有趣,但我想看看如何使用基本的R实现。

12个回答

86

使用 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))

你需要使用 "setkeyv(dt, c('name', 'type'))" 吗? - skan

31
你可以使用ave:
df$count <- ave(df$num, df[,c("name","type")], FUN=length)

1
也许可以更加简洁地使用 transform(df, count = ave(num, name, type, FUN = length)) 或者 with - David Arenburg
如果你有大量的数据,这个命令会非常慢。 - luchonacho

8
你可以这样做:
> 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

5
这应该可以完成你的工作:
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)

3
基础的R函数`aggregate`可以一行代码获取计数,但将这些计数添加回原始的`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))
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

2

使用dplyr中的add_tally是另一种选择。以下是一个可复制的示例:

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年9月11日,使用 reprex v2.0.2 工具。


2

使用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
你只需要将行数合并到基础数据集中,就可以完成最后一步了。
使用包中的tidy()函数,将频率表转换为数据框,并与df进行内部连接。
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

1
一种两行的替代方法是生成一个全为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与split <-一起使用,它将向量添加到它们给定的位置。这基本上就是ave所做的,因为你可以看到ave的倒数第二行是

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

然而,lengthssapply(list, length)的优化版本。

1

在基础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]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接