Zählen der Anzahl der Elemente mit den Werten von x in einem Vektor


400

Ich habe einen Vektor von Zahlen:

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

Wie kann ich R zählen lassen, wie oft ein Wert x im Vektor erscheint?

Antworten:


505

Sie können einfach verwenden 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 

Dann können Sie es unterteilen:

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

Oder konvertieren Sie es in einen data.frame, wenn Sie damit besser arbeiten können:

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

21
Vergessen Sie nicht mögliche Gleitkommaprobleme, insbesondere bei Tabellen, bei denen Zahlen zu Zeichenfolgen gezwungen werden.
Hadley

4
Das ist ein großartiger Punkt. Dies sind alles ganze Zahlen, also ist es in diesem Beispiel kein wirkliches Problem, oder?
Shane

nicht genau. Die Elemente der Tabelle gehören zur Klasse Integer Class (Tabelle (Zahlen) [1]), aber 435 ist eine Gleitkommazahl. Um daraus eine Ganzzahl zu machen, können Sie 435L verwenden.
Ian Fellows

@ Ian - Ich bin verwirrt darüber, warum 435 in diesem Beispiel ein Float ist. Können Sie etwas klarstellen? Vielen Dank.
Heather Stark

4
Warum nicht a["435"]anstelle von a[names(a)==435]?
Pomber

262

Der direkteste Weg ist sum(numbers == x).

numbers == xErstellt einen logischen Vektor, der an jeder Stelle, an der x auftritt, WAHR ist, und wenn er ausgeführt wird sum, wird der logische Vektor in einen numerischen Vektor gezwungen, der WAHR in 1 und FALSCH in 0 konvertiert.

Beachten Sie jedoch, dass es für Gleitkommazahlen besser ist, Folgendes zu verwenden : sum(abs(numbers - x) < 1e-6).


1
Guter Punkt zum Gleitkomma-Problem. Das beißt mir mehr in den Hintern, als ich allgemein zugeben möchte.
JD Long

3
@Jason, während es die Frage direkt beantwortet, ist meine Vermutung, dass die Leute die allgemeinere Lösung mochten, die die Antwort für alle xin den Daten liefert, anstatt einen bestimmten bekannten Wert von x. Um fair zu sein, darum ging es in der ursprünglichen Frage. Wie ich in meiner Antwort unten sagte: "Ich finde, es ist selten, dass ich die Häufigkeit eines Wertes und nicht aller Werte wissen möchte ..."
JBecker

62

Ich würde wahrscheinlich so etwas tun

length(which(numbers==x))

Aber wirklich, ein besserer Weg ist

table(numbers)

10
table(numbers)wird viel mehr Arbeit leisten als die einfachste Lösung, sum(numbers==x)weil es auch die Anzahl aller anderen Zahlen in der Liste herausfinden wird.
Ken Williams

1
Das Problem mit der Tabelle ist, dass es schwieriger ist, sie in komplexere Berechnungen
aufzunehmen

38

Es gibt auch count(numbers)aus plyrPaket. Viel bequemer als tablemeiner Meinung nach.


Gibt es ein dplyr-Äquivalent dazu?
Stevec

34

Meine bevorzugte Lösung verwendet rle, die einen Wert ( xin Ihrem Beispiel die Bezeichnung ) und eine Länge zurückgibt, die angibt, wie oft dieser Wert nacheinander angezeigt wurde.

Durch die Kombination rlemit sorthaben Sie eine extrem schnelle Möglichkeit, die Häufigkeit zu zählen, mit der ein Wert angezeigt wurde. Dies kann bei komplexeren Problemen hilfreich sein.

Beispiel:

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

Wenn der gewünschte Wert nicht angezeigt wird oder Sie diesen Wert für später speichern müssen, erstellen Sie 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

Ich finde es selten, dass ich die Häufigkeit eines Wertes und nicht aller Werte wissen möchte, und rle scheint der schnellste Weg zu sein, um alle zu zählen und zu speichern.


1
Ist der Vorteil gegenüber der Tabelle, dass das Ergebnis in einem leichter verwendbaren Format angezeigt wird? danke
Heather Stark

@HeatherStark Ich würde sagen, es gibt zwei Vorteile. Das erste ist definitiv, dass es ein leichter zu verwendendes Format ist als die Tabellenausgabe. Das zweite ist, dass ich manchmal die Anzahl der Elemente "in einer Reihe" anstatt innerhalb des gesamten Datensatzes zählen möchte. Zum Beispiel c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))würde zurückkehren values = c('A','G','A','G','C')und lengths=c(3, 4, 1, 2, 10)was manchmal nützlich ist.
JBecker

