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:

  1. Vereisen minder code om te schrijven.
  2. Heeft geen iteratieteller.
  3. 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, omdat mean 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 als lapply() maar probeert de uitvoer naar een vector of een matrix te vereenvoudigen.
    • vapply() = een variant van sapply() waarin het type van het uitvoerobject moet worden opgegeven.
  • mapply() = like lapply() maar kan meerdere vectoren doorgeven als invoer voor de opgegeven functie. Kan worden vereenvoudigd zoals sapply() .
    • Map() is een alias voor mapply() met SIMPLIFY = 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


Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow