Đếm số phần tử với các giá trị của x trong một vectơ


400

Tôi có một vectơ số:

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

Làm thế nào tôi có thể có R đếm số lần giá trị x xuất hiện trong vectơ?

Câu trả lời:


505

Bạn chỉ có thể sử dụng 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 

Sau đó, bạn có thể đặt nó:

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

Hoặc chuyển đổi nó thành data.frame nếu bạn cảm thấy thoải mái hơn khi làm việc với điều đó:

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

21
Đừng quên các vấn đề về dấu phẩy động tiềm năng, đặc biệt là với bảng, ép các số thành chuỗi.
hadley

4
Đó là một điểm tuyệt vời. Đây là tất cả các số nguyên, vì vậy nó không phải là một vấn đề thực sự trong ví dụ này, phải không?
Shane

không chính xác. Các phần tử của bảng là lớp số nguyên lớp (bảng (số) [1]), nhưng 435 là số dấu phẩy động. Để làm cho nó một số nguyên, bạn có thể sử dụng 435L.
Ian Fellows

@Ian - Tôi bối rối về lý do tại sao 435 là một float trong ví dụ này. Bạn có thể làm rõ một chút? cảm ơn.
Heather Stark

4
Tại sao không a["435"]chèn vào a[names(a)==435]?
pomber

262

Cách trực tiếp nhất là sum(numbers == x).

numbers == xtạo ra một vectơ logic là TRUE tại mọi vị trí x xảy ra và khi suming, vectơ logic được ép thành số chuyển đổi TRUE thành 1 và FALSE thành 0.

Tuy nhiên, lưu ý rằng đối với các số dấu phẩy động, tốt hơn là sử dụng cái gì đó như : sum(abs(numbers - x) < 1e-6).


1
điểm tốt về vấn đề điểm nổi. Điều đó cắn vào mông tôi nhiều hơn tôi thường thừa nhận.
JD Long

3
@Jason trong khi nó trả lời trực tiếp câu hỏi, tôi đoán là mọi người thích giải pháp tổng quát hơn cung cấp câu trả lời cho tất cả xtrong dữ liệu hơn là một giá trị cụ thể được biết đến x. Công bằng mà nói, đó là những gì câu hỏi ban đầu là về. Như tôi đã nói trong câu trả lời của mình dưới đây, "Tôi thấy rất hiếm khi tôi muốn biết tần số của một giá trị và không phải tất cả các giá trị ..."
JBecker

62

Tôi có thể sẽ làm một cái gì đó như thế này

length(which(numbers==x))

Nhưng thực sự, một cách tốt hơn là

table(numbers)

10
table(numbers)sẽ làm được nhiều việc hơn là giải pháp đơn giản nhất sum(numbers==x), bởi vì nó cũng sẽ tìm ra số đếm của tất cả các số khác trong danh sách.
Ken Williams

1
vấn đề với bảng là nó khó khăn hơn để bao gồm nó tính toán bên trong phức tạp hơn, ví dụ như sử dụng được áp dụng () trên dataframes
SKAN

38

Ngoài ra còn có count(numbers)từ plyrgói. Thuận tiện hơn nhiều so với tableý kiến ​​của tôi.


Có một dplyr tương đương với điều này?
stevec

34

Giải pháp ưa thích của tôi sử dụng rle, sẽ trả về một giá trị (nhãn, xtrong ví dụ của bạn) và độ dài, đại diện cho số lần giá trị đó xuất hiện trong chuỗi.

Bằng cách kết hợp rlevới sort, bạn có một cách cực kỳ nhanh chóng để đếm số lần bất kỳ giá trị nào xuất hiện. Điều này có thể hữu ích với các vấn đề phức tạp hơn.

Thí dụ:

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

Nếu giá trị bạn muốn không hiển thị hoặc bạn cần lưu trữ giá trị đó sau, hãy tạo amột 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

Tôi thấy hiếm khi tôi muốn biết tần số của một giá trị và không phải tất cả các giá trị, và rle dường như là cách nhanh nhất để lấy số lượng và lưu trữ tất cả.


1
Là lợi thế của điều này, so với bảng, mà nó mang lại một kết quả trong một định dạng dễ sử dụng hơn? cảm ơn
Heather Stark

@HeatherStark Tôi sẽ nói có hai lợi thế. Đầu tiên chắc chắn là nó là một định dạng dễ sử dụng hơn so với đầu ra của bảng. Thứ hai là đôi khi tôi muốn đếm số lượng phần tử "liên tiếp" thay vì trong toàn bộ tập dữ liệu. Ví dụ, c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))sẽ trở lại values = c('A','G','A','G','C')lengths=c(3, 4, 1, 2, 10)đôi khi hữu ích.
JBecker

