2016-03-31 27 views
15

ObiettiviUtilizzando standard di valutazione e do_ per eseguire simulazioni su una griglia di parametri senza do.call

voglio usare dplyr per eseguire simulazioni su griglie di parametri. Specificamente, vorrei una funzione che posso utilizzare in un altro programma che

  • viene passato un data.frame
  • per ogni riga calcola una simulazione utilizzando ogni colonna come argomento
  • anche è passato un certo dati aggiuntivi (ad esempio, condizioni iniziali)

Ecco il mio approccio

require(dplyr) 
run <- function(data, fun, fixed_parameters, ...) { 
    ## .... 
    ## argument checking 
    ## 

    fixed_parameters <- as.environment(fixed_parameters) 
    grouped_out <- do_(rowwise(data), ~ do.call(fun, c(., fixed_parameters, ...))) 
    ungroup(grouped_out) 
} 

Th è un lavoro. Ad esempio, per

growth <- function(n, r, K, b) { 
    # some dynamical simulation 
    # this is an obviously-inefficient way to do this ;) 
    n + r - exp(n)/K - b - rnorm(1, 0, 0.1) 
} 
growth_runner <- function(r, K, b, ic, ...) { 
    # a wrapper to run the simulation with some fixed values 
    n0 = ic$N0 
    T = ic$T 
    reps = ic$reps 
    data.frame(n_final = replicate(reps, {for(t in 1:T) { 
              n0 <- growth(n0, r, K, b) 
             }; 
             n0}) 
) 
} 

posso definire ed eseguire,

data <- expand.grid(b = seq(0.01, 0.5, length.out=10), 
         K = exp(seq(0.1, 5, length.out=10)), 
         r = seq(0.5, 3.5, length.out=10)) 
    initial_data = list(N0=0.9, T=5, reps=20) 
    output <- run(data, growth_runner, initial_data) 

Domanda

Anche se questo sembra funzionare, mi chiedo se c'è un modo per farlo senza do.call. (In parte a causa di issues with do.call.)

Sono davvero interessato a un modo per sostituire la riga grouped_out <- do_(rowwise(data), ~ do.call(fun, c(., fixed_parameters, ...))) con qualcosa che fa la stessa cosa ma senza do.call. Modifica: Un approccio che evita in qualche modo le penalizzazioni delle prestazioni dell'utilizzo di do.call descritto nel collegamento sopra funzionerebbe anche.

note e riferimenti

+1

FWIW sembra esattamente come 'plyr :: mdply'. Sfortunatamente i due pacchetti sono piuttosto incompatibili. – baptiste

+0

dang, non avrei mai trovato quella parte di 'plyr'! grazie per il puntatore – jaimedash

+1

Penso che probabilmente si voglia 'purrr :: invoke_rows' per questo, è l'equivalente moderno di' mdply'. http://rpackages.ianhowson.com/cran/purrr/man/by_row.html – Shorpy

risposta

1

Il sotto evita utilizzando do.call e presenta l'uscita nello stesso modo come l'OP.

Innanzitutto, sostituisci i parametri della funzione con un vettore che passerai - questo è ciò che passerai usando apply.

growth_runner <- function(data.in, ic, ...) { 
    # a wrapper to run the simulation with some fixed values 
    n0 = ic$N0 
    T = ic$T 
    reps = ic$reps 
    data.frame(n_final = replicate(reps, {for(t in 1:T) { 
    n0 <- growth(n0, data.in[3], data.in[2], data.in[1]) 
    }; 
    n0}) 
) 
} 

Imposta la griglia su cui eseguire la ricerca, proprio come hai fatto in precedenza.

data <- expand.grid(b = seq(0.01, 0.5, length.out=10), 
        K = exp(seq(0.1, 5, length.out=10)), 
        r = seq(0.5, 3.5, length.out=10)) 
initial_data = list(N0=0.9, T=5, reps=20) 

uso è previsto per passare attraverso la griglia, quindi aggiungere i risultati

