Como adicionar contagem de valores exclusivos por grupo para R data.frame

Desejo contar o número de valores exclusivos por meio do agrupamento de uma segunda variável e, em seguida, adicionar a contagem ao data.frame existente como uma nova coluna. Por exemplo, se o quadro de dados existente for assim:

color type 1 black chair 2 black chair 3 black sofa 4 green sofa 5 green sofa 6 red sofa 7 red plate 8 blue sofa 9 blue plate 10 blue chair 

Eu quero adicionar para cada color , a contagem de types exclusivos que estão presentes nos dados:

  color type unique_types 1 black chair 2 2 black chair 2 3 black sofa 2 4 green sofa 1 5 green sofa 1 6 red sofa 2 7 red plate 2 8 blue sofa 3 9 blue plate 3 10 blue chair 3 

Eu estava esperando para usar ave , mas não consigo encontrar um método simples que não requer muitas linhas. Eu tenho> 100.000 linhas, então também não tenho certeza de quão importante é a eficiência.

É um pouco semelhante a este problema: Conte o número de observações / linhas por grupo e adicione o resultado ao quadro de dados

    Usando ave (desde que você peça especificamente):

     within(df, { count < - ave(type, color, FUN=function(x) length(unique(x)))}) 

    Certifique-se de que o type seja vetor de caractere e não fator.


    Como você também diz que seus dados são enormes e que a velocidade / desempenho pode, portanto, ser um fator, sugiro também uma solução data.table .

     require(data.table) setDT(df)[, count := uniqueN(type), by = color] # v1.9.6+ # if you don't want df to be modified by reference ans = as.data.table(df)[, count := uniqueN(type), by = color] 

    uniqueN foi implementado na v1.9.6 e é um equivalente mais rápido de length(unique(.)) . Além disso, também funciona com data.frames / data.tables.


    Outras soluções:

    Usando plyr:

     require(plyr) ddply(df, .(color), mutate, count = length(unique(type))) 

    Usando aggregate :

     agg < - aggregate(data=df, type ~ color, function(x) length(unique(x))) merge(df, agg, by="color", all=TRUE) 

    Aqui está uma solução com o pacote dplyr – ele tem n_distinct() como um wrapper para length(unique()) .

     df %>% group_by(color) %>% mutate(unique_types = n_distinct(type)) 

    Isto também pode ser conseguido em um vectorized sem por operações de grupo, combinando unique com table ou tabulate

    Se df$color for factor , então

    Ou

     table(unique(df)$color)[as.character(df$color)] # black black black green green red red blue blue blue # 2 2 2 1 1 2 2 3 3 3 

    Ou

     tabulate(unique(df)$color)[as.integer(df$color)] # [1] 2 2 2 1 1 2 2 3 3 3 

    Se df$color for character , apenas

     table(unique(df)$color)[df$color] 

    Se df$color é um integer então apenas

     tabulate(unique(df)$color)[df$color]