1
sử dụng microbenchmark, có vẻ như tablenhanh hơn when the vector is long(tôi đã thử 100000) nhưng lâu hơn một chút khi nó ngắn hơn (tôi đã thử 1000)
ClementWalter

Điều này sẽ rất chậm nếu bạn có nhiều số.
skan

19

Có một hàm tiêu chuẩn trong R cho điều đó

tabulate(numbers)


Nhược điểm của tabulatelà bạn không thể xử lý số 0 và số âm.
omar

2
Nhưng bạn có thể xử lý số 0 của một số đã cho mà các giải pháp khác không xử lý
Dodgie

Tuyệt vời nhanh chóng! Và như omar nói, nó cho số không cho các giá trị không xuất hiện, cực kỳ hữu ích khi chúng ta muốn xây dựng phân phối tần số. Số nguyên không hoặc âm có thể được xử lý bằng cách thêm hằng số trước khi sử dụng tabulate. Lưu ý: sortdường như là cần thiết cho việc sử dụng chính xác của nó nói chung : 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

Đây là một cách nhanh và bẩn:

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

9

Nếu bạn muốn đếm số lần xuất hiện sau đó, bạn có thể sử dụng sapplychức năng:

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

Đầu ra:

        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

Đây có phải là bằng cách nào nhanh hơn bảng ??
Garini

7

Bạn có thể thay đổi số thành bất cứ điều gì bạn muốn trong dòng sau

length(which(numbers == 4))

3

Một cách nữa tôi thấy thuận tiện là:

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

Điều này chuyển đổi tập dữ liệu thành yếu tố, và sau đó tóm tắt () cung cấp cho chúng ta tổng số kiểm soát (tính các giá trị duy nhất).

Đầu ra là:

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 

Điều này có thể được lưu trữ dưới dạng dataframe nếu thích.

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

ở đây row.names đã được sử dụng để đổi tên tên hàng. không sử dụng row.names, tên cột trong s được sử dụng làm tên hàng trong khung dữ liệu mới

Đầu ra là:

     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

Sử dụng bảng nhưng không so sánh với 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 

tablerất hữu ích khi bạn đang sử dụng số lượng các yếu tố khác nhau nhiều lần. Nếu bạn chỉ cần một lần đếm, hãy sử dụngsum(numbers == x)


2

Có nhiều cách khác nhau để đếm một yếu tố cụ thể

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

Một phương pháp tương đối nhanh trên các vectơ dài và cho đầu ra thuận tiện là sử dụng lengths(split(numbers, numbers))(lưu ý chữ S ở cuối 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 

Đầu ra chỉ đơn giản là một vector có tên.
Tốc độ xuất hiện tương đương với rleđề xuất của JBecker và thậm chí nhanh hơn một chút trên các vectơ rất dài. Đây là một microbenchmark trong R 3.6.2 với một số chức năng được đề xuất:

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  

Điều quan trọng, chức năng duy nhất cũng đếm số lượng giá trị còn thiếu NAplyr::count. Chúng cũng có thể được lấy riêng bằng cách sử dụngsum(is.na(vec))


1

Đây là một giải pháp rất nhanh cho các vectơ nguyên tử một chiều. Nó dựa vào match(), vì vậy nó tương thích với 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

Bạn cũng có thể điều chỉnh thuật toán để nó không chạy 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

Trong trường hợp đầu ra đó là mong muốn, bạn thậm chí không cần nó để trả lại vectơ ban đầu và cột thứ hai có lẽ là tất cả những gì bạn cần. Bạn có thể lấy nó trong một dòng với đường ống:

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

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

1
Giải pháp thực sự tuyệt vời! Đó cũng là người nhanh nhất tôi có thể nghĩ ra. Có thể cải thiện một chút về hiệu suất cho đầu vào nhân tố bằng cách sử dụng u <- if (is.factor (x)) x [! Sao chép (x)] khác duy nhất (x).
Taz

0

Điều này có thể được thực hiện với outerđể có được một metrix công bằng theo sau rowSums, với một ý nghĩa rõ ràng.
Để có số lượng và numberstrong cùng một tập dữ liệu, data.frame được tạo trước tiên. Bước này là không cần thiết nếu bạn muốn đầu vào và đầu ra riêng biệt.

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.