Menghitung jumlah elemen dengan nilai x dalam vektor


400

Saya memiliki vektor angka:

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

Bagaimana saya bisa meminta R menghitung berapa kali nilai x muncul dalam vektor?

Jawaban:


505

Anda bisa menggunakan 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 

Kemudian Anda dapat mengelompokkannya:

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

Atau mengubahnya menjadi data.frame jika Anda lebih nyaman bekerja dengan itu:

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

21
Jangan lupa tentang masalah floating point potensial, terutama dengan tabel, yang memaksa angka ke string.
Hadley

4
Itu poin yang bagus. Ini semua adalah bilangan bulat, jadi ini bukan masalah nyata dalam contoh ini, bukan?
Shane

tidak persis. Elemen-elemen dari tabel adalah kelas integer kelas (tabel (angka) [1]), tetapi 435 adalah angka floating point. Untuk menjadikannya bilangan bulat, Anda dapat menggunakan 435L.
Ian Fellows

@Ian - Saya bingung mengapa 435 mengapung dalam contoh ini. Bisakah Anda menjelaskan sedikit? Terima kasih.
Heather Stark

4
Mengapa tidak a["435"]insetead a[names(a)==435]?
pomber

262

Cara paling langsung adalah sum(numbers == x).

numbers == xmenciptakan vektor logis yang BENAR di setiap lokasi yang terjadi x, dan ketika suming, vektor logis dipaksa untuk numerik yang mengubah BENAR ke 1 dan SALAH ke 0.

Namun, catatan bahwa untuk angka floating point lebih baik untuk penggunaan sesuatu seperti: sum(abs(numbers - x) < 1e-6).


1
poin bagus tentang masalah floating point. Itu menggigit pantat saya lebih dari yang biasanya saya akui.
JD Long

3
@Jason sementara itu menjawab pertanyaan secara langsung, tebakan saya adalah bahwa orang menyukai solusi yang lebih umum yang menyediakan jawaban untuk semua xdalam data daripada nilai yang diketahui spesifik x. Agar adil, itulah pertanyaan aslinya. Seperti yang saya katakan dalam jawaban saya di bawah, "Saya merasa jarang saya ingin mengetahui frekuensi satu nilai dan tidak semua nilai ..."
JBecker

62

Saya mungkin akan melakukan sesuatu seperti ini

length(which(numbers==x))

Tapi sungguh, cara yang lebih baik adalah

table(numbers)

10
table(numbers)akan melakukan lebih banyak pekerjaan daripada solusi termudah sum(numbers==x),, karena itu akan mencari tahu jumlah semua angka lain dalam daftar juga.
Ken Williams

1
masalah dengan tabel adalah bahwa lebih sulit untuk memasukkannya ke dalam kalkulus yang lebih kompleks, misalnya menggunakan apply () pada dataframe
skan

38

Ada juga count(numbers)dari plyrpaket. Jauh lebih nyaman daripada tablemenurut saya.


Apakah ada yang setara dplyr dari ini?
stevec

34

Solusi pilihan saya menggunakan rle, yang akan mengembalikan nilai (label,x dalam contoh Anda) dan panjang, yang menunjukkan berapa kali nilai tersebut muncul secara berurutan.

Dengan menggabungkan rledengansort , Anda memiliki cara yang sangat cepat untuk menghitung berapa kali nilai muncul. Ini bisa membantu dengan masalah yang lebih kompleks.

Contoh:

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

Jika nilai yang Anda inginkan tidak muncul, atau Anda perlu menyimpan nilai itu untuk nanti, buat 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

Saya merasa jarang bahwa saya ingin mengetahui frekuensi satu nilai dan tidak semua nilai, dan rle tampaknya menjadi cara tercepat untuk mendapatkan hitungan dan menyimpan semuanya.


1
Apakah keuntungan dari ini, vs tabel, yang memberikan hasil dalam format yang lebih mudah digunakan? terima kasih
Heather Stark

@HeatherStark Saya akan mengatakan ada dua keuntungan. Yang pertama jelas bahwa itu adalah format yang lebih mudah digunakan daripada output tabel. Yang kedua adalah bahwa kadang-kadang saya ingin menghitung jumlah elemen "dalam satu baris" daripada dalam seluruh dataset. Misalnya c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))mau kembali values = c('A','G','A','G','C')dan lengths=c(3, 4, 1, 2, 10)mana yang kadang berguna.
JBecker

