Il modo più veloce per aggiungere righe per i valori mancanti in un data.frame?

Ho una colonna nei miei set di dati in cui i periodi di tempo ( Time ) sono interi che vanno da ab. A volte potrebbero mancare periodi di tempo per un determinato gruppo. Mi piacerebbe riempire quelle file con NA . Di seguito sono riportati i dati di esempio per 1 (di diversi 1000) gruppi.

 structure(list(Id = c(1, 1, 1, 1), Time = c(1, 2, 4, 5), Value = c(0.568780482159894, -0.7207749516298, 1.24258192959273, 0.682123081696789)), .Names = c("Id", "Time", "Value"), row.names = c(NA, 4L), class = "data.frame") Id Time Value 1 1 1 0.5687805 2 1 2 -0.7207750 3 1 4 1.2425819 4 1 5 0.6821231 

Come puoi vedere, manca il tempo 3. Spesso uno o più potrebbe mancare. Posso risolvere questo da solo ma temo che non lo farei nel modo più efficiente. Il mio approccio sarebbe quello di creare una funzione che:

Genera una sequenza di periodi di tempo da min(Time) a max(Time)

Quindi fai un setdiff per afferrare i valori Time mancanti.

Converti quel vettore in un data.frame

Estrarre le variabili identificative univoche ( Id e altri non elencati sopra) e aggiungerli a questo data.frame.

Unisci i due.

Ritorno dalla funzione

Quindi l’intero processo sarebbe quindi eseguito come di seguito:

  # Split the data into individual data.frames by Id. temp_list <- dlply(original_data, .(Id)) # pad each data.frame tlist2 <- llply(temp_list, my_pad_function) # collapse the list back to a data.frame filled_in_data <- ldply(tlist2) 

Un modo migliore per raggiungere questo objective?

Dopo aver commentato con Ben Barnes e aver iniziato con il suo mydf3 :

 DT = as.data.table(mydf3) setkey(DT,Id,Time) DT[CJ(unique(Id),seq(min(Time),max(Time)))] Id Time Value Id2 [1,] 1 1 -0.262482283 2 [2,] 1 2 -1.423935165 2 [3,] 1 3 0.500523295 1 [4,] 1 4 -1.912687398 1 [5,] 1 5 -1.459766444 2 [6,] 1 6 -0.691736451 1 [7,] 1 7 NA NA [8,] 1 8 0.001041489 2 [9,] 1 9 0.495820559 2 [10,] 1 10 -0.673167744 1 First 10 rows of 12800 printed. setkey(DT,Id,Id2,Time) DT[CJ(unique(Id),unique(Id2),seq(min(Time),max(Time)))] Id Id2 Time Value [1,] 1 1 1 NA [2,] 1 1 2 NA [3,] 1 1 3 0.5005233 [4,] 1 1 4 -1.9126874 [5,] 1 1 5 NA [6,] 1 1 6 -0.6917365 [7,] 1 1 7 NA [8,] 1 1 8 NA [9,] 1 1 9 NA [10,] 1 1 10 -0.6731677 First 10 rows of 25600 printed. 

CJ sta per Cross Join, vedi ?CJ . Il padding con NA s succede perché nomatch di default è NA . Impostare invece nomatch su 0 per rimuovere le non corrispondenti. Se invece di eseguire il padding con NA è richiesta la riga prevalente, aggiungi roll=TRUE . Questo può essere più efficiente del riempimento con NA e quindi il riempimento di NA seguito. Vedi la descrizione di roll in ?data.table .

 setkey(DT,Id,Time) DT[CJ(unique(Id),seq(min(Time),max(Time))),roll=TRUE] Id Time Value Id2 [1,] 1 1 -0.262482283 2 [2,] 1 2 -1.423935165 2 [3,] 1 3 0.500523295 1 [4,] 1 4 -1.912687398 1 [5,] 1 5 -1.459766444 2 [6,] 1 6 -0.691736451 1 [7,] 1 7 -0.691736451 1 [8,] 1 8 0.001041489 2 [9,] 1 9 0.495820559 2 [10,] 1 10 -0.673167744 1 First 10 rows of 12800 printed. setkey(DT,Id,Id2,Time) DT[CJ(unique(Id),unique(Id2),seq(min(Time),max(Time))),roll=TRUE] Id Id2 Time Value [1,] 1 1 1 NA [2,] 1 1 2 NA [3,] 1 1 3 0.5005233 [4,] 1 1 4 -1.9126874 [5,] 1 1 5 -1.9126874 [6,] 1 1 6 -0.6917365 [7,] 1 1 7 -0.6917365 [8,] 1 1 8 -0.6917365 [9,] 1 1 9 -0.6917365 [10,] 1 1 10 -0.6731677 First 10 rows of 25600 printed. 

Per favore vedi la risposta di Matthew Dowle (oramai, spero sopra).

