Подсчитайте количество строк в группе и добавьте результат в исходный фрейм данных

#r #count #aggregate

#r #граф #агрегировать #r-часто задаваемые вопросы

Вопрос:

Допустим, у меня есть 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))
  

Теперь я хочу подсчитать количество строк (наблюдений) для каждой комбинации name и type . Это можно сделать следующим образом:

 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 помощью которого также может быть интересно изучить, хотя я хотел бы посмотреть, как это делается с помощью base R.

Ответ №1:

Использование 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))
  

Комментарии:

1. Вам нужен «setkeyv(dt, c(‘name’, ‘type’))»?

Ответ №2:

Вы можете использовать ave :

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

Комментарии:

1. Также можно было бы сделать это немного чище, возможно, используя transform(df, count = ave(num, name, type, FUN = length)) или with

2. Если у вас много данных , эта команда выполняется ОЧЕНЬ МЕДЛЕННО

Ответ №3:

Вы можете сделать это:

 > 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
  

Ответ №4:

Это должно выполнить вашу работу :

 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)
  

Ответ №5:

Базовая 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
  

Ответ №6:

Использование пакета 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
  

Ответ №7:

Альтернативой из двух строк является создание переменной, равной 0s, а затем заполнение ее с split<- помощью , split , и lengths вот так:

 # 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 с 0 для «red.chair» и «black.plate». Это передается в LHS, с split <- помощью которого берется вектор и соответствующим образом добавляются значения в заданных местах. Это, по сути, то, что ave делает, поскольку вы можете видеть, что предпоследняя строка ave

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

Тем не менее, lengths это оптимизированная версия sapply(list, length) .

Ответ №8:

Вы были всего в одном шаге от включения количества строк в базовый набор данных.

Используя tidy() функцию из broom пакета, преобразуйте таблицу частот в фрейм данных и внутреннее соединение с 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
  

Ответ №9:

Одна простая строка в базе 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]
  

Ответ №10:

Другой вариант с использованием 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 с помощью reprex v2.0.2

Ответ №11:

Другой способ, который обобщает больше:

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

Комментарии:

1. Пожалуйста, объясните, как это обобщает больше?