R Language
* familie van functies toepassen (functionals)
Zoeken…
Opmerkingen
Een functie in de *apply familie is een abstractie van een for lus. In vergelijking met de for loops *apply functies de volgende voordelen:
- Vereisen minder code om te schrijven.
- Heeft geen iteratieteller.
- Gebruikt geen tijdelijke variabelen om tussentijdse resultaten op te slaan.
Maar for loops zijn meer algemeen en kan ons meer controle mogelijk maakt om complexe berekeningen die niet altijd triviaal te doen met behulp van bereiken geven *apply functies.
De relatie tussen for lussen en *apply functies wordt uitgelegd in de documentatie voor for lussen .
Leden van de *apply Family van *apply
De *apply familie van functies bevat verschillende varianten van hetzelfde principe die verschillen, voornamelijk op basis van het soort output dat ze retourneren.
| functie | Invoer | uitgang |
|---|---|---|
apply | matrix , data.frame of array | vector of matrix (afhankelijk van de lengte van elk geretourneerd element) |
sapply | vector of list | vector of matrix (afhankelijk van de lengte van elk geretourneerd element) |
lapply | vector of list | list |
vapply | vector of `lijst | vector of matrix (afhankelijk van de lengte van elk geretourneerd element) van de door de gebruiker aangewezen klasse |
mapply | meerdere vectoren, lists of een combinatie | list |
Zie "Voorbeelden" om te zien hoe elk van deze functies wordt gebruikt.
Gebruik anonieme functies met toepassen
apply wordt gebruikt om een functie (misschien een anonieme) te evalueren over de marges van een matrix of matrix.
Laten we de iris gegevensset gebruiken om dit idee te illustreren. De iris gegevensset heeft metingen van 150 bloemen van 3 soorten. Laten we eens kijken hoe deze dataset is gestructureerd:
> 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
Stel je nu voor dat je het gemiddelde van elk van deze variabelen wilt weten. Een manier om dit op te lossen kan zijn om een for lus te gebruiken, maar R-programmeurs geven er vaak de voorkeur aan om apply te gebruiken (om redenen waarom, zie Opmerkingen):
> apply(iris[1:4], 2, mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
- In de eerste parameter stellen we
irisin om alleen de eerste 4 kolommen op te nemen, omdatmeanalleen op numerieke gegevens werkt. - De tweede parameterwaarde van
2geeft aan dat we alleen aan de kolommen willen werken (het tweede subscript van de r × c array);1zou de rij betekenen.
Op dezelfde manier kunnen we meer betekenisvolle waarden berekenen:
# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)
Voorbehoud : R heeft een aantal ingebouwde functies die beter zijn voor het berekenen van kolom- en colMeans en betekent: colMeans en rowMeans .
Laten we nu een andere en meer betekenisvolle taak uitvoeren: laten we het gemiddelde alleen berekenen voor die waarden die groter zijn dan 0.5 . Daarvoor zullen we onze eigen mean functie creëren.
> 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
(Let op het verschil in het gemiddelde van Petal.Width )
Maar wat als we deze functie niet in de rest van onze code willen gebruiken? Vervolgens kunnen we een anonieme functie gebruiken en onze code als volgt schrijven:
apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })
Zoals we hebben gezien, kunnen we dus apply gebruiken om dezelfde bewerking op kolommen of rijen van een gegevensset uit te voeren met slechts één regel.
Voorbehoud : Omdat apply zeer verschillende soorten uitvoer retourneert, afhankelijk van de lengte van de resultaten van de opgegeven functie, is dit misschien niet de beste keuze in gevallen waarin u niet interactief werkt. Sommige van de andere *apply familiefuncties zijn iets voorspelbaarder (zie Opmerkingen).
Bulkbestand laden
voor een groot aantal bestanden die mogelijk moeten worden verwerkt in een soortgelijk proces en met goed gestructureerde bestandsnamen.
eerst moet een vector van de te openen bestandsnamen worden aangemaakt, hiervoor zijn meerdere opties:
De vector handmatig maken met
paste0()files <- paste0("file_", 1:100, ".rds")Het gebruik van
list.files()met een regex-zoekterm voor het bestandstype, vereist kennis van reguliere expressies ( regex ) als andere bestanden van hetzelfde type zich in de map bevinden.files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
waarbij X een vector is van een deel van de gebruikte bestandsnaamindeling.
lapply wordt elk Weaver element van een lijst.
readRDS is specifiek voor .rds bestanden en zal veranderen afhankelijk van de toepassing van het proces.
my_file_list <- lapply(files, readRDS)
Dit is niet noodzakelijkerwijs sneller dan een for-lus van het testen, maar staat toe dat alle bestanden een element van een lijst zijn zonder ze expliciet toe te wijzen.
Ten slotte moeten we vaak meerdere pakketten tegelijk laden. Deze truc kan het vrij eenvoudig doen door library() te passen op alle bibliotheken die we willen importeren:
lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)
Meerdere `data.frames` combineren (` lapply`, `mapply`)
In deze oefening zullen we vier bootstrap lineaire regressiemodellen genereren en de samenvattingen van deze modellen combineren in een enkel dataframe.
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)))
Op dit punt kunnen we twee benaderingen gebruiken om de namen in het data.frame in te voegen.
#* 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)
Als je een fan bent van pijpen in magrittr stijl, kun je de hele taak in een enkele keten uitvoeren (hoewel het misschien niet verstandig is om dit te doen als je een van de tussenliggende objecten nodig hebt, zoals de modelobjecten zelf):
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", .)
Ingebouwde functies gebruiken
Ingebouwde functies: lapply (), sapply () en mapply ()
R wordt geleverd met ingebouwde functionaliteiten, waarvan misschien wel de meest bekende de toepassingsfamilie is. Hier is een beschrijving van enkele van de meestgebruikte functies voor het toepassen:
-
lapply()= neemt een lijst als argument en past de opgegeven functie toe op de lijst. -
sapply()= hetzelfde alslapply()maar probeert de uitvoer naar een vector of een matrix te vereenvoudigen.-
vapply()= een variant vansapply()waarin het type van het uitvoerobject moet worden opgegeven.
-
-
mapply()= likelapply()maar kan meerdere vectoren doorgeven als invoer voor de opgegeven functie. Kan worden vereenvoudigd zoalssapply().-
Map()is een alias voormapply()metSIMPLIFY = FALSE.
-
lapply ()
lapply() kan worden gebruikt met twee verschillende iteraties:
-
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() zal proberen de uitvoer om te zetten in een vector of een matrix.
# 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() werkt net als lapply() behalve dat het meerdere vectoren als invoer kan gebruiken (vandaar de m voor multivariate).
mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply
Door de gebruiker gedefinieerde functies gebruiken
Door de gebruiker gedefinieerde functies
Gebruikers kunnen hun eigen functionaliteiten maken in verschillende mate van complexiteit. De volgende voorbeelden zijn afkomstig van Functionals van 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
}
In het eerste geval accepteert randomise een enkel argument f en noemt het een steekproef van uniforme willekeurige variabelen. Om gelijkwaardigheid aan te tonen, noemen we set.seed hieronder:
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
Het tweede voorbeeld is een base::lapply van base::lapply , die functionals gebruikt om een bewerking ( f ) toe te passen op elk element in een lijst ( x ). Met de parameter ... kan de gebruiker aanvullende argumenten doorgeven aan f , zoals de optie na.rm in de mean functie:
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