Ecco qualcosa che usa il pacchetto data.table e può essere d’aiuto quando c’è più di una variabile ID. Potrebbe anche essere più veloce merge , a seconda di come vuoi ottenere i risultati. Sarei interessato al benchmarking e / o ai miglioramenti suggeriti.

Innanzitutto, crea alcuni dati più impegnativi con due variabili ID

 library(data.table) set.seed(1) mydf3< -data.frame(Id=sample(1:100,10000,replace=TRUE), Value=rnorm(10000)) mydf3<-mydf3[order(mydf3$Id),] mydf3$Time<-unlist(by(mydf3,mydf3$Id, function(x)sample(1:(nrow(x)+3),nrow(x)),simplify=TRUE)) mydf3$Id2<-sample(1:2,nrow(mydf3),replace=TRUE) 

Crea una funzione (Questa è stata MODIFICATA - vedi cronologia)

 padFun< -function(data,idvars,timevar){ # Coerce ID variables to character data[,idvars]<-lapply(data[,idvars,drop=FALSE],as.character) # Create global ID variable of all individual ID vars pasted together globalID<-Reduce(function(...)paste(...,sep="SOMETHINGWACKY"), data[,idvars,drop=FALSE]) # Create data.frame of all possible combinations of globalIDs and times allTimes<-expand.grid(globalID=unique(globalID), allTime=min(data[,timevar]):max(data[,timevar]), stringsAsFactors=FALSE) # Get the original ID variables back allTimes2<-data.frame(allTimes$allTime,do.call(rbind, strsplit(allTimes$globalID,"SOMETHINGWACKY")),stringsAsFactors=FALSE) # Convert combinations data.frame to data.table with idvars and timevar as key allTimesDT<-data.table(allTimes2) setnames(allTimesDT,1:ncol(allTimesDT),c(timevar,idvars)) setkeyv(allTimesDT,c(idvars,timevar)) # Convert data to data.table with same variables as key dataDT<-data.table(data,key=c(idvars,timevar)) # Join the two data.tables to create padding res<-dataDT[allTimesDT] return(res) } 

Usa la funzione

 (padded2< -padFun(data=mydf3,idvars=c("Id"),timevar="Time")) # Id Time Value Id2 # [1,] 1 1 -0.262482283 2 # [2,] 1 2 -1.423935165 2 # [3,] 1 3 0.500523295 1 # [4,] 1 4 -1.912687398 1 # [5,] 1 5 -1.459766444 2 # [6,] 1 6 -0.691736451 1 # [7,] 1 7 NA NA # [8,] 1 8 0.001041489 2 # [9,] 1 9 0.495820559 2 # [10,] 1 10 -0.673167744 1 # First 10 rows of 12800 printed. (padded<-padFun(data=mydf3,idvars=c("Id","Id2"),timevar="Time")) # Id Id2 Time Value # [1,] 1 1 1 NA # [2,] 1 1 2 NA # [3,] 1 1 3 0.5005233 # [4,] 1 1 4 -1.9126874 # [5,] 1 1 5 NA # [6,] 1 1 6 -0.6917365 # [7,] 1 1 7 NA # [8,] 1 1 8 NA # [9,] 1 1 9 NA # [10,] 1 1 10 -0.6731677 # First 10 rows of 25600 printed. 

La funzione modificata divide l'ID globale nelle sue parti componenti nella combinazione data.frame, prima di fondersi con i dati originali. Questo dovrebbe (penso) essere migliore.

Puoi usare tidyr per questo.

Usa tidyr::complete per compilare le righe per Time , e per impostazione predefinita i valori sono compilati con NA .

Crea dati

Ho esteso i dati di esempio per dimostrare che funziona per più Id e anche quando all’interno di un Id non è presente l’intervallo completo di Time .

 library(dplyr) library(tidyr) df < - tibble( Id = c(1, 1, 1, 1, 2, 2, 2), Time = c(1, 2, 4, 5, 2, 3, 5), Value = c(0.56, -0.72, 1.24, 0.68, 1.46, 0.74, 0.99) ) df #> # A tibble: 7 x 3 #> Id Time Value #>    #> 1 1 1 0.56 #> 2 1 2 -0.72 #> 3 1 4 1.24 #> 4 1 5 0.68 #> 5 2 2 1.46 #> 6 2 3 0.74 #> 7 2 5 0.99 

Compila le righe mancanti

 df %>% complete(nesting(Id), Time = seq(min(Time), max(Time), 1L)) #> # A tibble: 10 x 3 #> Id Time Value #>    #> 1 1 1 0.56 #> 2 1 2 -0.72 #> 3 1 3 NA #> 4 1 4 1.24 #> 5 1 5 0.68 #> 6 2 1 NA #> 7 2 2 1.46 #> 8 2 3 0.74 #> 9 2 4 NA #> 10 2 5 0.99 

Il mio approccio generale è quello di usare freqTable < - as.data.frame(table(idvar1, idvar2, idvarN)) quindi estrarre le righe dove Freq==0 , pad come necessario e quindi impilare nuovamente sui dati originali.