Conte o número de observações / linhas por grupo e adicione o resultado ao quadro de dados

Digamos que eu tenha um object 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)) 

Agora quero contar o número de observações de cada combinação de name e type . Isso pode ser feito da seguinte maneira:

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

ou possivelmente também com plyr (embora não tenha certeza de como).

No entanto, como obtenho os resultados incorporados no quadro de dados original? Para que os resultados sejam assim:

 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 

em que count agora armazena os resultados da agregação.

Uma solução com plyr pode ser interessante também, embora eu gostaria de ver como isso é feito com base R.

Usando plyr :

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

Usando data.table :

 library(data.table) dt = data.table(df) # using setkey or setkeyv to set the key setkeyv(dt, c('name', 'type')) # self dt[dt[ , count = length(num), 'name, type']] 

EDITAR (mnel)

O uso da versão 1.8.2 ou superior do data.table possui := por grupo. Existe também o valor .N (introduzido na versão 1.6.2), que é o número de linhas no grupo), por isso é tão fácil quanto

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

Usando dplyr :

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

Com a nova versão do dplyr ( 0.6.0 )

 df %>% add_count(name, type) 

Você pode usar ave :

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

Você consegue fazer isso:

 > 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 

ou talvez mais intuitivamente,

 > 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 

Isso deve fazer o seu trabalho:

 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) 

O aggregate function R base irá obter as contagens com um one-liner, mas adicionar essas contagens de volta ao data.frame original parece levar um pouco de processamento.

 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 

Outra maneira que generaliza mais:

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

Você estava a apenas um passo de incorporar a contagem de linhas ao dataset base.

Usando a function tidy() do pacote broom , converta a tabela de frequência em um frame de dados e junte interna com 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 

Uma alternativa de duas linhas é gerar uma variável de 0s e, em seguida, preenchê-la com split< - , split e lengths como este:

 # 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")])) 

Isso retorna o resultado desejado

 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 

Essencialmente, o RHS calcula os comprimentos de cada combinação de tipo de nome, retornando um vetor nomeado de comprimento 6 com 0s para "red.chair" e "black.plate". Isso é alimentado para o LHS com split < - que usa o vetor e adiciona apropriadamente os valores em seus pontos fornecidos. Isto é essencialmente o que ave faz, como você pode ver que o segundo a linha final de ave é

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

No entanto, lengths é uma versão otimizada do sapply(list, length) .