Contando o número de elementos com os valores de x em um vetor


400

Eu tenho um vetor de números:

numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435,
         453,435,324,34,456,56,567,65,34,435)

Como posso fazer R contar o número de vezes que um valor x aparece no vetor?

Respostas:


505

Você pode apenas usar table():

> a <- table(numbers)
> a
numbers
  4   5  23  34  43  54  56  65  67 324 435 453 456 567 657 
  2   1   2   2   1   1   2   1   2   1   3   1   1   1   1 

Então você pode configurá-lo:

> a[names(a)==435]
435 
  3

Ou converta-o em data.frame se você estiver mais confortável trabalhando com isso:

> as.data.frame(table(numbers))
   numbers Freq
1        4    2
2        5    1
3       23    2
4       34    2
...

21
Não se esqueça de possíveis problemas de ponto flutuante, especialmente com a tabela, que coage números a cadeias.
23411 hadley

4
Esse é um ótimo ponto. Como todos são números inteiros, não é um problema real neste exemplo, certo?
21909 Shane

não exatamente. Os elementos da tabela são da classe número inteiro da classe (tabela (números) [1]), mas 435 é um número de ponto flutuante. Para torná-lo um número inteiro, você pode usar 435L.
Ian Fellows

@Ian - Estou confuso sobre por que 435 é um flutuador neste exemplo. Você pode esclarecer um pouco? obrigado.
Heather Stark

4
Por que não a["435"]insinuar a[names(a)==435]?
pomber

262

A maneira mais direta é sum(numbers == x).

numbers == x cria um vetor lógico que é VERDADEIRO em todos os locais em que x ocorre e quando sum ing, o vetor lógico é coagido para numérico que converte TRUE em 1 e FALSE em 0.

No entanto, nota que, para números de ponto flutuante é melhor usar algo como: sum(abs(numbers - x) < 1e-6).


11
bom ponto sobre a questão do ponto flutuante. Isso morde minha bunda mais do que eu geralmente gosto de admitir.
JD longo

3
@ Jason, embora responda diretamente à pergunta, acho que as pessoas gostaram da solução mais geral que fornece a resposta para todos xos dados, em vez de um valor conhecido específico de x. Para ser justo, era disso que se tratava a pergunta original. Como eu disse em minha resposta abaixo, "Acho que é raro que eu quero saber a frequência de um valor e não todos os valores ..."
JBecker

62

Eu provavelmente faria algo assim

length(which(numbers==x))

Mas realmente, uma maneira melhor é

table(numbers)

10
table(numbers)vai fazer muito mais trabalho do que a solução mais fácil sum(numbers==x), porque também vai descobrir as contagens de todos os outros números da lista.
18110 Ken Williams

11
O problema com a mesa é que é mais difícil para incluí-lo dentro de mais complexo de cálculo, por exemplo, utilizando aplicar () em dataframes
skan

38

Há também count(numbers)do plyrpacote. Muito mais conveniente do que tablena minha opinião.


Existe um equivalente dplyr disso?
stevec

34

Minha solução preferida usa rle, que retornará um valor (o rótulo,x no seu exemplo) e um comprimento, que representa quantas vezes esse valor apareceu em sequência.

Combinando rlecomsort , você tem uma maneira extremamente rápida de contar o número de vezes que qualquer valor apareceu. Isso pode ser útil com problemas mais complexos.

Exemplo:

> numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435,453,435,324,34,456,56,567,65,34,435)
> a <- rle(sort(numbers))
> a
  Run Length Encoding
    lengths: int [1:15] 2 1 2 2 1 1 2 1 2 1 ...
    values : num [1:15] 4 5 23 34 43 54 56 65 67 324 ...

Se o valor que você deseja não aparecer, ou você precisar armazená-lo para mais tarde, faça aa data.frame.

> b <- data.frame(number=a$values, n=a$lengths)
> b
    values n
 1       4 2
 2       5 1
 3      23 2
 4      34 2
 5      43 1
 6      54 1
 7      56 2
 8      65 1
 9      67 2
 10    324 1
 11    435 3
 12    453 1
 13    456 1
 14    567 1
 15    657 1

Acho raro querer saber a frequência de um valor e não todos, e rle parece ser a maneira mais rápida de contar e armazenar todos.


11
A vantagem dessa tabela vs é que resulta em um formato mais fácil de usar? graças
Heather Stark

@HeatherStark, eu diria que existem duas vantagens. O primeiro é definitivamente que é um formato mais facilmente usado do que a saída da tabela. A segunda é que às vezes eu quero contar o número de elementos "em uma linha" em vez de dentro de todo o conjunto de dados. Por exemplo, c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))retornaria values = c('A','G','A','G','C')e lengths=c(3, 4, 1, 2, 10)que às vezes é útil.
JBecker

