2015-11-30 12 views
8

Il set di dati contiene tre variabili: id, sesso e grado (fattore).Frequenza di righe per ID

mydata <- data.frame(id=c(1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,4), sex=c(1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1), 
        grade=c("a","b","c","d","e", "x","y","y","x", "q","q","q","q", "a", "a", "a", NA, "b")) 

Per ogni ID, ho bisogno di vedere quanti gradi unico che abbiamo e quindi creare una nuova colonna (chiamata N) per registrare la frequenza di grado. Ad esempio, per ID = 1, abbiamo cinque valori univoci per "grado", quindi N = 4; per ID = 2, abbiamo due valori univoci per "grado", quindi N = 2; per ID = 4, abbiamo due valori unici per "grado" (ignora NA), quindi N = 2.

Il set di dati finale è

mydata <- data.frame(id=c(1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,4), sex=c(1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1), 
        grade=c("a","b","c","d","e", "x","y","y","x", "q","q","q","q", "a", "a", "a", NA, "b")) 
mydata$N <- c(5,5,5,5,5,2,2,2,2,1,1,1,1,2,2,2,2,2) 
+0

Questo sembrerebbe come un lavoro per un 'ciclo for', facendo un'iterazione per ogni ID valore unico e, eventualmente, un ciclo 'for' annidato per ogni grado unico. – giraffehere

+4

I loop non sono la risposta in R. A meno che non lo siano (il che non è il caso qui). – Heroka

+0

Mi chiedevo anche se sarebbe stato possibile utilizzare * dplyr * e fornire i conteggi per gruppi univoci. – Konrad

risposta

9

è possibile utilizzare il pacchetto data.table:

library(data.table) 
setDT(mydata) 

#I have removed NA's, up to you how to count them 
mydata[,N_u:=length(unique(grade[!is.na(grade)])),by=id] 

Molto breve, leggibile e veloce. Può anche essere fatto in base-R:

#lapply(split(grade,id),...: splits data into subsets by id 
#unlist: creates one vector out of multiple vectors 
#rep: makes sure each ID is repeated enough times 

mydata$N <- unlist(lapply(split(mydata$grade,mydata$id),function(x){ 
    rep(length(unique(x[!is.na(x)])),length(x)) 
} 
)) 

Perché c'era la discussione su ciò che è più veloce, facciamo un po 'di benchmarking.

dataset Data:

> test1 
Unit: milliseconds 
      expr  min  lq  mean median  uq  max neval cld 
length_unique 3.043186 3.161732 3.422327 3.286436 3.477854 10.627030 100 b 
     uniqueN 2.481761 2.615190 2.763192 2.738354 2.872809 3.985393 100 a 

dataset ingrandita: (10000 osservazioni, 1000 id)

> test2 
Unit: milliseconds 
      expr  min  lq  mean median  uq  max neval cld 
length_unique 11.84123 24.47122 37.09234 30.34923 47.55632 97.63648 100 a 
     uniqueN 25.83680 50.70009 73.78757 62.33655 97.33934 210.97743 100 b 
11

Con this recent commit, uniqueN ha un argomento na.rm, che possiamo usare come segue:

library(data.table) 
setDT(mydata)[, n := uniqueN(grade, na.rm=TRUE), by=id] 

Questo sarà disponibile dal prossimo versione stabile su CRAN (v1.9.8). In alternativa puoi anche installare la versione corrente di devel (v1.9.7) seguendo the instructions here per provarla.


Con data.table si potrebbe in questo modo:

library(data.table) 
setDT(mydata)[, n := uniqueN(grade[!is.na(grade)]), by = id] 

o:

setDT(mydata)[, n := uniqueN(na.omit(grade)), by = id] 

che dà:

> mydata 
    id sex grade n 
1: 1 1  a 5 
2: 1 1  b 5 
3: 1 1  c 5 
4: 1 1  d 5 
5: 1 1  e 5 
6: 2 0  x 2 
7: 2 0  y 2 
8: 2 0  y 2 
9: 2 0  x 2 
10: 3 0  q 1 
11: 3 0  q 1 
12: 3 0  q 1 
13: 3 0  q 1 
14: 4 1  a 2 
15: 4 1  a 2 
16: 4 1  a 2 
17: 4 1 NA 2 
18: 4 1  b 2 
+0

@RichardScriven che mi dà un risultato diverso – Jaap

+0

Ottengo lo stesso risultato mostrato nella risposta quando si utilizza 'na.omit (grado)'. Che cosa ottieni? –

+0

@docendodiscimus Ora ottengo lo stesso risultato. Probabilmente ha fatto un errore ieri sera. Aggiunta l'opzione alla risposta. – Jaap

5

Sembra che abbiamo recidere Al vota per data.table, ma si potrebbe anche utilizzare la funzione di base di R ave():

mydata$N <- ave(as.character(mydata$grade),mydata$id, 
       FUN = function(x) length(unique(x[!is.na(x)]))) 
+1

Un'altra idea, con base, è ' rowSums (tabella (mydata [c ("id", "grade")])> 0) [mydata $ id] ' –

4

uso Tapply e tabella di ricerca

mydata <- data.frame(id=c(1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,4), 
        sex=c(1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1,1), 
        grade=c("a","b","c","d","e", "x","y","y","x", "q", 
          "q","q","q", "a", "a", "a", NA, "b")) 
uniqN <- tapply(mydata$grade, mydata$id, function(x) sum(!is.na(unique(x)))) 
mydata$N <- uniqN[mydata$id] 
0

Qui è un metodo dplyr. Ho tenuto la tabella riassuntiva separata per motivi ordinati.

library(dplyr) 

summary = 
    mydata %>% 
    distinct(id, grade) %>% 
    filter(grade %>% is.na %>% `!`) %>% 
    count(id) 

mydata %>% 
    left_join(summary) 
7

Un'opzione dplyr che fa uso di dplyr::n_distinct e la sua na.rm -argument:

library(dplyr) 
mydata %>% group_by(id) %>% mutate(N = n_distinct(grade, na.rm = TRUE)) 
#Source: local data frame [18 x 4] 
#Groups: id [4] 
# 
#  id sex grade  N 
# (dbl) (dbl) (fctr) (int) 
#1  1  1  a  5 
#2  1  1  b  5 
#3  1  1  c  5 
#4  1  1  d  5 
#5  1  1  e  5 
#6  2  0  x  2 
#7  2  0  y  2 
#8  2  0  y  2 
#9  2  0  x  2 
#10  3  0  q  1 
#11  3  0  q  1 
#12  3  0  q  1 
#13  3  0  q  1 
#14  4  1  a  2 
#15  4  1  a  2 
#16  4  1  a  2 
#17  4  1  NA  2 
#18  4  1  b  2 
Problemi correlati