# Structura de baza a unei functii
<- function(ARGUMENTE) {
NUME
ACTIUNI
return(REZULTAT)
}
Elemente de programare în R
Funcții
O funcție este un obiect în R
care primește câteva obiecte de intrare (care se numesc argumentele funcției) și întoarce un obiect de ieșire. Structura unei funcții va avea următoarele patru părți:
Nume: Care este numele funcției? Aveți grijă să nu folosiți nume ale funcțiilor deja existente în
R
!Argumente: Care sunt datele de intrare pentru funcție? Puteți specifica oricâte date de intrare doriți!
Corp sau acțiune: Ce vreți să facă această funcție? Să traseze un grafic? Să calculeze o statistică?
Rezultat: Ce vreți să vă întoarcă funcția? Un scalar? Un vector? Un data.frame?
Funcțiile în R
sunt obiecte de primă clasă (first class objects), ceea ce înseamnă că ele pot fi tratate ca orice alt obiect din R
. Este important de reținut că, în R
,
funcțiile pot fi date ca argumente pentru alte funcții (de exemplu familia de funcții
apply()
)funcțiile pot fi imbricate (nested), cu alte cuvinte puteți crea funcții în interiorul altor funcții
Mai jos avem un exemplu de funcție care nu are niciun argument și nu întoarce nicio valoare:
<- function() {
f ## Aceasta este o functie goala
}## Functiile au clasa lor speciala
class(f)
[1] "function"
f()
NULL
Următoarea funcție întoarce numărul de caractere al textului dat ca argument:
<- function(mesaj){
f <- nchar(mesaj)
chars
chars
}
<- f("curs de statistica si probabilitati")
mes mes
[1] 35
În funcția de mai sus nu am indicat nimic special pentru ca funcția să ne întoarcă numărul de caractere. În R
, rezultatul unei funcții este întotdeauna ultima expresie evaluată. De asemenea există funcția return()
care poate fi folosită pentru a întoarce o valoare explicită, dar de multe ori această funcție este omisă.
Dacă utilizatorul nu specifică valoarea argumentului mesaj
în funcția de mai sus atunci R
-ul întoarce o eroare:
f()
Error in f(): argument "mesaj" is missing, with no default
Acest comportament al funcției poate fi modificat prin definirea unei valori implicite (de default). Orice argument al funcției poate avea o valoare de default.
<- function(mesaj = "Valoare de default"){
f <- nchar(mesaj)
chars
chars
}
# Folosim valoarea implicita
f()
[1] 18
# Folosim o valoare specificata
f("curs de statistica si probabilitati")
[1] 35
Argumentele funcțiilor în R
pot fi potrivite după poziția lor sau după numele lor. Potrivirea după poziție înseamnă că R
atribuie prima valoare primului argument, a doua valoare celui de-al doilea argument, etc. De exemplu atunci când folosim funcția rnorm()
,
str(rnorm)
function (n, mean = 0, sd = 1)
set.seed(1234) # pentru repetabilitate
<- rnorm(10, 3, 1)
mydata mydata
[1] 1.7929343 3.2774292 4.0844412 0.6543023 3.4291247 3.5060559 2.4252600
[8] 2.4533681 2.4355480 2.1099622
valoarea 10 este atribuită argumentului n
, valoarea 3 argumentului mean
iar valoarea 1 argumentului sd
, toate prin potrivire după poziție.
Atunci când specificăm argumentele funcției după nume, ordinea acestora nu contează. De exemplu
set.seed(1234)
rnorm(mean = 3, n = 10, sd = 1)
[1] 1.7929343 3.2774292 4.0844412 0.6543023 3.4291247 3.5060559 2.4252600
[8] 2.4533681 2.4355480 2.1099622
întoarce același rezultat cu cel obținut mai sus.
De cele mai multe ori, argumentele cu nume sunt folositoare atunci când funcția are un șir lung de argumente și ne dorim să folosim valorile implicite pentru majoritatea dintre ele. De asemenea aceste argumente pot fi folositoare și atunci când știm numele argumentului dar nu și poziția în lista de argumente. Un exemplu de astfel de funcție este funcția plot()
, care are multe argumente folosite în special pentru customizare:
args(plot.default)
function (x, y = NULL, type = "p", xlim = NULL, ylim = NULL,
log = "", main = NULL, sub = NULL, xlab = NULL, ylab = NULL,
ann = par("ann"), axes = TRUE, frame.plot = axes, panel.first = NULL,
panel.last = NULL, asp = NA, xgap.axis = NA, ygap.axis = NA,
...)
NULL
În R
există un argument special notat ...
, care indică un număr arbitrar de argumente care sunt atribuite altor funcții din corpul funcției. Acest argument este folosit în special atunci când vrem să extindem o altă funcție și nu vrem să copiem întreaga listă de argumente a acesteia. De exemplu, putem crea o funcție de plotare în care specificăm tipul în prealabil
<- function(x, y, type = "l", ...) {
myplot plot(x, y, type = type, ...) ## Atribuie '...' functiei 'plot'
}
Argumentul ...
poate fi folosit (și este necesar) și atunci când numărul de argumente pe care îl ia funcția nu este cunoscut în prealabil. De exemplu să considerăm funcțiile paste()
și cat()
args(paste)
function (..., sep = " ", collapse = NULL, recycle0 = FALSE)
NULL
args(cat)
function (..., file = "", sep = " ", fill = FALSE, labels = NULL,
append = FALSE)
NULL
Deoarece ambele funcții afișează text în consolă combinând mai mulți vectori de caractere împreună, este imposibil ca acestea să cunoască în prealabil câți vectori de caractere vor fi dați ca date de intrare de către utilizator, deci primul argument pentru fiecare funcție este ...
.
Este important de menționat că toate argumentele care apar după argumentul ...
trebuie explicitate după nume.
paste("Curs", "Probabilitati si Statistica", sep = ":")
[1] "Curs:Probabilitati si Statistica"
Exerciții
Exercițiul 1 Să presupunem că Ionel este convins că poate prezice cât aur va găsi pe o insulă folosind următoarea ecuație: \(ab - 324c + \log(a)\), unde \(a\) este aria insulei (în \(m^2\)), \(b\) este numărul de copaci de pe insulă iar \(c\) reprezintă cât de obosit este pe o scală de la 1 la 10. Creați o funcție numită Gasit_Aur
care primește ca argumente \(a\), \(b\) și \(c\) și întoarce valoare prezisă.
Un exemplu ar fi
Gasit_Aur(a = 1000, b = 30, c = 7)
[1] 27738.91
Exercițiul 2 Precizați care este rezultatul următoarelor funcții:
<- function(d, n, max){
f1 <- seq(from=1, by=d, length.out=n)
nums return(nums[nums <= max])
}
f1(4,5,10)
<- function(n,a,b,c,d){
f2 <- (1:n)*5
x <- x[a:b]
x print(x)
<- x[-c]
x print(x)
<- x[x<d]
x return(x)
}
f2(10,3,7,2,32)
<- function(a,b) {
f3 <- a[a<b]
a return(a)
}
f3(3:7,(1:5)^2)
Exercițiul 3 Creați următoarele funcții:
Funcțiile \(f_1\) și \(f_2\) care primesc ca argument vectorul \(x = \left(x_1, x_2, \ldots, x_n\right)\) și returnează vectorul \(\left(x_1, x_2^2, \ldots, x_n^n\right)\) și respectiv \(\left(x_1, \frac{x_2^2}{2}, \ldots, \frac{x_n^n}{n}\right)\)
Funcția \(f_3\) care primește două argumente, un scalar \(x\) și un scalar \(n\) și returnează
\[ 1+\frac{x}{1}+\frac{x^2}{2}+\frac{x^3}{3}+\cdots+\frac{x^n}{n} \]
- Funcția \(f_4\) care primește ca argument vectorul \(x = \left(x_1, x_2, \ldots, x_n\right)\) și returnează vectorul sumelor mobile
\[ \frac{x_1+x_2+x_3}{3}, \quad \frac{x_2+x_3+x_4}{3}, \quad \ldots, \quad \frac{x_{n-2}+x_{n-1}+x_n}{3} \]
Exercițiul 4 Presupunem că dintr-o urnă ce conține \(r\) bile roșii și \(a\) bile albastre extragem \(n\) bile fără întoarcere. Scrieți o funcție care calculează probabilitatea ca exact \(k\) dintre bilele extrase să fie de culoare roșie (Indicație: Ce face funcția choose
?).
Exercițiul 5 Scrieți o funcție care primește ca argumente două numere \(n\) și \(k\) și returnează matricea pătratică de dimensiune \(n\times n\):
\[ \left[\begin{array}{ccccccc} k & 1 & 0 & 0 & \cdots & 0 & 0 \\ 1 & k & 1 & 0 & \cdots & 0 & 0 \\ 0 & 1 & k & 1 & \cdots & 0 & 0 \\ 0 & 0 & 1 & k & \cdots & 0 & 0 \\ \cdots & \cdots & \cdots & \cdots & \cdots & \cdots & \cdots \\ 0 & 0 & 0 & 0 & \cdots & k & 1 \\ 0 & 0 & 0 & 0 & \cdots & 1 & k \end{array}\right] \]
Exercițiul 6 Fie \(T_n\) suma numerelor de la \(1\) la \(n\). Ne propunem să afișăm toate numerele de la \(1\) la \(T_{1500000}\) care sunt pătrate perfecte. Pentru aceasta se cere să construiți:
Funcția
T(n)
definită de șirul \(T_n\)Funcția
este.patrat.perfect(x)
care verifică dacăx
este pătrat perfectFuncția
afisare_patrate_perfecte(n)
care afișează numerele \(T_k\), \(1\leq k\leq n\), care sunt pătrate perfecte.
Structuri de control (if-else
, switch
, etc.)
Structurile de control, în R
, permit structurarea logică și controlul fluxului de execuție al unei serii de comenzi. Cele mai folosite structuri de control sunt:
if
șielse
: testează o condiție și acționează asupra eiswitch
: compară mai multe opțiuni și execută opțiunea pentru care avem potrivire
Structura if-else
Structura if-else
este una dintre cele mai folosite structuri de control în R permițând testarea unei condiții și acționând în funcție de valoarea de adevăr a acesteia.
Forma de bază a acestei structuri este
if(<conditie>) {
# executa instructiuni atunci cand conditia este adevarata
} else {
# executa instructiuni atunci cand conditia este falsa
}
dar putem să avem și o serie de teste, de tipul
if(<conditie1>) {
# executa instructiuni
else if(<conditie2>) {
} # executa instructiuni
else {
} # executa instructiuni
}
Avem următorul exemplu
# Generam un numar uniform in [0,10]
<- runif(1, 0, 10)
x
if(x > 3) {
<- 10
y else {
} <- 0
y }
Comanda switch
Comanda switch
este folosită cu precădere atunci când avem mai multe alternative dintre care vrem să alegem. Structura generală a aceste comenzi este
switch (Expresie, "Optiune 1", "Optiune 2", "Optiune 3", ....., "Optiune N")
sau într-o manieră extinsă
switch (Expresie,
"Optiune 1" = Executa aceste expresii atunci cand expresia se
1,
potriveste cu Optiunea "Optiune 2" = Executa aceste expresii atunci cand expresia se
2,
potriveste cu Optiunea "Optiune 3" = Atunci cand expresia se potriveste cu Optiunea 3,
executa aceste comenzi,
...."Optiune N" = Atunci cand expresia se potriveste cu Optiunea N,
executa aceste comenzi )
Considerăm următorul exemplu
<- 30
number1 <- 20
number2 # operator <- readline(prompt="Insereaza OPERATORUL ARITMETIC: ")
= "*"
operator
switch(operator,
"+" = print(paste("Suma celor doua numere este: ",
+ number2)),
number1 "-" = print(paste("Diferenta celor doua numere este: ",
- number2)),
number1 "*" = print(paste("Inmultirea celor doua numere este: ",
* number2)),
number1 "^" = print(paste("Ridicarea la putere a celor doua numere este: ",
^ number2)),
number1 "/" = print(paste("Impartirea celor doua numere este: ",
/ number2)),
number1 "%/%" = print(paste("Catul impartirii celor doua numere este: ",
%/% number2)),
number1 "%%" = print(paste("Restul impartirii celor doua numere este: ",
%% number2))
number1 )
[1] "Inmultirea celor doua numere este: 600"
Exerciții
Exercițiul 7 Scrieți o funcție f
care implementează funcția
\[ f(n)= \begin{cases}7 n+3 & \text { dacă } n \text { este multiplu de } 3 \\ \frac{7 n+2}{3} & \text { dacă } n \text { are restul } 1 \text { la împărțirea cu } 3 \\ \frac{n-2}{3} & \text { dacă } n \text { are restul } 2 \text { la împărțirea cu } 3 \end{cases} \]
Exercițiul 8 Un număr palindromic (pe scurt palindrom) este un număr care se citește la fel atunci când este scris de la stânga la dreapta și de la dreapta la stânga. De exemplu următoarele numere:
\[ 0,1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99,101,111,121, \ldots \]
sunt palindroame. Ne propunem să determinăm proporția de numere palindromice între \(1\) și \(100000\):
- Creați o funcție
cifre(n)
care întoarce vectorul cifrelor numărului \(n\), e.g.
cifre(1234)
[1] 1 2 3 4
- Creați o funcție
rasturnat(v)
care să inverseze elementele vectoruluiv
, e.g.
rasturnat(c(1, 3, 5, 4, 2))
[1] 2 4 5 3 1
- Creați funcția
este.palindrom(n)
care returnează valoareaTRUE
dacă \(n\) este palindrom șiFALSE
altfel, e.g.
este.palindrom(123321)
[1] TRUE
este.palindrom(1234)
[1] FALSE
Putem răspunde acum la întrebarea inițială?
Exercițiul 9 Algoritmul lui Zeller este folosit pentru a returna ziua din săptămână corespunzătoare datei introduse. Formula de calcul este
\[ f = \left([2.6 m-0.2]+k+y+\left[\frac{y}{4}\right]+\left[\frac{c}{4}\right]-2 c\right) \bmod 7 \]
unde - \(f\) este ziua cu \(1\) pentru Duminică, \(2\) pentru Luni, etc. - \(k\) este ziua din lună - \(y\) este anul (scris cu două cifre, e.g. \(63\)) - \(c\) este secolul (scris cu două cifre, e.g \(19\)) - \(m\) este luna (unde Ianuarie este luna 11 a anului trecut, Februarie luna 12 a anului trecut iar Martie este luna 1)
Astfel, data de 21/07/1963 are \(m=5\), \(k=21\), \(c=19\), \(y=63\) iar data de 21/2/1963 are \(m=12\), \(k=21\), \(c=19\), \(y=62\). Scrieți funcția zeller(ziua, luna, anul)
care să implementeze algoritmul lui Zeller.
Structuri repetitive (for
, while
, etc.)
Structurile repetitive din R
permit controlul fluxului de execuție al unei serii de comenzi, iar cele mai întâlnite sunt:
for
: execută o acțiune repetitivă de un număr fix de oriwhile
: execută o acțiune repetitivă cât timp o condiție este adevăratărepeat
: execută o acțiune repetitivă de o infinitate de ori (trebuie să folosimbreak
pentru a ieși din ea)break
: întrerupe execuția unei acțiuni repetitivenext
: sare peste un pas în executarea unei acțiuni repetitive
Bucle for
În R
, buclele for
iau o variabilă care se iterează și îi atribuie valori succesive dintr-un șir sau un vector. Buclele for
au următoarea structură de bază
for (<i> in <vector>) {
# executa instructiuni
}
de exemplu
for(i in 1:10) {
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
Următoarele trei bucle prezintă același comportament
<- c("a", "b", "c", "d")
x
for(i in 1:4) {
# Afiseaza fiecare elemnt din 'x'
print(x[i])
}
[1] "a"
[1] "b"
[1] "c"
[1] "d"
Funcția seq_along()
este des întâlnită atunci când folosim bucle for
deoarece crează un șir întreg folosind lungimea obiectului (în acest caz al lui x
)
# Genereaza un sir folosind lungimea lui 'x'
for(i in seq_along(x)) {
print(x[i])
}
[1] "a"
[1] "b"
[1] "c"
[1] "d"
De asemenea putem folosi chiar pe x
ca vector de indexare
for(letter in x) {
print(letter)
}
[1] "a"
[1] "b"
[1] "c"
[1] "d"
Atunci când folosim comenzile din buclele for
pe o singură linie nu este necesară folosirea parantezelor {}
for(i in 1:4) print(x[i])
[1] "a"
[1] "b"
[1] "c"
[1] "d"
Putem folosi buclele for
și imbricat (nested)
<- matrix(1:6, 2, 3)
x
for(i in seq_len(nrow(x))) {
for(j in seq_len(ncol(x))) {
print(x[i, j])
} }
Bucle de tip while
Acțiunile repetitive de tip while
încep prin testarea unei condiții și în cazul în care aceasta este adevărată atunci se execută corpul comenzii. Odată ce corpul buclei este executat, condiția este testată din nou până când devine falsă (se poate ca bucla while
să rezulte într-o repetiție infinită !). Structura generală a acestei bucle este
while(<conditie>) {
# executa instructiuni
}
Considerăm următorul exemplu
<- 0
count while(count < 4) {
print(count)
<- count + 1
count }
[1] 0
[1] 1
[1] 2
[1] 3
Uneori putem testa mai multe condiții (acestea sunt întotdeauna evaluate de la stânga la dreapta)
<- 5
z set.seed(123)
while(z >= 3 && z <= 10) {
<- rbinom(1, 1, 0.5) # arunc cu banul
ban
if(ban == 1) { # random walk
<- z + 1
z else {
} <- z - 1
z
}
}print(z)
[1] 11
Bucle de tip repeat
Acest tip de acțiuni repetitive nu sunt foarte des întâlnite, cel puțin în statistică sau analiză de date. O situație în care ar putea să apară este atunci când avem un algoritm iterativ în care căutăm o soluție și nu vrem să oprim algoritmul până când soluția nu este suficient de bună.
<- 1
x0 <- 1e-8
tol
repeat {
<- o_functie_definita()
x1
if(abs(x1 - x0) < tol) { # este suficient de buna solutia
break
else {
} <- x1
x0
} }
Comezile break
și next
Comanda next
este folosită pentru a sării un pas într-o buclă
for(i in 1:10) {
if(i <= 5) {
# sari peste primele 5 iteratii
next
}print(i)
}
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
Comanda break
este folosită pentru a părăsi o buclă imediat
for(i in 1:10) {
print(i)
if(i > 5) {
# opreste dupa 5 iteratii
break
} }
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
Calculul radicalului unui număr cu ajutorul buclei de tip repeat
și a comenzii break
se scrie
# folosind repeat
<- 12223
a
<- a/2
x repeat{
<- (x + a/x)/2
x if (abs(x^2 - a) < 1e-10) break
}
Exerciții
Exercițiul 10 Care este rezultatul următoarelor funcții:
<- function(n) {
f1 <- c(0,1,2)
x while (length(x) < n) {
<- c(x,3)
x
}return(x)
}
f1(6)
<- function(n) {
f2 <- 30
x while (x > 0) {
<- x-n
x
}return(x)
}
f2(7)
<- function(n){
f3 <- 1:(2*n)
x while(x[1] < n){
<- x[-1]
x
}return(x)
}
f3(5)
Exercițiul 11 Reamintiți-vă ce fac funcțiile head
și tail
. Scrieți o funcție middle
care să primească două argumente, x
(un vector, matrice sau data.frame) și n
un scalar, și care să returneze n
valori (rânduri) în jurul mijlocului lui x
.
Exercițiul 12 Creați funcția numere_pare(v)
care returnează numărul de numere pare din vectorul v
. Puteți crea această funcție și fără să folosiți bucle for
?
Exercițiul 13 Construiți următoarele matrice de dimensiune \(10 \times 10\): \(M_{i,j} = \frac{1}{\sqrt{|i-j|+1}}\) și \(N_{i,j} = \frac{i}{j^2}\). Puteți construi matricea \(M\) și matricea \(N\) fără a folosi bucle for
? (Hint: ce face comanda outer
?)
Exercițiul 14 Scrieți un program, folosind bucle de tip while
, care să permită calcularea radicalului numărului \(a\in\mathbb{N}\) plecând de la relația de recurență:
\[ 2x_{n+1} = x_n + \frac{a}{x_n},\quad x_1 = \frac{a}{2} \]
Exercițiul 15 Ne propunem să rezolvăm relația de recurență \(x_n=r x_{n-1}\left(1-x_{n-1}\right)\), cu valoarea inițială \(x_1\).
Scrieți o funcție
f_sir(x1, r, n)
care returnează vectorul \(\left(x_1, \ldots, x_n\right)\), a primilor \(n\) termeni ai șirului.Afișați grafic termenii șirului (a se vedea secțiunea Elemente de grafică în
R
) folosind comandaplot
.Scrieți o nouă funcție care determină numărul de iterații (termeni) pentru care \(\left|x_n-x_{n-1}\right|<tol\). Această funcția va avea trei argumente:
x1
,r
șitol
.
Exercițiul 16 Șirul lui Fibonacci este definit prin următoarea relație de recurență de ordin \(2\):
\[ F_{n+1}=F_n+F_{n-1} ; \quad n=2,3,4,5, \ldots, \]
unde \(F_1=F_2=1\). Scrieți o funcție în R
care întoarce primii \(n\) termeni ai șirului.
Exercițiul 17 Dat fiind eșantionul \(x_1, \ldots, x_n\), coeficientul de autocorelare de rang \(k\) este definit prin
\[ r_k=\frac{\sum_{i=k+1}^n\left(x_i-\bar{x}\right)\left(x_{i-k}-\bar{x}\right)}{\sum_{i=1}^n\left(x_i-\bar{x}\right)^2} \]
Scrieți o funcție care să primească două argumente, vectorul x
și rangul k
(cu \(1\leq k\leq n-1\)), și să returneze vectorul \(\left(r_0=1, r_1, \ldots, r_k\right)\).
Familia de funcții apply
Pe lângă buclele for
și while
, în R
există și un set de funcții care permit scrierea și rularea într-o manieră mai compactă a codului dar și aplicarea de funcții unor grupuri de date.
lapply()
: Evaluează o funcție pentru fiecare element al unei listesapply()
: La fel calapply
numai că încearcă să simplifice rezultatulapply()
: Aplică o funcție după fiecare dimensiune a unuiarray
tapply()
: Aplică o funcție pe submulțimi ale unui vectormapply()
: Varianta multivariată a funcțieilapply
split
: Împarte un vector în grupuri definite de o variabilă de tip factor.
Funcția lapply()
Funcția lapply()
efectuează următoarele operații:
- buclează după o listă, iterând după fiecare element din acea listă
- aplică o funcție fiecărui element al listei (o funcție pe care o specificăm)
- întoarce ca rezultat tot o listă (prefixul
l
vine de la listă).
Această funcție primește următoarele trei argument: (1) o listă X
; (2) o funcție FUN
; (3) alte argumente via ...
. Dacă X
nu este o listă atunci aceasta va fi transformată într-una folosind comanda as.list()
.
Considerăm următorul exemplu în care vrem să aplicăm funcția mean()
tuturor elementelor unei liste
set.seed(222)
<- list(a = 1:5, b = rnorm(10), c = rnorm(20, 1), d = rnorm(100, 5))
x lapply(x, mean)
$a
[1] 3
$b
[1] 0.1996044
$c
[1] 0.7881026
$d
[1] 5.064188
Putem să folosim funcția lapply()
pentru a evalua o funcție în moduri repetate. Mai jos avem un exemplu în care folosim funcția runif()
(permite generarea observațiilor uniform repartizate) de patru ori, de fiecare dată generăm un număr diferit de valori aleatoare. Mai mult, argumentele \(min=0\) și \(max=3\) sunt atribuite, prin intermediul argumentului ...
, funcției runif
.
<- 1:4
x lapply(x, runif, min = 0, max = 3)
[[1]]
[1] 0.03443616
[[2]]
[1] 1.267361 1.365441
[[3]]
[1] 1.8084700 2.1902665 0.4139585
[[4]]
[1] 1.5924650 0.7355067 2.1483841 1.6082945
Funcția sapply()
Funcția sapply()
are un comportament similar cu lapply()
prin faptul că funcția sapply()
apelează intern lapply()
pentru valorile de input, după care evaluează:
dacă rezultatul este o listă în care fiecare element este de lungime 1, atunci întoarce un vector
dacă rezultatul este o listă în care fiecare element este un vector de aceeași lungime (>1), se întoarce o matrice
în caz contrar se întoarce o listă.
Considerăm exemplul de mai sus
set.seed(222)
<- list(a = 1:4, b = rnorm(10), c = rnorm(20, 1), d = rnorm(100, 5))
x sapply(x, mean)
a b c d
2.5000000 0.1996044 0.7881026 5.0641876
Funcția split()
Funcția split()
primește ca argument un vector sau o listă (sau un data.frame) și împarte datele în grupuri determinate de o variabilă de tip factor (sau o listă de factor).
Argumentele aceste funcții sunt
str(split)
function (x, f, drop = FALSE, ...)
unde
x
este un vector, o listă sau un data.framef
este un factor sau o listă de factori
Considerăm următorul exemplu în care generăm un vector de date și îl împărțim după o variabilă de tip factor creată cu ajutorul funcției gl()
(generate levels, după cum am văzut în secțiunea Factori).
<- c(rnorm(10), runif(10), rnorm(10, 1))
x <- gl(3, 10)
f split(x, f)
$`1`
[1] -2.27414224 -0.11266780 0.61308167 0.07733545 0.57137727 0.11672493
[7] -0.95685256 -1.90008460 -1.48972089 0.55925676
$`2`
[1] 0.91159086 0.03291829 0.78368939 0.11852882 0.64443831 0.78790988
[7] 0.82451477 0.05642366 0.65075027 0.95426854
$`3`
[1] 2.6666242 2.6634334 1.8106280 -0.7837308 1.6575684 0.1546575
[7] 0.4930056 -0.9031544 2.4042311 1.4106863
Putem folosi funcția split
și în conjuncție cu funcția lapply
(atunci când vrem să aplicăm o funcție FUN
pe grupuri de date).
lapply(split(x, f), mean)
$`1`
[1] -0.4795692
$`2`
[1] 0.5765033
$`3`
[1] 1.157395
Funcția tapply()
Funcția tapply()
este folosită pentru aplicarea unei funcții FUN
pe submulțimile unui vector și poate fi văzută ca o combinație între split()
și sapply()
, dar doar pentru vectori.
str(tapply)
function (X, INDEX, FUN = NULL, ..., default = NA, simplify = TRUE)
Argumentele acestei funcții sunt date de următorul tabel:
tapply()
.
Argument | Descriere |
---|---|
X |
un vector |
INDEX |
este o variabilă de tip factor sau o listă de factori |
FUN |
o funcție ce urmează să fie aplicată |
... |
argumente ce vor fi atribuite funcției FUN |
simplify |
dacă vrem să simplificăm rezultatul |
Următorul exemplu calculează media după fiecare grupă determinată de o variabilă de tip factor a unui vector numeric.
<- c(rnorm(10), runif(10), rnorm(10, 1))
x <- gl(3, 10)
f f
[1] 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3
Levels: 1 2 3
tapply(x, f, mean)
1 2 3
-0.0007774025 0.3736457792 0.5789436983
Putem să aplicăm și funcții care întorc mai mult de un rezultat. În această situație rezultatul nu poate fi simplificat:
tapply(x, f, range)
$`1`
[1] -2.1904113 0.9249901
$`2`
[1] 0.004445296 0.998309704
$`3`
[1] -0.3379675 1.9327099
Funcția apply()
Funcția apply()
este folosită cu precădere pentru a aplica o funcție liniilor și coloanelor unei matrice (care este un array
bidimensional). Cu toate acestea poate fi folosită pe tablouri multidimensionale (array
) în general. Folosirea funcției apply()
nu este mai rapidă decât scrierea unei bucle for
, dar este mai compactă.
str(apply)
function (X, MARGIN, FUN, ..., simplify = TRUE)
Argumentele funcției apply()
sunt
X
un tablou multidimensionalMARGIN
este un vector numeric care indică dimensiunea sau dimensiunile după care se va aplica funcțiaFUN
este o funcție ce urmează să fie aplicată...
alte argumente pentru funcțiaFUN
Considerăm următorul exemplu în care calculăm media pe coloane într-o matrice
<- matrix(rnorm(200), 20, 10)
x apply(x, 2, mean) # media fiecarei coloane
[1] 3.745002e-02 1.857656e-01 -2.413659e-01 -2.093141e-01 -2.562272e-01
[6] 8.986712e-05 7.444137e-02 -7.460941e-03 6.275282e-02 9.801550e-02
precum și media după fiecare linie
apply(x, 1, sum) # media fiecarei linii
[1] 2.76179139 2.53107681 0.87923177 1.80480589 0.98225832 -3.06148753
[7] -1.40358820 -0.65969812 -1.63717046 -0.29330726 -2.41486442 -3.15698523
[13] 2.27126822 -3.88290287 -3.15595194 5.41211963 2.32985530 -3.05330574
[19] -0.02110926 -1.34909559
Exerciții
Exercițiul 18 Fie vectorii \(x = \left(x_1, \ldots, x_n\right)\) și \(y = \left(y_1, \ldots, y_m\right)\). Definim vectorul \(z = \left(z_1, \ldots, z_n\right)\) prin
\[ z_k=\sum_{j=1}^m \mathbf{1}_\left\{y_j<x_k\right\} \quad \text { pentru } k=1,2, \ldots, n \]
Folosind funcția
outer
, creați o funcție care primește ca argumente pe \(x\) și \(y\) și returnează vectorul \(z\)Creați aceeași funcție de la punctul a) folosind funcția
sapply
în loc deouter
Repetați cerința folosind acum
vapply
în loc desapply
sau deouter
Verificați care dintre cele trei funcții create este mai eficientă în termeni de rapiditate de execuție (Indicație: folosiți funcția
system.time
pentru a măsura timpul de execuție)
Exercițiul 19 Fie \(A\) o matrice care conține elemente de tip NA
. Se cere:
Să se construiască o funcție care extrage submatricea care conține toate coloanele din \(A\) ce nu au elemente de tip
NA
Să se construiască o funcție care extrage submatricea care este formată prin ștergerea fiecărei linii și coloane din \(A\) care conține elemente de tip
NA
.
Exercițiul 20 Setul de date airquality
prezintă măsurători zilnice ale nivelului de ozon (Ozone
), radiații solare (Solar.R
), viteza vântului (Wind
) și temperatură (Temp
) din New York între lunile Mai-Septembrie 1973. Determinați:
Temperatura medie din fiecare lună
Nivelul mediu de radiații solare din ziua 15 a lunilor considerate
Exercițiul 21 Ne propunem să construim mai multe funcții care evaluează funcția
\[ f(n)=\sum_{i=1}^n \sum_{s=1}^r \frac{s^2}{10+4 r^3} \]
și să verificăm care sunt mai eficiente.
Creați o funcție care evaluează \(f(n)\) folosind doar bucle
for
Creați o funcție care evaluează \(f(n)\) folosind funcțiile
row
șicol
pentru a construi o matrice a cărei sumă a elementelor să conducă la rezultatCreați o funcție care evaluează \(f(n)\) folosind funcția
outer
Creați o funcție care evaluează \(f(n)\) folosind funcția
sapply
(sauunlist
șiapply
)