2012-02-28 10 views
5

Prima di tutto questa domanda non riguarda il tentativo di risolvere un problema specifico. Come nuovo arrivato a R, sto anche lavorando per creare codice più efficiente e procedure di creazione del codice. Ottenere le prospettive su diversi metodi di programmazione e persino stili è la ragione alla base di questa domanda.Code Building Process and Embedded Functions

Qui di seguito sono tre modi per codificare qualcosa:

Primo Ecco i dati di esempio:

stackexample <- c(52,50,45,49.5,50.5,12,10,14,11.5,12,110,108,106,101,104) 
dim(stackexample)<- c(5,3) 

Un metodo: Fare la matematica nella funzione senza definire alcun oggetto

ertimesIVCV1 <- function (x) 
{ (solve(var(log((x[-nrow(x),])/(x[-1,])))))%*% 
    ((1+(log(x[1,]/(x)[nrow(x),])))^(1/nrow(x))-1)} 

ertimesIVCV1(stackexample) 

Metodo due: Definire gli oggetti nella funzione e quindi manipolare quegli oggetti

ertimesIVCV2 <- function (x) 
{ IVCV <- solve(var(log((x[-nrow(x),])/(x[-1,])))); 
    retsexcess <- (1+(log(x[1,]/(x)[nrow(x),])))^(1/nrow(x))-1; 
    IVCV%*%retsexcess} 

ertimesIVCV2(stackexample) 

Metodo Tre: definire diverse funzioni e chiamare queste funzioni in "sintesi come" funzione

IVCV <- function (x) {solve(var(log((x[-nrow(x),])/(x[-1,]))))} 
retsexcess <- function(x) (1+(log(x[1,]/(x)[nrow(x),])))^(1/nrow(x))-1 
ertimesIVCV3 <- function (x) {IVCV(x)%*%retsexcess(x)} 

ertimesIVCV3(stackexample) 

Quindi tutto produrre la stessa risposta:

  [,1] 
[1,] 1.4430104 
[2,] -0.1365155 
[3,] 11.8088378 

ma come si può vedere tre diversi approcci.

Esiste un numero ottimale di funzioni incorporate o dovremmo sempre cercare di elencare esplicitamente tutta la matematica? Quanti livelli di funzioni all'interno delle funzioni sono ottimali? L'uno o l'altro metodo è superiore alla velocità di calcolo? C'è una regola generale per questo? Come ti avvicini a questo? Eventuali commenti o suggerimenti o collegamenti sarebbero benvenuti e grazie!

Rye

+1

Vedere http://stackoverflow.com/q/4406873/210673, in particolare la risposta di @ GavinSimpson. – Aaron

risposta

6

IMHO, l'efficienza della velocità dovrebbe essere lo ultimo dei tuoi dubbi quando scrivi il codice, soprattutto se sei un principiante. Invece, il tuo obiettivo principale dovrebbe essere la semplicità, la leggibilità, la modularità.Non mi legga male, l'efficienza è una cosa grandiosa e troverai molti modi per rendere il tuo codice più veloce quando necessario, ma non dovrebbe essere una priorità da solo.

Quindi darò consigli sullo stile principalmente. Per illustrare, ecco come sarebbe la mia versione del tuo codice. Tieni presente che non so quale sia il codice che stai calcolando, quindi ho fatto del mio meglio per cercare di romperlo usando nomi di variabili significativi.

IVCV <- function(stack) { 

## This function computes [...] IVCV stands for [...] 
## Inputs: 
## - stack: a matrix where each column [...] 
## Output: a matrix [...] 

    n <- nrow(stack) # stack size 
    stack.ratios <- stack[-n, ]/stack[-1, ] 
    log.ratios <- log(stack.ratios) 
    ivcv   <- solve(var(log.ratios)) 

    return(ivcv) 
} 

ExcessReturn <- function(stack) { 

## This function computes [...] IVCV stands for [...] 
## Inputs: 
## - stack: a matrix where each column [...] 
## Output: a matrix [...] 

    n <- nrow(stack) # stack size 
    total.ratio <- stack[1, ]/stack[n, ] 
    excess.return <- (1 + log(total.ratio))^(1/n) - 1 

    return(excess.return) 
} 

ExcessReturnTimesIVCV <- function(stack) { 

## This function computes [...] IVCV stands for [...] 
## Inputs: 
## - stack: a matrix where each column [...] 
## Output: a vector [...] 

    return(IVCV(stack) %*% ExcessReturn(stack)) 
} 