11
usando microbenchmark, parece que tableé mais rápido when the vector is long(eu tentei 100000), mas ligeiramente mais longo quando mais curto (Tentei 1000)
ClementWalter

Isso será muito lento se você tiver muitos números.
skan

19

Existe uma função padrão em R para esse

tabulate(numbers)


A desvantagem tabulateé que você não pode lidar com números zero e negativos.
omar

2
Mas você pode lidar com zero instâncias de um determinado número, que as outras soluções não lidam
Dodgie

Fantasticamente rápido! E, como diz omar, fornece contagem zero para valores que não aparecem, extremamente útil quando queremos construir uma distribuição de frequência. Números inteiros zero ou negativos podem ser manipulados adicionando uma constante antes de usar tabulate. Nota: sortparece ser necessário para o seu uso correto em geral:tabulate(sort(numbers)) .
Pglpm

11
numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435 453,435,324,34,456,56,567,65,34,435)

> length(grep(435, numbers))
[1] 3


> length(which(435 == numbers))
[1] 3


> require(plyr)
> df = count(numbers)
> df[df$x == 435, ] 
     x freq
11 435    3


> sum(435 == numbers)
[1] 3


> sum(grepl(435, numbers))
[1] 3


> sum(435 == numbers)
[1] 3


> tabulate(numbers)[435]
[1] 3


> table(numbers)['435']
435 
  3 


> length(subset(numbers, numbers=='435')) 
[1] 3

9

aqui está uma maneira rápida e suja:

x <- 23
length(subset(numbers, numbers==x))

9

Se você quiser contar o número de aparências posteriormente, poderá usar a sapplyfunção:

index<-sapply(1:length(numbers),function(x)sum(numbers[1:x]==numbers[x]))
cbind(numbers, index)

Resultado:

        numbers index
 [1,]       4     1
 [2,]      23     1
 [3,]       4     2
 [4,]      23     2
 [5,]       5     1
 [6,]      43     1
 [7,]      54     1
 [8,]      56     1
 [9,]     657     1
[10,]      67     1
[11,]      67     2
[12,]     435     1
[13,]     453     1
[14,]     435     2
[15,]     324     1
[16,]      34     1
[17,]     456     1
[18,]      56     2
[19,]     567     1
[20,]      65     1
[21,]      34     2
[22,]     435     3

Isso é mais rápido que a tabela?
Garini


3

Mais uma maneira que eu acho conveniente é:

numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435,453,435,324,34,456,56,567,65,34,435)
(s<-summary (as.factor(numbers)))

Isso converte o conjunto de dados em fator e, em seguida, o resumo () fornece os totais de controle (contagens dos valores exclusivos).

A saída é:

4   5  23  34  43  54  56  65  67 324 435 453 456 567 657 
2   1   2   2   1   1   2   1   2   1   3   1   1   1   1 

Isso pode ser armazenado como dataframe, se preferir.

as.data.frame (cbind (Number = names (s), Freq = s), stringsAsFactors = F, row.names = 1: length (s))

aqui row.names foi usado para renomear nomes de linhas. sem usar row.names, os nomes de coluna em s são usados ​​como nomes de linha no novo dataframe

A saída é:

     Number Freq
1       4    2
2       5    1
3      23    2
4      34    2
5      43    1
6      54    1
7      56    2
8      65    1
9      67    2
10    324    1
11    435    3
12    453    1
13    456    1
14    567    1
15    657    1

3

Usando tabela, mas sem comparar com names:

numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435)
x <- 67
numbertable <- table(numbers)
numbertable[as.character(x)]
#67 
# 2 

tableé útil quando você estiver usando as contagens de diferentes elementos várias vezes. Se você precisar de apenas uma contagem, usesum(numbers == x)


2

Existem diferentes maneiras de contar elementos específicos

library(plyr)
numbers =c(4,23,4,23,5,43,54,56,657,67,67,435,453,435,7,65,34,435)

print(length(which(numbers==435)))

#Sum counts number of TRUE's in a vector 
print(sum(numbers==435))
print(sum(c(TRUE, FALSE, TRUE)))

#count is present in plyr library 
#o/p of count is a DataFrame, freq is 1 of the columns of data frame
print(count(numbers[numbers==435]))
print(count(numbers[numbers==435])[['freq']])

1

Um método que é relativamente rápido em vetores longos e fornece uma saída conveniente é usar lengths(split(numbers, numbers))(observe o S no final de lengths):

# Make some integer vectors of different sizes
set.seed(123)
x <- sample.int(1e3, 1e4, replace = TRUE)
xl <- sample.int(1e3, 1e6, replace = TRUE)
xxl <-sample.int(1e3, 1e7, replace = TRUE)

