การนับจำนวนองค์ประกอบด้วยค่าของ 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
อย่าลืมประเด็นปัญหาจุดลอยตัวที่อาจเกิดขึ้นโดยเฉพาะกับตารางซึ่งรวมตัวเลขกับสตริง
hadley

4
นั่นเป็นจุดที่ดี นี่คือจำนวนเต็มทั้งหมดดังนั้นมันจึงไม่ใช่ปัญหาจริงในตัวอย่างนี้ใช่ไหม
เชน

ไม่แน่นอน องค์ประกอบของตารางเป็นคลาสจำนวนเต็มคลาส (ตาราง (ตัวเลข) [1]) แต่ 435 เป็นหมายเลขทศนิยม เพื่อให้เป็นจำนวนเต็มคุณสามารถใช้ 435L
:. Ian Fellows

@Ian - ฉันสับสนว่าทำไม 435 เป็นทุ่นในตัวอย่างนี้ คุณช่วยอธิบายให้ฟังหน่อยได้ไหม? ขอบคุณ
Heather Stark

4
ทำไมไม่a["435"]insetead ของa[names(a)==435]?
pomber

262

sum(numbers == x)วิธีที่ตรงที่สุดคือ

numbers == xสร้างเวกเตอร์แบบลอจิคัลซึ่งเป็น TRUE ในทุกตำแหน่งที่ x เกิดขึ้นและเมื่อsuming เวกเตอร์แบบลอจิคัลจะถูกบังคับให้เป็นตัวเลขซึ่งแปลง TRUE เป็น 1 และ FALSE ถึง 0

sum(abs(numbers - x) < 1e-6)อย่างไรก็ตามทราบว่าสำหรับตัวเลขทศนิยมดีกว่าที่จะใช้สิ่งที่ชอบ:


1
จุดที่ดีเกี่ยวกับปัญหาจุดลอยตัว ที่กัดก้นของฉันมากกว่าที่ฉันมักจะยอมรับ
JD Long

3
@ สันขณะที่มันไม่ตอบคำถามโดยตรงฉันเดาว่าคนที่ชอบการแก้ปัญหาทั่วไปอื่น ๆ ที่ให้คำตอบสำหรับทุกในข้อมูลมากกว่าค่าเฉพาะที่รู้จักกันx xเพื่อความเป็นธรรมนั่นคือสิ่งที่เป็นคำถามดั้งเดิมเกี่ยวกับ ดังที่ฉันได้กล่าวไว้ในคำตอบของฉันด้านล่าง "ฉันคิดว่าเป็นเรื่องยากที่ฉันต้องการทราบความถี่ของค่าหนึ่งค่าและไม่ใช่ค่าทั้งหมด ... "
JBecker

62

ฉันอาจทำอะไรแบบนี้

length(which(numbers==x))

แต่จริงๆแล้ววิธีที่ดีกว่าคือ

table(numbers)

10
table(numbers)จะทำงานมากกว่าโซลูชั่นที่ง่ายที่สุดsum(numbers==x)เพราะมันจะคำนวณจำนวนของตัวเลขอื่น ๆ ทั้งหมดในรายการด้วย
Ken Williams

1
ปัญหาเกี่ยวกับตารางคือมันยากที่จะรวมไว้ในแคลคูลัสที่ซับซ้อนมากขึ้นตัวอย่างเช่นการใช้ Apply () บนดาต้าเฟรม
skan

38

นอกจากนี้ยังมีcount(numbers)จากplyrแพ็คเกจ สะดวกกว่าtableในความคิดของฉัน


มีสิ่งนี้เทียบเท่าหรือไม่
สตีฟ

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
ข้อได้เปรียบของสิ่งนี้เทียบกับตารางที่ให้ผลในรูปแบบที่ใช้งานได้ง่ายขึ้นหรือไม่? ขอบคุณ
Heather Stark

@ HeathStark ฉันจะบอกว่ามีสองข้อได้เปรียบ ประการแรกแน่นอนว่ามันเป็นรูปแบบที่ใช้ง่ายกว่าการแสดงผลของตาราง ที่สองคือบางครั้งฉันต้องการนับจำนวนองค์ประกอบ "ในแถว" มากกว่าภายในชุดข้อมูลทั้งหมด ตัวอย่างเช่น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) แต่อีกเล็กน้อยเมื่อมันสั้นกว่า (ฉันลอง 1,000)
4322 ClementWalter

นี่จะช้ามาก ๆ ถ้าคุณมีจำนวนมาก
skan

19

มีฟังก์ชั่นมาตรฐานใน R สำหรับสิ่งนั้น

tabulate(numbers)


ข้อเสียของtabulateคือคุณไม่สามารถจัดการกับศูนย์และจำนวนลบ
omar

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

มันเร็วกว่าโต๊ะหรือเปล่า?
Garini


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 

สามารถเก็บไว้เป็น dataframe ได้ถ้าต้องการ

as.data.frame (cbind (Number = ชื่อ), Freq = s), stringsAsFactors = F, row.names = 1: ความยาว

ที่นี่ row.names ถูกใช้เพื่อเปลี่ยนชื่อแถว โดยไม่ใช้ row.names ชื่อคอลัมน์ใน s จะถูกใช้เป็นชื่อแถวใน dataframe ใหม่

ผลลัพธ์คือ:

     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 

ผลลัพธ์เป็นเพียงเวกเตอร์ที่มีชื่อ
ความเร็วนั้นเทียบได้กับJBecker ที่rleเสนอและเร็วขึ้นเล็กน้อยเมื่อเทียบกับเวกเตอร์ที่ยาวมาก นี่คือ microbenchmark ใน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 <- ถ้า (is.factor (x)) x [! ซ้ำ (x)] อื่นที่ไม่ซ้ำกัน (x)
Taz

0

สิ่งนี้สามารถทำได้ด้วยouterการหาเมทริกซ์ของความเท่าเทียมกันตามมาrowSumsด้วยความหมายที่ชัดเจน
เพื่อให้มีการนับและnumbersในชุดข้อมูลเดียวกัน data.frame จะถูกสร้างขึ้นเป็นครั้งแรก ขั้นตอนนี้ไม่จำเป็นถ้าคุณต้องการอินพุตและเอาต์พุตแยกกัน

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.