2011-06-22 16 views
22

Mi trovo nella posizione di aver completato una grossa fetta di analisi e ora devo ripetere l'analisi con ipotesi di input leggermente diverse.Strategie per la ripetizione di grosse porzioni di analisi

L'analisi, in questo caso, comporta l'analisi del cluster, la tracciatura di numerosi grafici e l'esportazione di ID cluster e altre variabili di interesse. Il punto chiave è che si tratta di un'analisi approfondita, che deve essere ripetuta e confrontata solo due volte.

ho considerato:

  • Creazione di una funzione. Questo non è l'ideale, perché poi devo modificare il mio codice per sapere se sto valutando nella funzione o negli ambienti parent. Questo ulteriore sforzo sembra eccessivo, rende più difficile il debug e può introdurre effetti collaterali.
  • Avvolgere in un ciclo for. Di nuovo, non ideale, perché poi devo creare variabili di indicizzazione, che possono anche introdurre effetti collaterali.
  • Creazione di codice pre-amble, avvolgimento dell'analisi in un file separato e source. Funziona, ma sembra molto brutto e non ottimale.

L'obiettivo dell'analisi è terminare con un insieme di oggetti (in un elenco o in file di output separati) che posso analizzare ulteriormente per differenze.

Qual è una buona strategia per affrontare questo tipo di problema?

risposta

17

Rendere il codice riutilizzabile richiede un po 'di tempo, sforzo e contiene alcune sfide extra come ti dici.

La questione dell'investimento è probabilmente il problema chiave dell'informatica (se non in molti altri campi): scrivo uno script per rinominare 50 file in modo simile, oppure procedo a rinominarli manualmente .

La risposta, credo, è altamente personale e anche in quel caso, caso per caso. Se sei facile con la programmazione, potresti decidere di intraprendere il percorso di riutilizzo, poiché lo sforzo per te sarà relativamente basso (e anche allora i programmatori amano imparare nuovi trucchi, quindi è una motivazione nascosta e spesso controproducente).

Detto questo, nel tuo caso particolare: sceglierei l'opzione di sourcing: poiché hai intenzione di riutilizzare il codice solo 2 volte di più, uno sforzo maggiore andrebbe probabilmente sprecato (indichi che l'analisi è piuttosto estesa) . Quindi cosa succede se non è una soluzione elegante? Nessuno ti vedrà mai e tutti saranno contenti dei risultati rapidi.

Se si scopre che in un anno o giù di tempo il riutilizzo è superiore al previsto, è comunque possibile investire. E a quel punto, avrai anche (almeno) tre casi per i quali puoi confrontare i risultati della versione riutilizzabile e riscritta del tuo codice con i tuoi risultati attuali.

Se/quando so in anticipo che sto per riutilizzare il codice, cerco di tenerlo a mente durante lo sviluppo. In ogni caso, non scrivo quasi mai codice che non sia in una funzione (beh, a parte le due righe per SO e altre analisi out-of-the-box): trovo che questo mi renda più facile strutturare i miei pensieri.

+0

+1 perché lo farai solo altre due volte. La risposta dipende anche da quanto cambierai ogni volta attraverso l'analisi: solo pochi parametri, dati di input,? Trovo che ci sia spesso una transizione abbastanza fluida tra (1) estrarre i parametri chiave e definirli nella parte superiore del codice [o metterli in un file separato e 'source()' nel corpo dell'analisi] e (2) avvolgere il corpo del codice in una funzione. Non mi è chiaro quali sono le distinzioni tra ambiente principale e funzione del genitore. –

+0

+1 Questo è praticamente quello che ho fatto alla fine. Ho avvolto tutto il parametro che è cambiato per ogni corsa in un elenco. Quindi ha creato diversi elenchi (con la stessa struttura) contenenti i valori di input per ogni esecuzione.Per ogni iterazione, ho copiato gli elenchi necessari e salvato le variabili risultanti in elenchi di output. In altre parole, un po 'di wrapping di codice in un preambolo e pulizia, e lavoro svolto. Funziona. Va bene se è brutto ... – Andrie

3

