Zliczanie liczby elementów o wartości x w wektorze


400

Mam wektor liczb:

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

Jak mogę zliczyć R, ile razy wartość x pojawia się w wektorze?

Odpowiedzi:


505

Możesz po prostu użyć 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 

Następnie możesz go podzielić:

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

Lub przekonwertuj go na ramkę danych, jeśli wygodniej z tym pracujesz:

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

21
Nie zapominaj o potencjalnych problemach zmiennoprzecinkowych, szczególnie z tabelą, która wymusza liczby na ciągi.
hadley,

4
To świetna uwaga. To są liczby całkowite, więc w tym przykładzie nie jest to prawdziwy problem, prawda?
Shane,

nie dokładnie. Elementy tabeli są klasą całkowitą (tabela (liczby) [1]), ale 435 jest liczbą zmiennoprzecinkową. Aby uczynić go liczbą całkowitą, możesz użyć 435L.
Ian Fellows,

@Ian - Nie jestem pewien, dlaczego 435 jest liczbą zmiennoprzecinkową w tym przykładzie. Czy możesz coś wyjaśnić? dzięki.
Heather Stark

4
Dlaczego nie a["435"]insetead od a[names(a)==435]?
pomber

262

Najbardziej bezpośredni sposób to sum(numbers == x).

numbers == xtworzy wektor logiczny, który jest PRAWDZIWY w każdym miejscu, w którym występuje x, a podczas suming wektor logiczny jest wymuszany na numeryczny, który konwertuje PRAWDA na 1, a FAŁSZ na 0.

Jednak należy pamiętać, że dla liczb zmiennoprzecinkowych to lepiej użyć czegoś takiego: sum(abs(numbers - x) < 1e-6).


1
dobra uwaga na temat kwestii zmiennoprzecinkowej To gryzie mnie w tyłek bardziej, niż zwykle to przyznaję.
JD Long,

3
@Jason, choć odpowiada bezpośrednio na pytanie, zgaduję, że ludziom podobało się bardziej ogólne rozwiązanie, które zapewnia odpowiedź na wszystkie xdane, a nie na określoną znaną wartość x. Szczerze mówiąc, o to właśnie chodziło w pierwotnym pytaniu. Jak powiedziałem w odpowiedzi poniżej, „rzadko zdarza mi się znać częstotliwość jednej wartości, a nie wszystkich wartości ...”
JBecker

62

Prawdopodobnie zrobiłbym coś takiego

length(which(numbers==x))

Ale tak naprawdę jest lepszy sposób

table(numbers)

10
table(numbers)wykona znacznie więcej pracy niż najłatwiejsze rozwiązanie, sum(numbers==x)ponieważ obliczy również liczbę wszystkich pozostałych liczb na liście.
Ken Williams,

1
problem z tabelą polega na tym, że trudniej jest zawrzeć ją w bardziej złożonym rachunku różniczkowym, na przykład za pomocą Apply () w ramkach danych
skan

38

Jest też count(numbers)z plyrpaczki. O wiele wygodniejszy niż tablew mojej opinii.


Czy istnieje odpowiednik dplyr tego?
stevec

34

Moje preferowane rozwiązanie używa rle, które zwróci wartość (etykietę xw twoim przykładzie) i długość, która reprezentuje ile razy ta wartość pojawiła się w sekwencji.

Łącząc się rlez sort, masz niezwykle szybki sposób na policzenie liczby wyświetleń dowolnej wartości. Może to być pomocne w przypadku bardziej złożonych problemów.

Przykład:

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

Jeśli wartość chcesz nie pokazuje się, czy trzeba przechowywać tę wartość na później, należy .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

Uważam, że rzadko zdarza się, że chcę znać częstotliwość jednej wartości, a nie wszystkich wartości, a rle wydaje się być najszybszym sposobem na zliczenie i zapamiętanie ich wszystkich.


1
Czy zaletą tego, w porównaniu do tabeli, jest to, że daje wynik w łatwiejszym do wykorzystania formacie? dzięki
Heather Stark

