R Language
* tillämpa familj av funktioner (funktionaliteter)
Sök…
Anmärkningar
En funktion i *apply
familjen är en abstraktion av en for
loop. Jämfört med for
slingor *apply
följande fördelar:
- Kräv mindre kod för att skriva.
- Har inte en iterationsräknare.
- Använder inte tillfälliga variabler för att lagra mellanresultat.
Men for
slingor är mer generella och kan ge oss mer kontroll så att vi kan uppnå komplexa beräkningar som inte alltid är triviala att använda med *apply
funktioner.
Förhållandet mellan for
slingor och *apply
förklaras i dokumentationen for
slingor .
Medlemmar i *apply
familjen
Funktionen *apply
innehåller flera varianter av samma princip som skiljer sig främst på vilken typ av utgång de returnerar.
fungera | Inmatning | Produktion |
---|---|---|
apply | matrix , data.frame eller array | vektor eller matris (beroende på längden på varje returnerat element) |
sapply | vektor eller list | vektor eller matris (beroende på längden på varje returnerat element) |
lapply | vektor eller list | list |
vapply | vektor eller `lista | vektor eller matris (beroende på längden på varje återlämnat element) i den användardesignerade klassen |
mapply | flera vektorer, lists eller en kombination | list |
Se "Exempel" för att se hur var och en av dessa funktioner används.
Använd anonyma funktioner med tillämp
apply
används för att utvärdera en funktion (kanske en anonym) över marginalerna för en matris eller matris.
Låt oss använda iris
datasättet för att illustrera denna idé. iris
datasättet har mätningar av 150 blommor från 3 arter. Låt oss se hur detta datasats är strukturerat:
> 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
Föreställ dig nu att du vill veta medelvärdet för var och en av dessa variabler. Ett sätt att lösa detta kan vara att använda en for
loop, men R-programmerare föredrar ofta att använda apply
(av anledningar, se anmärkningar):
> apply(iris[1:4], 2, mean)
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.843333 3.057333 3.758000 1.199333
- I den första parametern lägger vi ned
iris
att de endast inkluderar de första fyra kolumnerna, eftersommean
bara fungerar på numeriska data. - Det andra parametervärdet på
2
indikerar att vi bara vill arbeta på kolumnerna (det andra underskriptet för rxc-arrayen);1
skulle ge raden medel.
På samma sätt kan vi beräkna mer meningsfulla värden:
# standard deviation
apply(iris[1:4], 2, sd)
# variance
apply(iris[1:4], 2, var)
Varning : R har några inbyggda funktioner som är bättre för att beräkna colMeans
och rowMeans
och medel: colMeans
and rowMeans
.
Nu, låt oss göra en annorlunda och mer meningsfull uppgift: låt oss beräkna medelvärdet endast för de värden som är större än 0.5
. För det skapar vi vår egen 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
(Observera skillnaden i medelvärdet för Petal.Width
)
Men vad händer om vi inte vill använda den här funktionen i resten av vår kod? Sedan kan vi använda en anonym funktion och skriva vår kod så här:
apply(iris[1:4], 2, function(x) { mean(x[x > 0.5]) })
Så, som vi har sett, kan vi använda apply
att utföra samma operation på kolumner eller rader i ett datasats med bara en rad.
Varning : Eftersom apply
returnerar mycket olika typer av output beroende på längden på resultaten för den angivna funktionen, är det kanske inte det bästa valet i de fall där du inte arbetar interaktivt. Några av de andra *apply
familjefunktionerna är lite mer förutsägbara (se Anmärkningar).
Laddning av massfil
för ett stort antal filer som kan behöva köras i en liknande process och med välstrukturerade filnamn.
Först måste en vektor av filnamnen som ska nås skapas, det finns flera alternativ för detta:
Skapa vektorn manuellt med
paste0()
files <- paste0("file_", 1:100, ".rds")
Att använda
list.files()
med en regex-sökterm för filtypen kräver kunskap om reguljära uttryck ( regex ) om andra filer av samma typ finns i katalogen.files <- list.files("./", pattern = "\\.rds$", full.names = TRUE)
där X
är en vektor av en del av filnamnsformatet som används.
lapply
kommer att mata ut varje svar som element i en lista.
readRDS
är specifikt för .rds
filer och kommer att ändras beroende på processens tillämpning.
my_file_list <- lapply(files, readRDS)
Detta är inte nödvändigtvis snabbare än en för-loop från testning men tillåter alla filer att vara ett element i en lista utan att tilldela dem uttryckligen.
Slutligen måste vi ofta ladda flera paket samtidigt. Detta trick kan göra det ganska enkelt genom att tillämpa library()
på alla bibliotek som vi vill importera:
lapply(c("jsonlite","stringr","igraph"),library,character.only=TRUE)
Kombinera flera `data.frames` (` lapply`, `mapply`)
I denna övning kommer vi att generera fyra linjära regressionsmodeller för bootstrap och kombinera sammanfattningarna av dessa modeller till en enda dataram.
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)))
På denna punkt kan vi ta två sätt att infoga namnen i 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)
Om du är en fan av magrittr
, kan du utföra hela uppgiften i en enda kedja (även om det kanske inte är klokt att göra det om du behöver någon av mellanobjekten, till exempel själva modellobjekten):
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", .)
Använda inbyggda funktionaliteter
Inbyggda funktioner: lapply (), sapply () och mapply ()
R levereras med inbyggda funktionaliteter, varav de kanske mest kända är tillämpningsfamiljen av funktioner. Här är en beskrivning av några av de vanligaste tillämpningsfunktionerna:
-
lapply()
= tar en lista som ett argument och tillämpar den angivna funktionen på listan. -
sapply()
= samma somlapply()
men försöker förenkla utgången till en vektor eller en matris.-
vapply()
= en variant avsapply()
där utgångsobjektets typ måste anges.
-
-
mapply()
= gillarlapply()
men kan överföra flera vektorer som inmatning till den angivna funktionen. Kan förenklas somsapply()
.-
Map()
är ett alias för attmapply()
medSIMPLIFY = FALSE
.
-
lapply ()
lapply()
kan användas med två olika iterationer:
-
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()
kommer att försöka lösa dess utgång till antingen en vektor eller en matris.
# 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()
fungerar ungefär som lapply()
förutom att det kan ta flera vektorer som input (därav m för multivariat).
mapply(sum, 1:5, 10:6, 3) # 3 will be "recycled" by mapply
Använda användardefinierade funktioner
Användardefinierade funktioner
Användare kan skapa sina egna funktioner i varierande grad av komplexitet. Följande exempel är från Functionals av 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
}
I det första fallet accepterar randomise
ett enda argument f
och kallar det för ett urval av enhetliga slumpmässiga variabler. För att visa likvärdighet kallar vi set.seed
nedan:
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
Det andra exemplet är en base::lapply
av base::lapply
, som använder funktionaliteter för att tillämpa en operation ( f
) på varje element i en lista ( x
). Den ...
parameter tillåter användaren att skicka ytterligare argument till f
, såsom na.rm
alternativet i mean
funktion:
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