Підрахунок кількості елементів зі значеннями 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 Fellows

@Ian - Мене бентежить питання, чому в цьому прикладі 435 є плаваючою точкою. Можете трохи уточнити? Дякую.
Хізер Старк

4
Чому б не a["435"]вторгнутись a[names(a)==435]?
помбер

262

Найбільш прямий спосіб sum(numbers == x).

numbers == xстворює логічний вектор, який є ПРАВИЛЬНИМ у кожному місці, де зустрічається х, і коли suming, логічний вектор примусовий до числового, який перетворює TRUE в 1, а FALSE в 0.

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


1
хороший пункт про питання з плаваючою точкою. Це кусає мою попку більше, ніж я, як правило, хочу визнати.
JD Лонг

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на мою думку.


Чи існує еквівалент дплр цього?
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 ...

Якщо значення , яке ви хочете не відображається, або вам потрібно зберегти це значення для подальших, зробити .adata.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
за допомогою microbenchmark, здається, tableце швидше when the vector is long(я спробував 100000), але трохи довше, коли він коротший (я спробував 1000)
ClementWalter

Це буде дуже повільно, якщо у вас буде чимало номерів.
скан

19

Для цього існує стандартна функція в R

tabulate(numbers)


Недоліком tabulateє те, що ви не можете мати справу з нульовими та від’ємними числами.
омар

2
Але ви можете мати справу з нульовими екземплярами заданої кількості, з якими не вирішуються інші рішення
Dodgie

Фантастично швидко! І як каже омар, він дає нульовий підрахунок для не з'являються значень, що дуже корисно, коли ми хочемо побудувати розподіл частоти. Нульові чи негативні цілі числа можна обробити, додавши константу перед використанням 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

Якщо ви хочете згодом порахувати кількість показів, ви можете скористатися 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)))

Це перетворює набір даних у коефіцієнт, а потім підсумок () дає нам контрольні підсумки (підраховує унікальні значення).

Вихід:

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 (Кількість = імена (ів), Freq = s), stringsAsFactors = F, row.names = 1: length (s))

тут імена рядків використовувались для перейменування імен рядків. без використання рядків.імена, імена стовпців у 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 [! Дублюється (x)] else 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.