2012-05-11 12 views
8

dire che ho un frame di dati come questo:Fill in frame di dati con i valori da file sopra

ID, ID_2, FIRST, VALUE 
----------------------- 
'a', 'aa', TRUE, 2 
'a', 'ab', FALSE, NA 
'a', 'ac', FALSE, NA 
'b', 'aa', TRUE, 5 
'b', 'ab', FALSE, NA 

Quindi valore è impostato solo per FIRST = TRUE una volta per ogni ID. ID_2 può essere duplicato tra gli ID, ma non è necessario.

Come si inseriscono i numeri dalle prime righe di ciascun ID in tutte le righe di quell'ID, in modo che la colonna VALUE diventi 2, 2, 2, 5, 5?

So che potrei semplicemente eseguire il loop su tutti gli ID con un ciclo for, ma sto cercando un modo più efficiente.

risposta

16

Se è necessario solo per portare avanti i valori della colonna VALORE, allora penso che si può utilizzare la funzione na.lofc() da zoo pacchetto. Ecco un esempio:

a<-c(1,NA,NA,2,NA) 
na.locf(a) 
[1] 1 1 1 2 2 
4

Se il valore per uno specifico ID appare sempre nel primo record, che sembra essere il caso per i dati, è possibile utilizzare match per trovare quel record:

df <- read.csv(textConnection(" 

ID, ID_2, FIRST, VALUE 
'a', 'aa', TRUE, 2 
'a', 'ab', FALSE, NA 
'a', 'ac', FALSE, NA 
'b', 'aa', TRUE, 5 
'b', 'ab', FALSE, NA 

")) 

df$VALUE <- df$VALUE[match(df$ID, df$ID)] 
df 
# ID ID_2 FIRST VALUE 
# 1 'a' 'aa' TRUE  2 
# 2 'a' 'ab' FALSE  2 
# 3 'a' 'ac' FALSE  2 
# 4 'b' 'aa' TRUE  5 
# 5 'b' 'ab' FALSE  5 
19

La domanda richiede efficienza rispetto a un ciclo. Ecco un confronto tra quattro soluzioni:

  1. zoo::na.locf, che introduce una dipendenza pacchetto e anche se gestisce molti casi limite, è necessario che i valori 'vuoti' sono NA. Le altre soluzioni si adattano facilmente agli spazi vuoti non NA.

  2. Un semplice ciclo in R. basamento

  3. Una funzione ricorsiva in R. basamento

  4. mia soluzione Vectorised in R. basamento

  5. La nuova funzione fill() in tidyr versione 0.3 .0., Che funziona su data.frames.

Si noti che la maggior parte di queste soluzioni sono per i vettori, non per i frame di dati, quindi non controllano alcuna colonna ID. Se il frame di dati non è raggruppato per ID, con il valore da riempire giù essendo nella parte superiore di ciascun gruppo, allora si potrebbe provare una funzione di windowing in dplyr o data.table

# A popular solution 
f1 <- zoo::na.locf 

# A loop, adapted from https://stat.ethz.ch/pipermail/r-help/2008-July/169199.html 
f2 <- function(x) { 
    for(i in seq_along(x)[-1]) if(is.na(x[i])) x[i] <- x[i-1] 
    x 
} 

# Recursion, also from https://stat.ethz.ch/pipermail/r-help/2008-July/169199.html 
f3 <- function(z) { 
    y <- c(NA, head(z, -1)) 
    z <- ifelse(is.na(z), y, z) 
    if (any(is.na(z))) Recall(z) else z } 

# My own effort 
f4 <- function(x, blank = is.na) { 
    # Find the values 
    if (is.function(blank)) { 
    isnotblank <- !blank(x) 
    } else { 
    isnotblank <- x != blank 
    } 
    # Fill down 
    x[which(isnotblank)][cumsum(isnotblank)] 
} 

# fill() from the `tidyr` version 0.3.0 
library(tidyr) 
f5 <- function(y) { 
    fill(y, column) 
} 
# Test data, 2600 values, ~58% blanks 
x <- rep(LETTERS, 100) 
set.seed(2015-09-12) 
x[sample(1:2600, 1500)] <- NA 
x <- c("A", x) # Ensure the first element is not blank 
y <- data.frame(column = x, stringsAsFactors = FALSE) # data.frame version of x for tidyr 

# Check that they all work (they do) 
identical(f1(x), f2(x)) 
identical(f1(x), f3(x)) 
identical(f1(x), f4(x)) 
identical(f1(x), f5(y)$column) 

library(microbenchmark) 
microbenchmark(f1(x), f2(x), f3(x), f4(x), f5(y)) 

Risultati:

Unit: microseconds 
    expr  min  lq  mean median  uq  max neval 
f1(x) 422.762 466.6355 508.57284 505.6760 527.2540 837.626 100 
f2(x) 2118.914 2206.7370 2501.04597 2312.8000 2497.2285 5377.018 100 
f3(x) 7800.509 7832.0130 8127.06761 7882.7010 8395.3725 14128.107 100 
f4(x) 52.841 58.7645 63.98657 62.1410 65.2655 104.886 100 
f5(y) 183.494 225.9380 305.21337 331.0035 350.4040 529.064 100 
+1

mi piace. Aggiunta minore a f4 per gestire NA precedenti. l'ultima riga dovrebbe essere: c (NA, x [quale (isnotblank)]) [cumsum (isnotblank) +1] – DangerMouse

+0

Ottima risposta. f4 funziona anche con i personaggi. – BCC

+0

Questo è brillante ma potrebbe trarre beneficio da qualche spiegazione. – C8H10N4O2

0

+1 per @nacnudus Maniglie spazi vuoti

f4 <- function(x, blank = is.na) { 

    # Find the values 
    if (is.function(blank)) { 
    isnotblank <- !blank(x) 
    } else { 
    isnotblank <- x != blank 
    } 

    # Fill down 
    xfill <- cumsum(isnotblank) 
    xfill[ xfill == 0 ] <- NA 

    # Replace Blanks 
    xnew <- x[ which(isnotblank) ][ xfill ] 
    xnew[is.na(xnew)] <- blank 
    return(xnew) 
} 
Problemi correlati