Ho un vettore di numeri:
numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435,
453,435,324,34,456,56,567,65,34,435)
Come posso avere R contare il numero di volte che un valore x appare nel vettore?
Ho un vettore di numeri:
numbers <- c(4,23,4,23,5,43,54,56,657,67,67,435,
453,435,324,34,456,56,567,65,34,435)
Come posso avere R contare il numero di volte che un valore x appare nel vettore?
Risposte:
Puoi semplicemente usare 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
Quindi puoi sottoimpostarlo:
> a[names(a)==435]
435
3
O convertilo in un data.frame se ti senti più a tuo agio nel farlo:
> as.data.frame(table(numbers))
numbers Freq
1 4 2
2 5 1
3 23 2
4 34 2
...
a["435"]insetead di a[names(a)==435]?
Il modo più diretto è sum(numbers == x).
numbers == xcrea un vettore logico che è VERO in ogni posizione in cui si verifica x, e quando suming, il vettore logico viene forzato in numerico che converte VERO in 1 e FALSO in 0.
Si noti tuttavia che per i numeri in virgola mobile è meglio usare qualcosa come: sum(abs(numbers - x) < 1e-6).
xnei dati piuttosto che uno specifico valore noto di x. Ad essere sinceri, questo era l'argomento iniziale. Come ho detto nella mia risposta di seguito, "Trovo raro che voglia conoscere la frequenza di un valore e non tutti i valori ..."
Probabilmente farei qualcosa del genere
length(which(numbers==x))
Ma davvero, un modo migliore è
table(numbers)
table(numbers)farà molto più lavoro della soluzione più semplice sum(numbers==x), perché determinerà anche i conteggi di tutti gli altri numeri nell'elenco.
La mia soluzione preferita utilizza rle, che restituirà un valore (l'etichetta, xnel tuo esempio) e una lunghezza, che rappresenta quante volte quel valore è apparso in sequenza.
Combinando rlecon sort, hai un modo estremamente veloce per contare il numero di volte in cui è apparso un valore. Questo può essere utile con problemi più complessi.
Esempio:
> 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 ...
Se il valore desiderato non viene visualizzato o devi memorizzarlo per un momento successivo, crea aun 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
Trovo raro che voglia conoscere la frequenza di un valore e non tutti i valori, e rle sembra essere il modo più veloce per ottenere il conteggio e memorizzarli tutti.
c(rep('A', 3), rep('G', 4), 'A', rep('G', 2), rep('C', 10))ritornerebbe values = c('A','G','A','G','C')e lengths=c(3, 4, 1, 2, 10)che a volte è utile.
tablesia più veloce when the vector is long(ho provato 100000) ma leggermente più lungo quando è più corto (ho provato 1000)
C'è una funzione standard in R per questo
tabulate(numbers)
tabulateè che non è possibile gestire i numeri zero e negativi.
tabulate. Nota: sortsembra essere necessarie per il suo corretto utilizzo in generale: tabulate(sort(numbers)).
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
Se si desidera contare il numero di presenze successivamente, è possibile utilizzare la sapplyfunzione:
index<-sapply(1:length(numbers),function(x)sum(numbers[1:x]==numbers[x]))
cbind(numbers, index)
Produzione:
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
È possibile modificare il numero come desiderato nella riga seguente
length(which(numbers == 4))
Un altro modo che trovo conveniente è:
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)))
Questo converte il set di dati in fattore e quindi il sommario () ci fornisce i totali del controllo (conteggi dei valori univoci).
L'output è:
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
Questo può essere memorizzato come frame di dati, se preferito.
as.data.frame (cbind (Number = names (s), Freq = s), stringsAsFactors = F, row.names = 1: length (s))
qui row.names è stato usato per rinominare i nomi delle righe. senza usare row.names, i nomi delle colonne in s vengono usati come nomi di riga nel nuovo frame di dati
L'output è:
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
Utilizzando table ma senza confrontarlo con 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è utile quando si utilizzano più volte i conteggi di diversi elementi. Se hai bisogno di un solo conteggio, usasum(numbers == x)
Esistono diversi modi per contare elementi specifici
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']])
Un metodo che è relativamente veloce su vettori lunghi e fornisce un output conveniente è quello di usare lengths(split(numbers, numbers))(notare la S alla fine di 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
L'output è semplicemente un vettore denominato.
La velocità appare paragonabile a quella rleproposta da JBecker e persino un po 'più veloce su vettori molto lunghi. Ecco un microbenchmark in R 3.6.2 con alcune delle funzioni proposte:
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
È importante sottolineare che l'unica funzione che conta anche il numero di valori mancanti NAè plyr::count. Questi possono anche essere ottenuti separatamente usandosum(is.na(vec))
Questa è una soluzione molto veloce per i vettori atomici monodimensionali. Si affida match(), quindi è compatibile con 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
Potresti anche modificare l'algoritmo in modo che non venga eseguito 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
Nei casi in cui l'output è desiderabile, probabilmente non è nemmeno necessario che restituisca nuovamente il vettore originale e la seconda colonna è probabilmente tutto ciò di cui hai bisogno. Puoi ottenerlo in una riga con la pipe:
match(x, x) %>% `[`(tabulate(.), .)
#> [1] 3 2 3 2 3 1 2 2
Questo può essere fatto outerper ottenere un metrox di uguaglianze seguito da rowSums, con un significato ovvio.
Per avere i conteggi e numbersnello stesso set di dati, viene prima creato un data.frame. Questo passaggio non è necessario se si desidera input e output separati.
df <- data.frame(No = numbers)
df$count <- rowSums(outer(df$No, df$No, FUN = `==`))