1
menggunakan microbenchmark, tampaknya itu tablelebih cepat when the vector is long(saya mencoba 100000) tetapi sedikit lebih lama ketika lebih pendek (saya mencoba 1000)
ClementWalter

Ini akan menjadi sangat lambat jika Anda memiliki banyak angka.
skan

19

Ada fungsi standar dalam R untuk itu

tabulate(numbers)


Kerugiannya tabulateadalah Anda tidak bisa berurusan dengan angka nol dan negatif.
omar

2
Tetapi Anda dapat menangani nol contoh nomor yang diberikan, yang tidak ditangani oleh solusi lain
Dodgie

Sangat cepat! Dan seperti yang dikatakan omar, ini memberikan nol hitungan untuk nilai yang tidak muncul, sangat berguna ketika kita ingin membangun distribusi frekuensi. Bilangan bulat nol atau negatif dapat ditangani dengan menambahkan konstanta sebelum digunakan tabulate. Catatan: sorttampaknya diperlukan untuk penggunaan yang benar secara umum: 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

inilah salah satu cara cepat dan kotor:

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

9

Jika Anda ingin menghitung jumlah penampilan selanjutnya, Anda dapat menggunakan sapplyfungsi ini:

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

Keluaran:

        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

Apakah ini lebih cepat dari meja ??
Garini


3

Satu lagi cara yang menurut saya nyaman adalah:

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

Ini mengubah dataset menjadi faktor, dan kemudian ringkasan () memberi kita total kontrol (jumlah nilai unik).

Output adalah:

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 

Ini dapat disimpan sebagai kerangka data jika diinginkan.

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

di sini row.names telah digunakan untuk mengubah nama nama baris. tanpa menggunakan row.names, nama kolom dalam s digunakan sebagai nama baris dalam bingkai data baru

Output adalah:

     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

Menggunakan tabel tetapi tanpa membandingkan dengan 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 

tableberguna ketika Anda menggunakan jumlah elemen yang berbeda beberapa kali. Jika Anda hanya perlu satu hitungan, gunakansum(numbers == x)


2

Ada berbagai cara penghitungan elemen tertentu

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

Metode yang relatif cepat pada vektor panjang dan memberikan output yang nyaman adalah dengan menggunakan lengths(split(numbers, numbers))(perhatikan S pada akhir 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 

Outputnya hanyalah sebuah vektor bernama.
Kecepatan muncul sebanding dengan yang rlediusulkan oleh JBecker dan bahkan sedikit lebih cepat pada vektor yang sangat panjang. Berikut ini adalah microbenchmark dalam R 3.6.2 dengan beberapa fungsi yang diusulkan:

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  

Yang penting, satu-satunya fungsi yang juga menghitung jumlah nilai yang hilang NAadalah plyr::count. Ini juga dapat diperoleh secara terpisah menggunakansum(is.na(vec))


1

Ini adalah solusi yang sangat cepat untuk vektor atom satu dimensi. Itu bergantung match(), sehingga kompatibel dengan 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

Anda juga dapat mengubah algoritme agar tidak berjalan 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

Dalam kasus di mana output itu diinginkan, Anda mungkin bahkan tidak memerlukannya untuk mengembalikan kembali vektor asli, dan kolom kedua mungkin semua yang Anda butuhkan. Anda bisa mendapatkannya dalam satu baris dengan pipa:

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

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

1
Solusi yang sangat bagus! Itu juga yang tercepat yang bisa saya pikirkan. Dapat sedikit ditingkatkan untuk kinerja untuk input faktor menggunakan u <- if (is.factor (x)) x [! Diduplikasi (x)] lain unik (x).
Taz

0

Hal ini dapat dilakukan dengan outermendapatkan metriks kesetaraan diikuti oleh rowSums, dengan makna yang jelas.
Untuk memiliki jumlah dan numbersdalam dataset yang sama, sebuah data.frame pertama kali dibuat. Langkah ini tidak diperlukan jika Anda ingin input dan output terpisah.

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.