1) sì, suddividere il codice in piccole funzioni. È migliore per leggibilità, flessibilità e manutenzione. Semplifica inoltre il test delle unità, dove è possibile progettare test per ogni elemento elementare di codice.

2) documentare una funzione includendo commenti sulla sua descrizione/input/output all'interno del corpo della funzione. In questo modo, dopo che la funzione è stata creata, l'utente può vedere la sua descrizione come parte della stampa della funzione (ad esempio, è sufficiente digitare ExcessReturnTimesIVCV nella GUI).

3) scomporre la complessità in più istruzioni. In questo momento, tutti e tre i tuoi suggerimenti sono difficili da capire, con troppe cose in corso su ogni riga. Una dichiarazione dovrebbe fare una cosa semplice in modo che possa leggere facilmente. È improbabile che la creazione di più oggetti rallenti il ​​processo e renderà il debug molto più semplice.

4) i nomi degli oggetti sono la chiave per rendere chiaro il tuo codice. Sceglili bene e usa una sintassi coerente. Uso UpperCamelCase per i nomi delle mie funzioni e le parole minuscole separate da punti per la maggior parte degli altri oggetti.

5) inserire commenti, in particolare dove 3) e 4) non sono sufficienti per rendere chiaro il codice. Nel mio esempio, ho scelto di utilizzare una variabile n. Sono andato contro la raccomandazione che i nomi delle variabili dovrebbero essere descrittivi, ma era per rendere il codice un po 'più leggero e dare espressioni come stack[-n, ]/stack[-1, ] qualche bella simmetria. Poiché n è un brutto nome, ho inserito un commento che ne spiega il significato. Avrei anche potuto inserire più commenti nel codice se avessi saputo cosa stavano realmente facendo le funzioni.

6) Utilizzare regole di sintassi coerenti, principalmente per migliorare la leggibilità. Sentirai diverse opinioni su ciò che dovrebbe essere usato qui. In generale, non esiste un approccio migliore. La cosa più importante è fare una scelta e attenervisi. Quindi ecco i miei suggerimenti:

a) una dichiarazione per riga, nessun punto e virgola.

b) spaziatura e rientranza coerenti (senza linguette). Inserisco spazi dopo le virgole, attorno agli operatori binari. Io uso anche spaziatura aggiuntiva per allineare le cose se aiuta la leggibilità.

c) rinforzo coerente: fare attenzione al modo in cui si utilizzano le parentesi graffe per definire i blocchi, altrimenti è probabile che si verifichino problemi in modalità script. Vedere la Sezione 8.1.43 del R Inferno (un ottimo riferimento.)

Buona fortuna!

6

Se l'obiettivo è tempo di efficienza allora la risposta con gli esempi offerti è "chi se ne frega?". Il sovraccarico delle chiamate di funzione non è ciò che determina l'efficienza. Probabilmente dovresti concentrarti su altri problemi, come la comprensione da parte dell'utente e la capacità di mantenere il codice.

require(rbenchmark) 
benchmark(replications=100, ver1= ertimesIVCV1(stackexample), 
ver2=ertimesIVCV2(stackexample), 
ver3 = ertimesIVCV3(stackexample)) 
# ------------------ 
    test replications elapsed relative user.self sys.self user.child sys.child 
1 ver1   100 0.030 1.000000  0.03  0   0   0 
2 ver2   100 0.030 1.000000  0.03  0   0   0 
3 ver3   100 0.031 1.033333  0.03  0   0   0 
4

Non sono d'accordo con DWin (anche se non proprio, sto solo facendo un giro diverso). Se l'obiettivo è il tuo tempo-efficienza, allora abbiamo alcuni casi. Se stai facendo qualcosa una volta, allora sono d'accordo con "a chi importa?". Fai quello che vuoi/qualsiasi cosa tu pensi in quel momento, probabilmente il metodo 1 o 2.

Il vantaggio del Metodo 3 è in ripetibilità. Se stai digitando lo stesso codice più di un paio di volte, la tua efficienza è in calo. Mettilo in una funzione, e risparmia la digitazione e in particolare la possibilità di digitare erroneamente. Vedo che stai già parlando di mettere le cose in una funzione, ma la tua funzione IVCV sarebbe utile come utilità o per altre funzioni? Altrimenti, non preoccupartene.

Quanto più grande diventa un progetto, tanto migliore diventa per romperlo in pezzi che ottengono la propria funzione. Ciò può rendere l'organizzazione, il debug e le modifiche andare molto più agevolmente.