1
Mit Microbenchmark scheint tablees schneller zu sein when the vector is long(ich habe 100000 ausprobiert), aber etwas länger, wenn es kürzer ist (ich habe 1000 ausprobiert)
ClementWalter

Dies wird sehr langsam sein, wenn Sie viele Zahlen haben.
Skan

19

Dafür gibt es in R eine Standardfunktion

tabulate(numbers)


Der Nachteil von tabulateist, dass Sie nicht mit Nullen und negativen Zahlen umgehen können.
Omar

2
Aber Sie können mit null Instanzen einer bestimmten Zahl umgehen, die die anderen Lösungen nicht behandeln
Dodgie

Fantastisch schnell! Und wie omar sagt, gibt es eine Nullzahl für nicht erscheinende Werte, was äußerst nützlich ist, wenn wir eine Häufigkeitsverteilung erstellen möchten. Null- oder negative Ganzzahlen können behandelt werden, indem vor der Verwendung eine Konstante hinzugefügt wird tabulate. Hinweis: sortscheint für die korrekte Verwendung im Allgemeinen erforderlich zu sein : 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

Hier ist ein schneller und schmutziger Weg:

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

9

Wenn Sie die Anzahl der Auftritte anschließend zählen möchten, können Sie die folgende sapplyFunktion verwenden:

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

Ausgabe:

        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

Ist das auf jeden Fall schneller als Tisch?
Garini


3

Ein weiterer Weg, den ich bequem finde, ist:

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

Dadurch wird der Datensatz in einen Faktor konvertiert, und dann gibt summary () die Kontrollsummen (Anzahl der eindeutigen Werte) an.

Ausgabe ist:

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 

Dies kann bei Bedarf als Datenrahmen gespeichert werden.

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

Hier wurde row.names verwendet, um Zeilennamen umzubenennen. Ohne Verwendung von row.names werden Spaltennamen in s als Zeilennamen in neuen Datenrahmen verwendet

Ausgabe ist:

     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

Tabelle verwenden, aber ohne zu vergleichen mit 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 

tableist nützlich, wenn Sie die Anzahl der verschiedenen Elemente mehrmals verwenden. Wenn Sie nur eine Zählung benötigen, verwenden Siesum(numbers == x)


2

Es gibt verschiedene Möglichkeiten, bestimmte Elemente zu zählen

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

Eine Methode, die bei langen Vektoren relativ schnell ist und eine bequeme Ausgabe liefert, ist die Verwendung lengths(split(numbers, numbers))(beachten Sie das S am Ende von 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 

Die Ausgabe ist einfach ein benannter Vektor.
Die Geschwindigkeit scheint vergleichbar mit rleder von JBecker vorgeschlagenen und bei sehr langen Vektoren sogar etwas schneller zu sein. Hier ist ein Mikrobenchmark in R 3.6.2 mit einigen der vorgeschlagenen Funktionen:

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  

Wichtig ist, dass die einzige Funktion, die auch die Anzahl der fehlenden Werte zählt, NAist plyr::count. Diese können auch separat mit bezogen werdensum(is.na(vec))


1

Dies ist eine sehr schnelle Lösung für eindimensionale Atomvektoren. Es match()basiert auf , ist also kompatibel mit 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

Sie können den Algorithmus auch so anpassen, dass er nicht ausgeführt wird 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

In Fällen, in denen diese Ausgabe wünschenswert ist, benötigen Sie sie wahrscheinlich nicht einmal, um den ursprünglichen Vektor zurückzugeben, und die zweite Spalte ist wahrscheinlich alles, was Sie benötigen. Sie können das in einer Zeile mit dem Rohr erhalten:

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

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

1
Wirklich tolle Lösung! Das ist auch die schnellste, die ich mir vorstellen konnte. Die Leistung für die Faktoreingabe kann mit u <- if (is.factor (x)) x [! Duplicated (x)] else unique (x) etwas verbessert werden.
Taz

0

Dies kann getan werden outer, um eine Metrik von Gleichheiten zu erhalten, gefolgt von rowSumseiner offensichtlichen Bedeutung.
Um die numbersAnzahl und das gleiche Dataset zu erhalten, wird zuerst ein data.frame erstellt. Dieser Schritt ist nicht erforderlich, wenn Sie eine separate Eingabe und Ausgabe wünschen.

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.