2010-05-05 8 views
10

Desidero implementare un "Ultima osservazione effettuata" per un set di dati su cui sto lavorando e che alla fine presenta valori mancanti.Ultima osservazione eseguita in avanti in un frame di dati?

Ecco un semplice codice per farlo (domanda dopo):

LOCF <- function(x) 
{ 
    # Last Observation Carried Forward (for a left to right series) 
    LOCF <- max(which(!is.na(x))) # the location of the Last Observation to Carry Forward 
    x[LOCF:length(x)] <- x[LOCF] 
    return(x) 
} 


# example: 
LOCF(c(1,2,3,4,NA,NA)) 
LOCF(c(1,NA,3,4,NA,NA)) 

Ora, questo funziona alla grande per i vettori semplici. Ma se dovessi provare a usarlo su un frame di dati:

a <- data.frame(rep("a",4), 1:4,1:4, c(1,NA,NA,NA)) 
a 
t(apply(a, 1, LOCF)) # will make a mess 

Trasforma il mio frame di dati in una matrice di caratteri.

Riesci a pensare a un modo di fare LOCF su un data.frame, senza trasformarlo in una matrice? (Ho potuto utilizzare i loop e tale da correggere il disordine, ma mi piacerebbe per una soluzione più elegante)

Cheers,

Tal

risposta

18

Questo già esiste:

library(zoo) 
na.locf(data.frame(rep("a",4), 1:4,1:4, c(1,NA,NA,NA))) 
+2

+1 e rseek.org naturalmente colpisce immediatamente questo come primi risultati. –

+0

La mia offerta per non vederlo - grazie Shane. Ma temo che non faccia il lavoro. (riempie la colonna 3, invece di ogni riga) –

+1

Potresti averlo trovato anche se hai cercato stackoverflow.com per '[r] locf'. – Shane

0

ho finito risolvendo questo usando un ciclo:

fillInTheBlanks <- function(S) { 
    L <- !is.na(S) 
    c(S[L][1], S[L])[cumsum(L)+1] 
} 


LOCF.DF <- function(xx) 
{ 
    # won't work well if the first observation is NA 

    orig.class <- lapply(xx, class) 

    new.xx <- data.frame(t(apply(xx,1, fillInTheBlanks))) 

    for(i in seq_along(orig.class)) 
    { 
     if(orig.class[[i]] == "factor") new.xx[,i] <- as.factor(new.xx[,i]) 
     if(orig.class[[i]] == "numeric") new.xx[,i] <- as.numeric(new.xx[,i]) 
     if(orig.class[[i]] == "integer") new.xx[,i] <- as.integer(new.xx[,i]) 
    } 

    #t(na.locf(t(a))) 

    return(new.xx) 
} 

a <- data.frame(rep("a",4), 1:4,1:4, c(1,NA,NA,NA)) 
LOCF.DF(a) 
2

Questa domanda è vecchia ma per i posteri ... il miglior solut ion usa il pacchetto data.table con il rotolo = T.

+17

compila con un esempio – mnel

0

Invece di apply() è possibile utilizzare lapply() e quindi trasformare l'elenco risultante in data.frame.

LOCF <- function(x) { 
    # Last Observation Carried Forward (for a left to right series) 
    LOCF <- max(which(!is.na(x))) # the location of the Last Observation to Carry Forward 
    x[LOCF:length(x)] <- x[LOCF] 
    return(x) 
} 

a <- data.frame(rep("a",4), 1:4, 1:4, c(1, NA, NA, NA)) 
a 
data.frame(lapply(a, LOCF)) 
4

Ci sono un sacco di pacchetti che implementano esattamente questa funzionalità. (con stessa funzionalità di base, ma alcune differenze di opzioni aggiuntive)

  • spaziotempo :: na.locf
  • imputeTS :: na.locf
  • zoo :: na.locf
  • XTS :: na.locf
+1

Anche tidyverse ha una funzione fill() equivalente. Sarebbe bello avere qualcosa di veloce in data.table. – skan

4

Se non si desidera caricare un pacchetto grande come zoo solo per la funzione na.locf, ecco una soluzione breve che funziona anche se nel vettore di input sono presenti alcuni NA principali.

na.locf <- function(x) { 
    v <- !is.na(x) 
    c(NA, x[v])[cumsum(v)+1] 
} 
+0

Questa soluzione mi piace di più. Se vuoi applicarlo a un 'data.frame' come nella domanda originale, puoi usarlo tramite' a [] = lapply (a, na.locf) '. – cryo111

5

Aggiunta la nuova funzione tidyr::fill() per il riporto l'ultima osservazione in una colonna per riempire NA s:

a <- data.frame(col1 = rep("a",4), col2 = 1:4, 
       col3 = 1:4, col4 = c(1,NA,NA,NA)) 
a 
# col1 col2 col3 col4 
# 1 a 1 1 1 
# 2 a 2 2 NA 
# 3 a 3 3 NA 
# 4 a 4 4 NA 

a %>% tidyr::fill(col4) 
# col1 col2 col3 col4 
# 1 a 1 1 1 
# 2 a 2 2 1 
# 3 a 3 3 1 
# 4 a 4 4 1 
Problemi correlati