Ricerca…


Osservazioni

Una funzione nella famiglia *apply è un'astrazione di un ciclo for . Rispetto for cicli for *apply funzioni *apply hanno i seguenti vantaggi:

  1. Richiede meno codice per scrivere.
  2. Non ha un contatore di iterazione.
  3. Non usa variabili temporanee per memorizzare risultati intermedi.

Tuttavia for i loop sono più generale e ci può dare un maggiore controllo che consente di ottenere calcoli complessi che non sono sempre banale da fare utilizzando *apply funzioni.

La relazione tra cicli for e *apply funzioni è spiegata nella documentazione for loop .

I membri della *apply Famiglia

La famiglia di funzioni *apply contiene diverse varianti dello stesso principio che si differenziano principalmente in base al tipo di output restituito.

funzione Ingresso Produzione
apply matrix , data.frame o array vettore o matrice (a seconda della lunghezza di ciascun elemento restituito)
sapply vettore o list vettore o matrice (a seconda della lunghezza di ciascun elemento restituito)
lapply vettore o list list
vapply vettore o `lista vettore o matrice (a seconda della lunghezza di ciascun elemento restituito) della classe designata dall'utente
mapply più vettori, lists o una combinazione list

Vedi "Esempi" per vedere come viene utilizzata ciascuna di queste funzioni.

Utilizzare le funzioni anonime con apply

apply è usato per valutare una funzione (forse anonima) oltre i margini di un array o di una matrice.

Usiamo il set di dati iris per illustrare questa idea. Il set di dati iris ha misurazioni di 150 fiori di 3 specie. Vediamo come questo set di dati è strutturato:

> head(iris)

  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1         5.1          3.5          1.4         0.2  setosa
2         4.9          3.0          1.4         0.2  setosa
3         4.7          3.2          1.3         0.2  setosa
4         4.6          3.1          1.5         0.2  setosa
5         5.0          3.6          1.4         0.2  setosa
6         5.4          3.9          1.7         0.4  setosa

Ora, immagina di voler conoscere la media di ciascuna di queste variabili. Un modo per risolvere questo potrebbe essere l'uso di un ciclo for , ma i programmatori R preferiranno spesso utilizzare apply (per i motivi per cui, vedi Note):

> apply(iris[1:4], 2, mean)

Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.199333
  • Nel primo parametro, suddividiamo il iris per includere solo le prime 4 colonne, poiché la mean funziona solo su dati numerici.
  • Il secondo valore del parametro 2 indica che vogliamo lavorare solo sulle colonne (il secondo indice dell'array r × c); 1 darebbe i mezzi di riga.

Allo stesso modo possiamo calcolare valori più significativi:

# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)

Avvertenza : R ha alcune funzioni integrate che sono migliori per il calcolo delle somme di colonne e righe e significa: colMeans e rowMeans .

Ora, facciamo un compito diverso e più significativo: calcoliamo la media solo per quei valori che sono più grandi di 0.5 . Per questo, creeremo la nostra funzione mean .

> our.mean.function <- function(x) { mean(x[x > 0.5]) }
> apply(iris[1:4], 2, our.mean.function)

Sepal.Length  Sepal.Width Petal.Length  Petal.Width 
    5.843333     3.057333     3.758000     1.665347

(Nota la differenza nella media di Petal.Width )

Ma cosa succede se non vogliamo usare questa funzione nel resto del nostro codice? Quindi, possiamo usare una funzione anonima e scrivere il nostro codice in questo modo:

apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })

Quindi, come abbiamo visto, possiamo usare apply per eseguire la stessa operazione su colonne o righe di un set di dati usando solo una riga.

Avvertenza : poiché apply restituisce tipi di output molto diversi a seconda della lunghezza dei risultati della funzione specificata, potrebbe non essere la scelta migliore nei casi in cui non si lavori in modo interattivo. Alcuni degli altri *apply funzioni familiari sono un po 'più prevedibili (vedere Note).

Caricamento di file collettivi

per un numero elevato di file che potrebbero dover essere utilizzati in un processo simile e con nomi di file ben strutturati.

in primo luogo deve essere creato un vettore dei nomi dei file da accedere, ci sono più opzioni per questo:

  • Creare manualmente il vettore con paste0()

     files <- paste0("file_", 1:100, ".rds")
    
  • L'utilizzo di list.files() con un termine di ricerca regolare per il tipo di file richiede la conoscenza delle espressioni regolari ( regex ) se altri file dello stesso tipo si trovano nella directory.

     files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
    

dove X è un vettore di parte del formato di denominazione dei file utilizzato.

lapply emetterà ogni risposta come elemento di una lista.

readRDS è specifico per i file .rds e cambierà in base all'applicazione del processo.

my_file_list <- lapply(files, readRDS)

Questo non è necessariamente più veloce di un ciclo for dal test, ma consente a tutti i file di essere un elemento di un elenco senza assegnarli esplicitamente.

Infine, abbiamo spesso bisogno di caricare più pacchetti contemporaneamente. Questo trucco può farlo abbastanza facilmente applicando library() a tutte le librerie che vogliamo importare:

lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)

Combinare più `data.frames` (` lapply`, `mapply`)

In questo esercizio, genereremo quattro modelli di regressione lineare di bootstrap e combineremo i riepiloghi di questi modelli in un singolo frame di dati.

library(broom)

#* Create the bootstrap data sets
BootData <- lapply(1:4,
                   function(i) mtcars[sample(1:nrow(mtcars),
                                             size = nrow(mtcars),
                                             replace = TRUE), ])

#* Fit the models
Models <- lapply(BootData,
                 function(BD) lm(mpg ~ qsec + wt + factor(am),
                                 data = BD))

#* Tidy the output into a data.frame
Tidied <- lapply(Models,
                 tidy)

#* Give each element in the Tidied list a name
Tidied <- setNames(Tidied, paste0("Boot", seq_along(Tidied)))

A questo punto, possiamo adottare due approcci per inserire i nomi nel data.frame.

#* Insert the element name into the summary with `lapply`
#* Requires passing the names attribute to `lapply` and referencing `Tidied` within
#* the applied function.
Described_lapply <- 
 lapply(names(Tidied),
        function(nm) cbind(nm, Tidied[[nm]]))

Combined_lapply <- do.call("rbind", Described_lapply)

#* Insert the element name into the summary with `mapply`
#* Allows us to pass the names and the elements as separate arguments.
Described_mapply <- 
 mapply(
  function(nm, dframe) cbind(nm, dframe),
  names(Tidied),
  Tidied,
  SIMPLIFY = FALSE)

Combined_mapply <- do.call("rbind", Described_mapply)

Se sei un fan delle magrittr stile magrittr , puoi eseguire l'intera operazione in una singola catena (anche se potrebbe non essere prudente farlo se hai bisogno di uno qualsiasi degli oggetti intermediari, come gli stessi oggetti del modello):

library(magrittr)
library(broom)
Combined <- lapply(1:4,
                   function(i) mtcars[sample(1:nrow(mtcars),
                                             size = nrow(mtcars),
                                             replace = TRUE), ]) %>%
 lapply(function(BD) lm( mpg ~ qsec + wt + factor(am), data = BD)) %>%
 lapply(tidy) %>%
 setNames(paste0("Boot", seq_along(.))) %>%
 mapply(function(nm, dframe) cbind(nm, dframe),
        nm = names(.),
        dframe = .,
        SIMPLIFY = FALSE) %>%
 do.call("rbind", .)

Utilizzo di funzionali incorporati

Funzionalità incorporate: lapply (), sapply () e mapply ()

R è dotato di funzionali incorporati, di cui forse la più nota è la famiglia di funzioni applicabili. Ecco una descrizione di alcune delle funzioni di applicazione più comuni:

  • lapply() = prende una lista come argomento e applica la funzione specificata alla lista.
  • sapply() = lo stesso di lapply() ma tenta di semplificare l'output a un vettore oa una matrice.
    • vapply() = una variante di sapply() in cui deve essere specificato il tipo dell'oggetto di output.
  • mapply() = come lapply() ma può passare più vettori come input per la funzione specificata. Può essere semplificato come sapply() .
    • Map() è un alias per mapply() con SIMPLIFY = FALSE .

lapply ()

lapply() può essere utilizzato con due diverse iterazioni:

  • lapply(variable, FUN)
  • lapply(seq_along(variable), FUN)
# Two ways of finding the mean of x
set.seed(1)
df <- data.frame(x = rnorm(25), y = rnorm(25))
lapply(df, mean)
lapply(seq_along(df), function(x) mean(df[[x]))

sapply ()

sapply() tenterà di risolvere il suo output su un vettore o una matrice.

# Two examples to show the different outputs of sapply()
sapply(letters, print)  ## produces a vector
x <- list(a = 1:10, beta = exp(-3:3), logic = c(TRUE,FALSE,FALSE,TRUE))
sapply(x, quantile)  ## produces a matrix

mapply ()

mapply() funziona in modo molto simile a lapply() eccetto che può prendere più vettori come input (da cui il m per multivariato).

mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply

Utilizzo di funzionali definiti dall'utente

Funzionalità definite dall'utente

Gli utenti possono creare i propri funzionali con vari gradi di complessità. I seguenti esempi sono tratti da Functionals di Hadley Wickham:

randomise <- function(f) f(runif(1e3))
        
lapply2 <- function(x, f, ...) {
    out <- vector("list", length(x))
    for (i in seq_along(x)) {
        out[[i]] <- f(x[[i]], ...)
    }
    out
}

Nel primo caso, randomise accetta un singolo argomento f , e lo chiama su un campione di variabili casuali uniformi. Per dimostrare l'equivalenza, chiamiamo set.seed qui sotto:

set.seed(123)
randomise(mean)
#[1] 0.4972778
    
set.seed(123)
mean(runif(1e3))
#[1] 0.4972778


set.seed(123)
randomise(max)
#[1] 0.9994045

set.seed(123)
max(runif(1e3))
#[1] 0.9994045

Il secondo esempio è una reimplementazione di base::lapply , che utilizza le funzioni per applicare un'operazione ( f ) a ciascun elemento di una lista ( x ). Il parametro ... consente all'utente di passare argomenti aggiuntivi a f , come l'opzione na.rm nella funzione mean :

lapply(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
# 
# [[2]]
# [1] NA

lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean)
# [[1]]
# [1] 3
# 
# [[2]]
# [1] NA


lapply(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
# 
# [[2]]
# [1] 4

lapply2(list(c(1, 3, 5), c(2, NA, 6)), mean, na.rm = TRUE)
# [[1]]
# [1] 3
# 
# [[2]]
# [1] 4


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow