Ce este R Vector, Victor? | R-BLOGGERS

URMĂREȘTE-NE
16,065FaniÎmi place
1,142CititoriConectați-vă

(Acest articol a fost publicat pentru prima dată pe R pe biofunctorși a contribuit cu drag la R-Bloggers). (Puteți raporta problema despre conținutul de pe această pagină aici)


Doriți să vă împărtășiți conținutul pe R-Bloggers? Faceți clic aici dacă aveți un blog sau aici dacă nu.

Gătit cu FlatMap

În episodul din această săptămână din seria „Hidden Monads in R”, voi explora aspectul vectorial al structurilor de date R și voi vedea cum operațiunea FlatMap poate fi destul de utilă.

Flatmap? Nu sunt toate hărțile plate?

Organizația Premiilor Nobel oferă unei API informații despre premii și laureați. Putem prelua un fișier JSON, ceea ce am făcut. Am citit fișierul și examinez una dintre intrările de mai jos.

# Source: http://api.nobelprize.org/v1/prize.json
prizes <- jsonlite::fromJSON("./prize.json", simplifyDataFrame = FALSE)(("prizes"))

str(prizes((11)))

## List of 3
##  $ year     : chr "2023"
##  $ category : chr "physics"
##  $ laureates:List of 3
##   ..$ :List of 5
##   .. ..$ id        : chr "1026"
##   .. ..$ firstname : chr "Pierre"
##   .. ..$ surname   : chr "Agostini"
##   .. ..$ motivation: chr ""for experimental methods that generate attosecond pulses of light for the study of electron dynamics in matter""
##   .. ..$ share     : chr "3"
##   ..$ :List of 5
##   .. ..$ id        : chr "1027"
##   .. ..$ firstname : chr "Ferenc"
##   .. ..$ surname   : chr "Krausz"
##   .. ..$ motivation: chr ""for experimental methods that generate attosecond pulses of light for the study of electron dynamics in matter""
##   .. ..$ share     : chr "3"
##   ..$ :List of 5
##   .. ..$ id        : chr "1028"
##   .. ..$ firstname : chr "Anne"
##   .. ..$ surname   : chr "L’Huillier"
##   .. ..$ motivation: chr ""for experimental methods that generate attosecond pulses of light for the study of electron dynamics in matter""
##   .. ..$ share     : chr "3"

Să zicem că vreau un vector de personaje care conține numele complete ale laureaților Nobel în medicină din 2020. În primul rând, pot contura o funcție care primește un astfel de vector dintr -o singură intrare (știu, aceasta este fizică).

who_got_it <- function(prize) {
    laureates <- vapply(
        X = prize(("laureates")),
        FUN = (l) c(l(("surname")) %||% "", l(("firstname")) %||% ""),
        FUN.VALUE = c("Doe", "John")
    )
    trimws(paste(laureates(2,), laureates(1,)))
}

who_got_it(prizes((11)))

## (1) "Pierre Agostini" "Ferenc Krausz"   "Anne L’Huillier"

Pentru a -mi atinge obiectivul, trebuie doar să filtrez lista în consecință și lapply Funcția de la intrările potrivite.

(medicine_since_2020 <- Filter(
    f = (p) p(("category")) == "medicine" & as.numeric(p(("year"))) >= 2020,
    x = prizes
    ) |>
    lapply(who_got_it)
)

## ((1))
## (1) "Victor Ambros" "Gary Ruvkun"  
## 
## ((2))
## (1) "Katalin Karikó" "Drew Weissman" 
## 
## ((3))
## (1) "Svante Pääbo"
## 
## ((4))
## (1) "David Julius"      "Ardem Patapoutian"
## 
## ((5))
## (1) "Harvey Alter"     "Michael Houghton" "Charles Rice"

Pur! Dar le vreau într -un singur vector. Deci am nevoie de un Unist Pas la sfârșit.

unlist(medicine_since_2020)

##  (1) "Victor Ambros"     "Gary Ruvkun"       "Katalin Karikó"   
##  (4) "Drew Weissman"     "Svante Pääbo"      "David Julius"     
##  (7) "Ardem Patapoutian" "Harvey Alter"      "Michael Houghton" 
## (10) "Charles Rice"

Da, este atât de simplu. Acesta este un Flatmap proces pentru vectori și este o compoziție a unui hartă și a aplatiza Pas (Lapply și Unlist în acest caz). Aproape că pare o prostie să scrii o funcție FlatMap, până la urmă nu este atât de dificil să te licitați și să ne desfășurați secvențial. Dar este folosit des, așa că economisește timp și reduce greșelile. În acest caz – pentru a fi corect – ar fi trebuit să folosesc
unlist(recursive = FALSE)altfel, aplatizează listele cuibărite și asta ar fi greșit.

Experimentele de laborator sunt adesea efectuate în plăci de plastic cu 96 de godeuri, cu 8 rânduri (etichetate AH) și 12 coloane (etichetate 1-12). Fiecare microwell este un micro-experiment separat (etichetat A1-H12). Să generăm etichete bine pentru un astfel de set de date!

rows <- LETTERS(1:8)
columns <- 1:12 |> sprintf(fmt = "%02i")

