2014-09-16 19 views
6

Ho un telaio dati:Spostare AN all'interno dataframe in R

df <- structure(list(a = c(NA, NA, 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L), b = c(NA, NA, NA, 1L, 2L, 3L, 4L, 5L, 6L, 7L), d = c(NA, NA, NA, NA, 1L, 2L, 3L, 4L, 5L, 6L)), .Names = c("a", "b", "d"), row.names = c(NA, -10L), class = "data.frame") 

> df 
    a b d 
1 NA NA NA 
2 NA NA NA 
3 1 NA NA 
4 2 1 NA 
5 3 2 1 
6 4 3 2 
7 5 4 3 
8 6 5 4 
9 7 6 5 
10 8 7 6 

Vorrei muoversi su ogni colonna e spostare le AN alla parte inferiore del telaio dati:

> df.out 
    a b d 
1 1 1 1 
2 2 2 2 
3 3 3 3 
4 4 4 4 
5 5 5 5 
6 6 6 6 
7 7 7 NA 
8 8 NA NA 
9 NA NA NA 
10 NA NA NA 

aggiornamento per rendere più chiare le mie domande ..

df <- structure(list(a = c(NA, NA, 1, 5, 34, 7, 3, 5, 8, 4), b = c(NA, 
NA, NA, 57, 2, 7, 9, 5, 12, 100), d = c(NA, NA, NA, NA, 5, 7, 
2, 8, 2, 5)), .Names = c("a", "b", "d"), row.names = c(NA, -10L 
), class = "data.frame") 

> df 
    a b d 
1 NA NA NA 
2 NA NA NA 
3 1 NA NA 
4 5 57 NA 
5 34 2 5 
6 7 7 7 
7 3 9 2 
8 5 5 8 
9 8 12 2 
10 4 100 5 

dovrebbe comportare:

Sembra un compito facile ma sono bloccato su dove iniziare .. Potete aiutare?

+1

Si noti che i dati effettivamente modificati qui (guardando dal punto di vista di fila): è che intenzionale, o vuoi semplicemente ordinare gli AN in basso? – PascalVKooten

+0

Questo è di proposito, in pratica voglio che tutti i numeri siano allineati in termini di righe, quindi ogni colonna dovrebbe essere spostata singolarmente. – beetroot

+0

Non riesco a immaginare cosa ti serve, ma ho inviato una risposta. Probabilmente vorrai cercare una soluzione data.table, sono generalmente le più veloci. – PascalVKooten

risposta

4

Dopo malinteso completamente la questione, ecco la mia risposta definitiva:

# named after beetroot for being the first to ever need this functionality 
beetroot <- function(x) { 
    # count NA 
    num.na <- sum(is.na(x)) 
    # remove NA 
    x <- x[!is.na(x)] 
    # glue the number of NAs at the end 
    x <- c(x, rep(NA, num.na)) 
    return(x) 
} 

# apply beetroot over each column in the dataframe 
as.data.frame(lapply(df, beetroot)) 

Si conterà le AN, rimuovere le AN e AN colla nella parte inferiore per ogni colonna nella cornice di dati.

+0

Non è necessario eseguire 'as.matrix' nel tuo ultimo esempio. 'apply' costruisce automaticamente a matrice. Ma dovresti racchiuderlo in 'as.data.frame' per riportarlo su un data.frame. – Thomas

+0

@Thomas è d'accordo. – PascalVKooten

+0

Grazie, ma come nella risposta di Thomas questo ordina i valori in ogni colonna ma ho bisogno che l'ordine delle righe in ogni colonna rimanga lo stesso (la posizione delle NA dovrebbe essere l'unica cosa che cambia) – beetroot

12

Un'altra soluzione utilizzando lapply (senza ordinare/riordino del dati- secondo i vostri commenti)

df[] <- lapply(df, function(x) c(x[!is.na(x)], x[is.na(x)])) 
df 
#  a b d 
# 1 1 57 5 
# 2 5 2 7 
# 3 34 7 2 
# 4 7 9 8 
# 5 3 5 2 
# 6 5 12 5 
# 7 8 100 NA 
# 8 4 NA NA 
# 9 NA NA NA 
# 10 NA NA NA 

o utilizzando data.table al fine di aggiornare df per riferimento, piuttosto che la creazione di una copia di esso (che ha vinto la soluzione' t ordinare i dati non)

library(data.table) 
setDT(df)[, names(df) := lapply(.SD, function(x) c(x[!is.na(x)], x[is.na(x)]))] 
df 
#  a b d 
# 1: 1 57 5 
# 2: 5 2 7 
# 3: 34 7 2 
# 4: 7 9 8 
# 5: 3 5 2 
# 6: 5 12 5 
# 7: 8 100 NA 
# 8: 4 NA NA 
# 9: NA NA NA 
# 10: NA NA NA 

Alcuni benchmark rivelano la soluzione di base è il più veloce in assoluto:

library("microbenchmark") 
david <- function() lapply(df, function(x) c(x[!is.na(x)], x[is.na(x)])) 
dt <- setDT(df) 
david.dt <- function() dt[, names(dt) := lapply(.SD, function(x) c(x[!is.na(x)], x[is.na(x)]))] 

microbenchmark(as.data.frame(lapply(df, beetroot)), david(), david.dt()) 
# Unit: microseconds 
#         expr  min  lq median  uq  max neval 
# as.data.frame(lapply(df, beetroot)) 1145.224 1215.253 1274.417 1334.7870 4028.507 100 
#        david() 116.515 127.382 140.965 149.7185 308.493 100 
#       david.dt() 3087.335 3247.920 3330.627 3415.1460 6464.447 100 
+1

Congratulazioni per aver capito cosa voleva l'OP dall'inizio. – Thomas

+1

Mi piace. Il mio unico altro pensiero su un processo simile sarebbe: 'lapply (df, function (x) x [order (is.na (x))])' che è probabilmente meno efficiente nei timing. – thelatemail

+1

Grazie per la tua risposta - scusa, c'erano così tante risposte/commenti allo stesso tempo, quindi ho completamente perso la tua e l'ho vista solo ora! – beetroot

0

Se hai piccolo numero di colonne, suggerisco:

data.frame(a=sort(example$a, na.last=T), b=sort(example$b, na.last=T), d=sort(example$d, na.last=T)) 

migliore, Adii_

4

Per divertimento, si può anche fare uso di length<- e na.omit.

Ecco che cosa combinazione avrebbe fatto:

x <- c(NA, 1, 2, 3) 
x 
# [1] NA 1 2 3 
`length<-`(na.omit(x), length(x)) 
# [1] 1 2 3 NA 

applicato al tuo problema, la soluzione potrebbe essere:

df[] <- lapply(df, function(x) `length<-`(na.omit(x), nrow(df))) 
df 
#  a b d 
# 1 1 57 5 
# 2 5 2 7 
# 3 34 7 2 
# 4 7 9 8 
# 5 3 5 2 
# 6 5 12 5 
# 7 8 100 NA 
# 8 4 NA NA 
# 9 NA NA NA 
# 10 NA NA NA