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
iris
in om alleen de eerste 4 kolommen op te nemen, omdatmean
alleen op numerieke gegevens werkt. - De tweede parameterwaarde van
2
geeft aan dat we alleen aan de kolommen willen werken (het tweede subscript van de r × c array);1
zou 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