2012-11-29 10 views
15

Ho il seguente frame di dati (semplificato) con la variabile paese come un fattore e la variabile valore è mancante:valori Come riempire AN con LOCF da fattori di frame di dati, suddivisi per paese

country value 
AUT  NA 
AUT  5 
AUT  NA 
AUT  NA 
GER  NA 
GER  NA 
GER  7 
GER  NA 
GER  NA 

I seguente genera il frame di dati di cui sopra:

data <- data.frame(country=c("AUT", "AUT", "AUT", "AUT", "GER", "GER", "GER", "GER", "GER"), value=c(NA, 5, NA, NA, NA, NA, 7, NA, NA)) 

Ora, vorrei sostituire i valori NA in ciascun sottoinsieme paese con il metodo dell'ultima osservazione effettuata (LOCF). Conosco il comando na.locf nel pacchetto zoo. data <- na.locf(data) mi darebbe la seguente struttura dei dati:

country value 
AUT  NA 
AUT  5 
AUT  5 
AUT  5 
GER  5 
GER  5 
GER  7 
GER  7 
GER  7 

Tuttavia, la funzione deve essere utilizzata solo sui singoli sottogruppi suddivisi per paese. Quello che segue è l'output che mi servirebbe:

country value 
AUT  NA 
AUT  5 
AUT  5 
AUT  5 
GER  NA 
GER  NA 
GER  7 
GER  7 
GER  7 

Non riesco a pensare a un modo semplice per implementarlo. Prima di iniziare con for-loops, mi chiedevo se qualcuno avesse qualche idea su come risolvere questo problema.

Molte grazie !!

+0

Si potrebbe ottenere una risposta più rapida se hai modificato la tua domanda per includere una ragionevole struttura dei dati di test. –

+0

Si desidera [zoo :: na.locf()] (http://www.inside-r.org/packages/cran/zoo/docs/na.locf)! – smci

risposta

10

Ecco una soluzione ddply. Prova questo

library(plyr) 
ddply(DF, .(country), na.locf) 
    country value 
1  AUT <NA> 
2  AUT  5 
3  AUT  5 
4  AUT  5 
5  GER <NA> 
6  GER <NA> 
7  GER  7 
8  GER  7 
9  GER  7 

Modifica Da ddply aiuto si può trovare che

.variables: variables to split data frame by, 
as quoted variables, a formula or character vector. 

tanto altro alternative per ottenere quello che vuoi sono:

ddply(DF, "country", na.locf) 
ddply(DF, ~country, na.locf) 

nota che la sostituzione .variables con DF$variable non è permesso, ecco perché hai ricevuto un errore quando facevi questo.

DF è il vostro data.frame

+0

Incredibile, grazie! Esattamente quello di cui avevo bisogno. Ho provato 'ddply' prima, usando' ddply (DF, DF $ country, na.locf) 'e questo non ha funzionato. Qual è la differenza nell'usare la notazione '.()'? – rp1

+0

@ rp1 vedi la mia modifica. –

5

Spalato il data.frame con by e utilizzare na.locf sui sottoinsiemi:

do.call(rbind,by(data,data$country,na.locf)) 

Se si desidera rimuovere i nomi di riga:

do.call(rbind,unname(by(data,data$country,na.locf))) 
+0

'do.call' e' by' funzionano bene insieme. –

+0

Grazie, funziona anche così. Tuttavia, dovrei rinominare nuovamente i nomi delle righe in 'seq_len (nrow (data))'. Pertanto, ho scelto la risposta di cui sopra. Tuttavia, la tua soluzione potrebbe essere computazionalmente più veloce, dal momento che 'ddply' sembra essere piuttosto lento con dataset di grandi dimensioni. – rp1

+0

Bella soluzione di base: D +1 –

1

Se la velocità è una considerazione, allora questa soluzione unstack/stack è di circa 4 a 6 volte più veloce rispetto agli altri sul mio sistema anche se non comporta una linea leggermente più lunga di codice:

stack(lapply(unstack(data, value ~ country), na.locf, na.rm = FALSE)) 
3

È sufficiente dividere per paese, quindi fare un zoo::na.locf() o na.fill, compilando a destra. Ecco un esempio che mostra esplicitamente la sintassi arg di tre componenti di na.Fill:

library(plyr) 
library(zoo) 

data <- data.frame(country=c("AUT", "AUT", "AUT", "AUT", "GER", "GER", "GER", "GER", "GER"), value=c(NA, 5, NA, NA, NA, NA, 7, NA, NA)) 

# The following is equivalent to na.locf 
na.fill.right <- function(...) { na.fill(..., list(left=NA,interior=NA,right="extend")) } 

ddply(data, .(country), na.fill.right) 

    country value 
1  AUT <NA> 
2  AUT  5 
3  AUT  5 
4  AUT  5 
5  GER <NA> 
6  GER <NA> 
7  GER  7 
8  GER  7 
9  GER  7 
+0

@Gregor, quindi OP voleva anche dividere per paese, mi mancava quello e la menzione "na.locf", erano sepolti nel terzo paragrafo. Funziona perfettamente ora. Normalmente il titolo e il primo paragrafo dovrebbero specificare la domanda, non vedo perché non li hai aggiustati, l'ho appena fatto. Qualcuno di voi potrebbe e dovrebbe averlo corretto negli ultimi 1,5 anni. Puoi rimuovere il tuo downvote ora. – smci

11

Una versione moderna della soluzione ddply è quello di utilizzare il pacchetto dplyr:

library(dplyr) 
DF %>% 
    group_by(county) %>% 
    mutate(value = na.locf(value, na.rm = F))  
+1

+1 per la versione di dplyr. @ Gregor, si noti che è necessario aggiungere 'na.rm = F' alla chiamata' na.locf() '. Altrimenti ciò genera un errore. – Felix

+0

Grazie, Felix --- modificato in. – Gregor

+0

c'è un modo per farlo per più di una colonna in una volta? –

2

Il modo tidyverse, anche se non utilizzando LOCF, è:

library(tidyverse) 

data %>% 
    group_by(country) %>% 
    fill(value) 

Source: local data frame [9 x 2] 
Groups: country [2] 

country value 
(fctr) (dbl) 
1  AUT NA 
2  AUT  5 
3  AUT  5 
4  AUT  5 
5  GER NA 
6  GER NA 
7  GER  7 
8  GER  7 
9  GER  7 
Problemi correlati