Deci, tot ce trebuie să facem este să combinăm un vector de valori cu altul, folosind la îndemână paste0() Funcție, nu? Greşit.

paste0(rows, columns) |> noquote()

##  (1) A01 B02 C03 D04 E05 F06 G07 H08 A09 B10 C11 D12

Avem doar 12 valori în loc de 96, iar vectorul mai scurt (litere) este reciclat după cum este necesar. De multe ori este ceea ce vrei, așa că s -a făcut așa dintr -un motiv întemeiat. Dar în acest caz, am prefera să avem o combinație de fiecare dintre ele.

Este posibil ca unii cititori să fi început deja să dea seama de cuibărit pentru bucle (vă rog). Programatorii R mai experimentați ar fi probabil pentru expand.grid() sau
rep(rows, each = length(columns) pentru a se potrivi cu vectorii și apoi paste()
ei împreună. Dar R este un limbaj versatil și există multe căi către aceeași destinație. O funcţional Programatorul R ar putea lua doar flatmap În afara raftului, și iată cum.

Din motive pur didactice, să definim o funcție de pastă ne-vectorizată, numită paste01 . Este nevoie de o singură valoare și un vector de caracter și returnează un vector de caracter – combinația valorii cu fiecare membru al vectorului.

$$paste01 :: Str rightarrow (Str) rightarrow (Str)$$

paste01 <- (x, y) { stopifnot(length(x) == 1L); paste0(x, y)}
paste01(rows(1), columns)

##  (1) "A01" "A02" "A03" "A04" "A05" "A06" "A07" "A08" "A09" "A10" "A11" "A12"

Când cartizăm această funcție pe a noastră rows vector, noi aproape Obțineți ceea ce avem nevoie.

$$lapply(paste01) :: (Str) rightarrow (Str) rightarrow ((Str))$$

lapply(rows, paste01, columns) |> head(3L)

## ((1))
##  (1) "A01" "A02" "A03" "A04" "A05" "A06" "A07" "A08" "A09" "A10" "A11" "A12"
## 
## ((2))
##  (1) "B01" "B02" "B03" "B04" "B05" "B06" "B07" "B08" "B09" "B10" "B11" "B12"
## 
## ((3))
##  (1) "C01" "C02" "C03" "C04" "C05" "C06" "C07" "C08" "C09" "C10" "C11" "C12"

Este o listă de vectori, așa că trebuie să o aplatim. Yupp, este o foaie de apartament.

$$unlist(lapply(paste01)) :: (Str) rightarrow (Str) rightarrow (Str)$$

unlist(lapply(rows, paste01, columns)) |>
    noquote()

##  (1) A01 A02 A03 A04 A05 A06 A07 A08 A09 A10 A11 A12 B01 B02 B03 B04 B05 B06 B07
## (20) B08 B09 B10 B11 B12 C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12 D01 D02
## (39) D03 D04 D05 D06 D07 D08 D09 D10 D11 D12 E01 E02 E03 E04 E05 E06 E07 E08 E09
## (58) E10 E11 E12 F01 F02 F03 F04 F05 F06 F07 F08 F09 F10 F11 F12 G01 G02 G03 G04
## (77) G05 G06 G07 G08 G09 G10 G11 G12 H01 H02 H03 H04 H05 H06 H07 H08 H09 H10 H11
## (96) H12

Tlada!

Deci, poate fi definită o funcție FlatMap pentru vectori. Este nevoie de:

  1. un vector de valori
  2. o funcție care se transformă unul dintre aceștia într -un vector (potențial diferit)

Tipul de ieșire se potrivește cu cel de -al doilea tip de vector.

$$ FlatMap :: (a) dreapta (a dreapta (b)) dreapta (b) $$

flatmap <- function(X, FUN, ..., USE.NAMES = TRUE) {
    unlist(lapply(X, FUN, ...), recursive = FALSE, USE.NAMES = USE.NAMES)
}

Debrief

O astfel de funcție ar putea fi, de asemenea, definită ca un operator de infix și ar putea lua forma de %>>=%de exemplu. Dacă asta pare familiar, nu este o coincidență: FlatMap este Operația de legare pentru vectorul monad. Anterior, am presupus că încă un alt operator de infix nu este ceea ce R are nevoie de cel mai mult și am creat în schimb un înveliș funcțional.

La fel s -ar putea face aici! R are deja un înveliș foarte similar,
base::Vectorize()care are nevoie doar de o modificare minusculă, unlist()-Nuing rezultatele. Este atât de banal încât nici măcar nu o voi scrie aici.

Ceea ce mă emoționează mult mai mult este posibilitatea de a combina cele două idei: manipularea NA-S și FlatMapping într-un singur lega Funcția de înveliș, care ar permite cu adevărat concentrarea pe logică și a lăsat „expertul” înveliș să se ocupe de restul. Ca obișnuit, este nevoie de mai multă explorare.

Dominic Botezariu
Dominic Botezariuhttps://www.noobz.ro/
Creator de site și redactor-șef.

Cele mai noi știri

Pe același subiect

LĂSAȚI UN MESAJ

Vă rugăm să introduceți comentariul dvs.!
Introduceți aici numele dvs.