@HeatherStark Powiedziałbym, że są dwie zalety. Pierwszym jest zdecydowanie to, że jest to łatwiejszy format niż wynik tabeli. Po drugie, czasami chcę policzyć liczbę elementów „w rzędzie”, a nie w całym zbiorze danych. Na przykład c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))wróciłby values = c('A','G','A','G','C')i lengths=c(3, 4, 1, 2, 10)co czasami jest przydatne.
JBecker

1
przy użyciu mikrodruku wydaje się, że tablejest szybszy when the vector is long(próbowałem 100000), ale nieco dłużej, gdy jest krótszy (próbowałem 1000)
ClementWalter

To będzie bardzo wolne, jeśli masz dużo liczb.
skan

19

W tym celu istnieje standardowa funkcja w języku R.

tabulate(numbers)


Wadą tego tabulatejest to, że nie radzisz sobie z liczbami zerowymi i ujemnymi.
omar

2
Ale możesz sobie poradzić z zerowymi wystąpieniami danej liczby, których inne rozwiązania nie obsługują
Dodgie

Fantastycznie szybko! I jak mówi Omar, daje zerową liczbę nie pojawiających się wartości, co jest niezwykle przydatne, gdy chcemy zbudować rozkład częstotliwości. Zerowe lub ujemne liczby całkowite mogą być obsługiwane przez dodanie stałej przed użyciem tabulate. Uwaga: sortwydaje się być konieczne dla właściwego stosowania w ogóle: 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

oto jeden szybki i brudny sposób:

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

9

Jeśli chcesz później policzyć liczbę wystąpień, możesz skorzystać z sapplyfunkcji:

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

Wynik:

        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

Czy to w jakikolwiek sposób jest szybsze niż stół?
Garini


3

Jeszcze jeden sposób, który uważam za wygodny, to:

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

Konwertuje to zestaw danych na współczynnik, a następnie podsumowanie () daje nam sumy kontrolne (liczby unikatowych wartości).

Dane wyjściowe to:

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 

W razie potrzeby można to zapisać jako ramkę danych.

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

tutaj row.names służy do zmiany nazw nazw wierszy. bez użycia row.names, nazwy kolumn ws są używane jako nazwy wierszy w nowej ramce danych

Dane wyjściowe to:

     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

Używanie tabeli, ale bez porównywania z 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 

tablejest przydatny, gdy kilka razy korzystasz z liczników różnych elementów. Jeśli potrzebujesz tylko jednej liczby, użyjsum(numbers == x)


2

Istnieją różne sposoby liczenia określonych elementów

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

Metodą, która jest stosunkowo szybka na długich wektorach i daje wygodne wyjście, jest użycie lengths(split(numbers, numbers))(zwróć uwagę na S na końcu 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 

Wynik jest po prostu nazwanym wektorem.
Szybkość wydaje się porównywalna do rleproponowanej przez JBeckera, a nawet nieco większa w przypadku bardzo długich wektorów. Oto znak firmowy w R 3.6.2 z niektórymi proponowanymi funkcjami:

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  

Co ważne, jedyną funkcją, która również liczy liczbę brakujących wartości, NAjest plyr::count. Można je również uzyskać osobno za pomocąsum(is.na(vec))


1

To bardzo szybkie rozwiązanie dla jednowymiarowych wektorów atomowych. To zależy match(), więc jest kompatybilny z 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

Możesz również dostosować algorytm, aby nie działał 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

W przypadkach, w których pożądane jest wyjście, prawdopodobnie nawet nie potrzebujesz go, aby przywrócić oryginalny wektor, a druga kolumna to prawdopodobnie wszystko, czego potrzebujesz. Możesz uzyskać to w jednej linii za pomocą fajki:

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

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

1
Naprawdę świetne rozwiązanie! To także najszybszy, jaki mogłem wymyślić. Można go nieco poprawić pod względem wydajności dla wprowadzania czynnikowego przy użyciu u <- if (is.factor (x)) x [! Duplicated (x)] else unique (x).
Taz

0

Można tego dokonać, outeraby uzyskać metrykę równości rowSums, która ma oczywiste znaczenie.
Aby mieć liczby i numbersten sam zestaw danych, najpierw tworzona jest ramka danych. Ten krok nie jest potrzebny, jeśli chcesz osobne wejście i wyjście.

df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.