Подсчет количества элементов со значениями x в векторе


400

У меня есть вектор чисел:

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

Как можно R посчитать, сколько раз значение x появляется в векторе?

Ответы:


505

Вы можете просто использовать 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 

Тогда вы можете сделать это:

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

Или конвертируйте его в data.frame, если вам удобнее работать с этим:

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

21
Не забывайте о потенциальных проблемах с плавающей точкой, особенно с таблицей, которая приводит числа к строкам.
Хэдли

4
Это отличный момент. Это все целые числа, так что в данном примере это не проблема, верно?
Шейн

не совсем. Элементы таблицы имеют класс целочисленных классов (таблица (числа) [1]), но 435 - это число с плавающей запятой. Чтобы сделать его целым числом, вы можете использовать 435L.
Ян Феллоуз

@Ian - Я смущен тем, почему 435 является поплавком в этом примере. Можешь немного уточнить? Спасибо.
Хизер Старк

4
Почему не a["435"]взамен a[names(a)==435]?
pomber

262

Самый прямой путь sum(numbers == x).

numbers == xсоздает логический вектор, который имеет значение ИСТИНА в каждом месте, где происходит x, и при sumэтом логический вектор приводится к числовому значению, которое преобразует ИСТИНА в 1 и ЛОЖЬ в 0.

Тем не менее, обратите внимание , что для чисел с плавающей точкой, лучше использовать что - то вроде: sum(abs(numbers - x) < 1e-6).


1
Хороший вопрос о проблеме с плавающей запятой. Это кусает мою задницу больше, чем я обычно хотел бы признать.
JD Long

3
@ Jason, хотя он и отвечает на вопрос напрямую, я предполагаю, что людям понравилось более общее решение, которое дает ответ для всех xв данных, а не конкретное известное значение x. Чтобы быть справедливым, это было то, о чем был первоначальный вопрос. Как я сказал в своем ответе ниже: «Я нахожу, что редко я хочу знать частоту одного значения, а не всех значений ...»
JBecker,

62

Я бы наверное сделал что то подобное

length(which(numbers==x))

Но на самом деле, лучший способ

table(numbers)

10
table(numbers)собирается сделать гораздо больше работы, чем простейшее решение, sum(numbers==x)потому что он также рассчитает количество всех других чисел в списке.
Кен Уильямс

1
проблема с таблицей состоит в том, что ее сложнее включить в более сложное исчисление, например, с помощью apply () на
фреймах данных

38

Есть также count(numbers)из plyrпакета. Гораздо удобнее, чем tableна мой взгляд.


Есть ли dplyr эквивалент этого?
Stevec

34

Мое предпочтительное решение использует rle, которое будет возвращать значение (метка, xв вашем примере) и длину, которая представляет, сколько раз это значение появилось в последовательности.

Комбинируя rleс sort, вы получаете очень быстрый способ подсчитать, сколько раз появилось любое значение. Это может быть полезно при более сложных проблемах.

Пример:

> 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 ...

Если нужное значение не отображается или вам нужно сохранить это значение для дальнейшего использования, сделайте 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

Я нахожу, что редко я хочу знать частоту одного значения, а не всех значений, и кажется, что rle - это самый быстрый способ подсчитать и сохранить их все.


1
Преимущество этого, по сравнению с таблицей, в том, что он дает результат в более удобном для использования формате? спасибо
Хизер Старк

@HeatherStark Я бы сказал, что есть два преимущества. Во-первых, это, безусловно, более удобный формат, чем вывод таблицы. Во-вторых, иногда я хочу посчитать количество элементов «подряд», а не внутри всего набора данных. Например, c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))вернется values = c('A','G','A','G','C')и lengths=c(3, 4, 1, 2, 10)что иногда полезно.
JBecker,

1
с помощью микробенчмарка tableполучается, что он быстрее when the vector is long(я пробовал 100000), но немного дольше, когда он короче (я пробовал 1000)
ClementWalter

Это будет очень медленно, если у вас много цифр.
Скан

19

Для этого есть стандартная функция в R

tabulate(numbers)


Недостатком tabulateявляется то, что вы не можете иметь дело с нулевыми и отрицательными числами.
Омар

2
Но вы можете иметь дело с нулем экземпляров данного числа, которое другие решения не обрабатывают
Доджи

Фантастически быстро! И, как говорит Омар, он дает нулевой счет для не появляющихся значений, что чрезвычайно полезно, когда мы хотим построить распределение частоты. Нулевые или отрицательные целые числа могут быть обработаны путем добавления константы перед использованием tabulate. Примечание: sortпредставляется необходимым для его правильного применения в целом: 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

вот один быстрый и грязный способ:

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

9

Если вы хотите посчитать количество появлений впоследствии, вы можете использовать sapplyфункцию:

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

Вывод:

        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

Это быстрее, чем таблица?
Гарини


3

Еще один способ, который я считаю удобным:

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

Это преобразует набор данных в фактор, а затем summary () дает нам контрольные суммы (количество уникальных значений).

Выход:

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 

Это может быть сохранено как датафрейм при желании.

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

здесь row.names был использован для переименования имен строк. без использования row.names имена столбцов в s используются как имена строк в новом фрейме данных

Выход:

     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

Используя таблицу, но не сравнивая с 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полезно, когда вы используете количество различных элементов несколько раз. Если вам нужен только один счет, используйтеsum(numbers == x)


2

Существуют разные способы подсчета конкретных элементов

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

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

Выход просто именованный вектор.
Скорость кажется сравнимой с rleпредложенной JBecker и даже немного выше на очень длинных векторах. Вот микробенчмарк в R 3.6.2 с некоторыми из предложенных функций:

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  

Важно отметить, что единственной функцией, которая также подсчитывает количество пропущенных значений, NAявляется plyr::count. Их также можно получить отдельно, используяsum(is.na(vec))


1

Это очень быстрое решение для одномерных атомных векторов. Опирается наmatch() , поэтому он совместим с 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

Вы также можете настроить алгоритм так, чтобы он не работал 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

В тех случаях, когда этот вывод желателен, вам, вероятно, даже не нужно возвращать исходный вектор, а второй столбец, вероятно, - все, что вам нужно. Вы можете получить это в одну строку с трубкой:

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

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

1
Действительно отличное решение! Это также самый быстрый, который я мог придумать. Это может быть немного улучшено для производительности для факторного ввода, используя u <- if (is.factor (x)) x [! Duplicated (x)] иначе unique (x).
Таз

0

Это можно сделать, outerчтобы получить метрику равенств rowSumsс очевидным значением.
Чтобы иметь счетчики и numbersв одном наборе данных, сначала создается data.frame. Этот шаг не нужен, если вы хотите раздельный ввод и вывод.

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.