output.mid = apply(data, 1, ic=initial_data, FUN=growth_runner) 
output <- data.frame('n_final'=unlist(output.mid)) 

E avete la vostra uscita senza chiamate a do.call o qualsiasi libreria esterna.

> dim(output) 
[1] 20000  1 
> head(output) 
    n_final 
1 -0.6375070 
2 -0.7617193 
3 -0.3266347 
4 -0.7921655 
5 -0.5874983 
6 -0.4083613 
+0

Spiacente, ti manca il contesto critico della domanda: utilizzando dplyr. (Prima riga della domanda). La modifica di 5/19 lo rende chiaro. Questo è comunque un codice utile per eseguire lo stesso compito generale in un modo meno generico. Grazie! – jaimedash

+0

Si noti inoltre che 'apply()' fallirà non appena si avranno parametri non numerici – hadley

0

È possibile sostituire la riga con do.call con la seguente (Grazie a @shorpy per indicare purrr:invoke_rows()):

grouped_out <- purrr::invoke_rows(fun, dplyr::rowwise(data), fixed_parameters) 

senza altre modifiche, questo darà un frame di dati con un colonna di data.frames, come

Source: local data frame [1,000 x 4] 
      b  K  r    .out 
     (dbl) (dbl) (dbl)    (chr) 
1 0.01000000 1.105171 0.5 <data.frame [20,1]> 
2 0.06444444 1.105171 0.5 <data.frame [20,1]> 
3 0.11888889 1.105171 0.5 <data.frame [20,1]> 

Per recuperare qualcosa più vicino al comportamento originale, sostituire e la linea finale della run con

dplyr::ungroup(tidyr::unnest(grouped_out, .out)) 

che dà

Source: local data frame [20,000 x 4] 

     b  K  r n_final 
    (dbl) (dbl) (dbl)  (dbl) 
1 0.01 1.105171 0.5 -0.6745470 
2 0.01 1.105171 0.5 -0.7500365 
3 0.01 1.105171 0.5 -0.6568312 

Nessun altro modifiche al codice sono necessari :)

+0

Non mi fiderei di ciò poiché 'invoke_rows()' potrebbe non essere lungo per questo mondo – hadley

+1

grazie per l'heads up! guarderò in 'pmap' (come nella tua risposta) invece – jaimedash

5

ho trovato un po 'difficile da seguire il codice, ma penso questo è equivalente

prima cosa definire una funzione che fa il calcolo che ti interessa:

growth_t <- function(n0, r, K, b, T) { 
    n <- n0 

    for (t in 1:T) { 
    n <- n + r - exp(n)/K - b - rnorm(1, 0, 0.1) 
    } 
    n 
} 

Poi ho definire i dati che si desidera variare, tra cui una variabile "fittizia" di ripetizioni:

data <- expand.grid(
    b = seq(0.01, 0.5, length.out = 5), 
    K = exp(seq(0.1, 5, length.out = 5)), 
    r = seq(0.5, 3.5, length.out = 5), 
    rep = 1:20 
) 

Quindi posso inviarlo in purrr::pmap_d(). pmap_d() esegue una mappa "parallela", ovvero prende una lista (o una cornice dati) come input e chiama la funzione variando tutti gli argomenti con nome per ogni iterazione. I parametri fissi vengono forniti dopo il nome della funzione.

library(purrr) 
data$output <- pmap_dbl(data[1:3], growth_t, n0 = 0.9, T = 5) 

Questo davvero non si sente come un problema dplyr per me, perché non è realmente circa la manipolazione dei dati.

+0

grazie! fair point re dplyr, è iniziato con 'dplyr :: do'. ma data la possibilità di estendere gli strumenti per i dati ordinati, e in particolare la direzione che si sta dirigendo con 'purrr' (ad esempio, http://stackoverflow.com/q/35505187/4598520), sono d'accordo che probabilmente è meglio descritto come problema di dati in ordine – jaimedash