# Number of times each value appears in x:
a <- lengths(split(x,x))

# Number of times the value 64 appears:
a["64"]
#~ 64
#~ 15

# Occurences of the first 10 values
a[1:10]
#~ 1  2  3  4  5  6  7  8  9 10 
#~ 13 12  6 14 12  5 13 14 11 14 

A saída é simplesmente um vetor nomeado.
A velocidade parece comparável à rleproposta pelo JBecker e até um pouco mais rápida em vetores muito longos. Aqui está uma marca de microbench no R 3.6.2 com algumas das funções propostas:

library(microbenchmark)

f1 <- function(vec) lengths(split(vec,vec))
f2 <- function(vec) table(vec)
f3 <- function(vec) rle(sort(vec))
f4 <- function(vec) plyr::count(vec)

microbenchmark(split = f1(x),
               table = f2(x),
               rle = f3(x),
               plyr = f4(x))
#~ Unit: microseconds
#~   expr      min        lq      mean    median        uq      max neval  cld
#~  split  402.024  423.2445  492.3400  446.7695  484.3560 2970.107   100  b  
#~  table 1234.888 1290.0150 1378.8902 1333.2445 1382.2005 3203.332   100    d
#~    rle  227.685  238.3845  264.2269  245.7935  279.5435  378.514   100 a   
#~   plyr  758.866  793.0020  866.9325  843.2290  894.5620 2346.407   100   c 

microbenchmark(split = f1(xl),
               table = f2(xl),
               rle = f3(xl),
               plyr = f4(xl))
#~ Unit: milliseconds
#~   expr       min        lq      mean    median        uq       max neval cld
#~  split  21.96075  22.42355  26.39247  23.24847  24.60674  82.88853   100 ab 
#~  table 100.30543 104.05397 111.62963 105.54308 110.28732 168.27695   100   c
#~    rle  19.07365  20.64686  23.71367  21.30467  23.22815  78.67523   100 a  
#~   plyr  24.33968  25.21049  29.71205  26.50363  27.75960  92.02273   100  b 

microbenchmark(split = f1(xxl),
               table = f2(xxl),
               rle = f3(xxl),
               plyr = f4(xxl))
#~ Unit: milliseconds
#~   expr       min        lq      mean    median        uq       max neval  cld
#~  split  296.4496  310.9702  342.6766  332.5098  374.6485  421.1348   100 a   
#~  table 1151.4551 1239.9688 1283.8998 1288.0994 1323.1833 1385.3040   100    d
#~    rle  399.9442  430.8396  464.2605  471.4376  483.2439  555.9278   100   c 
#~   plyr  350.0607  373.1603  414.3596  425.1436  437.8395  506.0169   100  b  

Importante, a única função que também conta o número de valores ausentes NAé plyr::count. Estes também podem ser obtidos separadamente usandosum(is.na(vec))


1

Esta é uma solução muito rápida para vetores atômicos unidimensionais. Ele conta com match(), portanto, é compatível com NA:

x <- c("a", NA, "a", "c", "a", "b", NA, "c")

fn <- function(x) {
  u <- unique.default(x)
  out <- list(x = u, freq = .Internal(tabulate(match(x, u), length(u))))
  class(out) <- "data.frame"
  attr(out, "row.names") <- seq_along(u)
  out
}

fn(x)

#>      x freq
#> 1    a    3
#> 2 <NA>    2
#> 3    c    2
#> 4    b    1

Você também pode ajustar o algoritmo para que ele não seja executado unique().

fn2 <- function(x) {
  y <- match(x, x)
  out <- list(x = x, freq = .Internal(tabulate(y, length(x)))[y])
  class(out) <- "data.frame"
  attr(out, "row.names") <- seq_along(x)
  out
}

fn2(x)

#>      x freq
#> 1    a    3
#> 2 <NA>    2
#> 3    a    3
#> 4    c    2
#> 5    a    3
#> 6    b    1
#> 7 <NA>    2
#> 8    c    2

Nos casos em que essa saída é desejável, você provavelmente nem precisa retornar o vetor original e a segunda coluna é provavelmente tudo o que você precisa. Você pode colocar isso em uma linha com o pipe:

match(x, x) %>% `[`(tabulate(.), .)

#> [1] 3 2 3 2 3 1 2 2

11
Realmente ótima solução! Também é o mais rápido que eu pude criar. Pode ser um pouco melhor para o desempenho da entrada de fator usando u <- if (is.factor (x)) x [! Duplicated (x)] else unique (x).
Taz

0

Isso pode ser feito outerpara obter uma metrix de igualdades seguida por rowSums, com um significado óbvio.
Para ter as contagens e numbersno mesmo conjunto de dados, um data.frame é criado primeiro. Esta etapa não é necessária se você desejar entrada e saída separadas.

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.