R Language
* applica famiglia di funzioni (funzionali)
Ricerca…
Osservazioni
Una funzione nella famiglia *apply
è un'astrazione di un ciclo for
. Rispetto for
cicli for
*apply
funzioni *apply
hanno i seguenti vantaggi:
- Richiede meno codice per scrivere.
- Non ha un contatore di iterazione.
- 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é lamean
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 dilapply()
ma tenta di semplificare l'output a un vettore oa una matrice.-
vapply()
= una variante disapply()
in cui deve essere specificato il tipo dell'oggetto di output.
-
-
mapply()
= comelapply()
ma può passare più vettori come input per la funzione specificata. Può essere semplificato comesapply()
.-
Map()
è un alias permapply()
conSIMPLIFY = 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