Mi piace lavorare con la combinazione di un piccolo script di shell, un programma di ritaglio PDF e Sweave in questi casi. Questo ti restituisce bei rapporti e ti incoraggia a cercare. Di solito lavoro con diversi file, quasi come creare un pacchetto (almeno penso che sia così :).Ho un file separato per la giocoleria di dati e file separati per diversi tipi di analisi, come ad esempio descriptiveStats.R, regressions.R.

btw Ecco il mio piccolo script di shell,

#!/bin/sh 
R CMD Sweave docSweave.Rnw 
for file in `ls pdfs`; 
do pdfcrop pdfs/"$file" pdfs/"$file" 
done 
pdflatex docSweave.tex 
open docSweave.pdf 

Il file Sweave fonti in genere i file R di cui sopra in caso di necessità. Non sono sicuro che sia quello che stai cercando, ma questa è la mia strategia fino ad ora. Almeno credo che creare report trasparenti e riproducibili sia ciò che aiuta a seguire almeno una strategia.

+0

+1 Dovrò esplorare di più gli script di shell - Tendo a non usarli con R. Questo sembra equivalente, o per lo meno analogo, all'utilizzo di 'source'. – Andrie

+0

Tutto dipende un po 'dal sistema operativo. Non sono ancora sicuro se Python, script di shell o script Apple si adattino meglio alle mie esigenze, ma per quanto posso giudicare lo script di shell con R sembra davvero degno di attenzione, in particolare per i report automatizzati. –

10

Se possibile, impostare i parametri che differiscono tra set/avvia/esperimenti in un file di parametri esterno. Quindi, è possibile generare il codice, chiamare una funzione, persino utilizzare un pacchetto, ma le operazioni sono determinate da un piccolo insieme di parametri definiti esternamente.

Ad esempio, JSON funziona molto bene e i pacchetti RJSONIO e rjson consentono di caricare il file in un elenco. Supponi di caricarlo in una lista chiamata parametersNN.json. Un esempio è il seguente:

{ 
"Version": "20110701a", 
"Initialization": 
{ 
    "indices": [1,2,3,4,5,6,7,8,9,10], 
    "step_size": 0.05 
}, 
"Stopping": 
{ 
    "tolerance": 0.01, 
    "iterations": 100 
} 
} 

Salva che come "parameters01.json" e carico:

library(RJSONIO) 
Params <- fromJSON("parameters.json") 

e sei fuori e in esecuzione. (NB: Mi piace usare la versione unica #s nei miei file di parametri, solo per poter identificare il set in un secondo momento, se guardo l'elenco "parametri" all'interno di R.) Basta chiamare il tuo script e puntare ai parametri file, ad esempio:

Rscript --vanilla MyScript.R parameters01.json 

poi, all'interno del programma, identificare il file parametri dalla funzione commandArgs().

Successivamente, è possibile suddividere il codice in funzioni e pacchetti, ma questo è probabilmente il modo più semplice per rendere uno script vanilla generalizzabile a breve termine ed è una buona pratica a lungo termine, poiché il codice dovrebbe essere separato dalla specifica dei parametri run/dataset/speriment-dependent.

Modifica: per essere più precisi, vorrei anche specificare le directory oi file di input e output (o i nomi/i prefissi di nomi) nel JSON. Ciò rende molto chiaro come un set di parametri abbia portato a un particolare set di output. Tutto ciò che sta in mezzo è solo il codice che viene eseguito con una determinata parametrizzazione, ma il codice non dovrebbe davvero cambiare molto, dovrebbe?


Aggiornamento: Tre mesi, e molte migliaia di piste, più saggio di mia risposta precedente, direi che l'archiviazione esterna dei parametri in JSON è utile per 1-1000 sedute diverse. Quando il numero di parametri o configurazioni è compreso tra migliaia e sopra, è meglio passare all'utilizzo di un database per la gestione della configurazione. Ogni configurazione può avere origine in un JSON (o XML), ma essere in grado di affrontare diversi layout di parametri richiede una soluzione su scala più ampia, per la quale un database come SQLite (via RSQLite) è una soluzione eccellente.

Mi rendo conto che questa risposta è eccessiva per la domanda originale: come ripetere il lavoro solo un paio di volte, con alcune modifiche ai parametri, ma quando si ridimensionano a centinaia o migliaia di modifiche ai parametri nella ricerca in corso, sono disponibili strumenti più ampi necessario. :)

+0

Questa è stata una risposta estremamente utile per me. Non sono nemmeno andato fino a 'JSON', ma ho usato un piccolo script' .R' in una directory di configurazione. Il motore della griglia imposta una variabile ambientale con il nome del lavoro, quindi posso leggerlo e farlo estrarre il file di configurazione con lo stesso nome per quella corsa. Funziona bene per i miei bisogni; sembra che i tuoi siano molto più sostanziosi! –

+1

@ AriB.Friedman felice di aiutare! L'approccio DB sembra funzionare bene per le migliaia e oltre. Se non altro, puoi pensare alle impostazioni dei parametri come analoghe alla progettazione sperimentale - la memorizzazione dei parametri del progetto è scalabile in un DB e, in definitiva, vogliamo sapere perché le diverse esecuzioni hanno avuto esiti diversi. – Iterator

1

Tendo a spingere tali risultati in un elenco globale. Io uso Common Lisp ma poi R non è così diverso.

+1

+1 Sì, questo è quello che ho fatto alla fine. Creare un elenco di variabili che devono essere modificate e un altro elenco con i risultati. Quindi cambia le variabili locali dentro e fuori dalla lista. – Andrie

1

Troppo tardi per voi qui, ma io uso molto Sweave e molto probabilmente avrei usato un file Sweave dall'inizio (ad esempio se so che il prodotto finale deve essere una sorta di rapporto).

Per ripetere parti dell'analisi una seconda e terza volta, ci sono quindi due opzioni:

  • se i risultati sono piuttosto "indipendente" (cioè dovrebbe produrre 3 rapporti, confronto intendono le relazioni sono controllate fianco a fianco), e l'input modificato si presenta sotto forma di nuovi file di dati, che vanno nella propria directory insieme a una copia del file Sweave, e creo report separati (simili alla sorgente, ma per Sweave sono più naturali di per semplice fonte).

  • se preferisco ripetere esattamente la stessa cosa una o due volte all'interno di un file Sweave, prenderei in considerazione il riutilizzo di blocchi di codice. Questo è simile al brutto ciclo.

    Il motivo è che, ovviamente, i risultati sono insieme per il confronto, che sarebbe poi l'ultima parte del rapporto.

  • Se è chiaro fin dall'inizio che ci saranno alcuni set di parametri e un confronto, scrivo il codice in modo tale che non appena sto bene con ogni parte dell'analisi viene avvolto in una funzione (cioè sto scrivendo acutamente la funzione nella finestra dell'editor, ma valuto le righe direttamente nell'area di lavoro mentre scrivo la funzione).

Dato che ci si trova nella situazione descritta, sono d'accordo con Nick - niente di sbagliato con source e tutto il resto significa molto più sforzo, ora che lo avete già come script.

2

La terza opzione non è male. Lo faccio in molti casi. Puoi costruire un po 'più di struttura inserendo i risultati del codice pre-ampio negli ambienti e allegare quello che vuoi utilizzare per ulteriori analisi. Un esempio:

setup1 <- local({ 
      x <- rnorm(50, mean=2.0) 
      y <- rnorm(50, mean=1.0) 
      environment() 
      # ... 
     }) 

    setup2 <- local({ 
      x <- rnorm(50, mean=1.8) 
      y <- rnorm(50, mean=1.5) 
      environment() 
      # ... 
     }) 

attach(setup1) ed eseguire/codice sorgente vostra analisi

plot(x, y) 
t.test(x, y, paired = T, var.equal = T) 
... 

Al termine, detach(setup1) e collegare il secondo.

Ora, almeno è possibile passare facilmente da una configurazione all'altra. Mi ha aiutato un paio di volte

+0

+1 Questo approccio è davvero interessante. Generalmente cerco di evitare l'uso di 'attach', ma mi piace l'idea di creare ambienti per aggirare questo problema. Lo esplorerò ulteriormente. – Andrie

+0

Anche a me piace questo, con la stessa cautela nell'usare "attach" come ha detto Andrie. Ciò che è utile in questo è che è più generale di semplici elenchi di parametri e può includere calcoli. – Iterator

Problemi correlati