Manual de R (Estadística)/El llenguatge R

Introducció modifica

En R es diu que tot són objectes. En R, un objecte és qualsevol cosa que es pot assignar a una variable. Això inclou constants, estructures de dades, funcions, i fins i tot gràfics. Els objectes tenen una mode (que es descriu com s'emmagatzema l'objecte) i una class (que diu quin tipus d'objecte és). El manual del llenguatge és pot consultar a [1].

Tipus de dades modifica

En R les variables es poden emmagatzemar com diferents tipus de dades o "classes" [1].

Existeixen cinc tipus de dades bàsiques (veure Tipus de variables ):

  • Caràcters.
  • Nombres (nombres reals).
  • Enters.
  • Nombres complexos.
  • Lògics (Verdader / Fals).

Aquestes s'agrupen en unes classes de dades més complexes:

  • Vectors: tots els elements són del mateix tipus (p. ex., tots han de ser caràcters o tots enters).
  • Llistes: els elements poden ser de tipus diferents.
  • Factors: s'utilitzen per representar les dades categòriques. Hom pot pensar en un factor com un vector d'enters on cada nombre té una etiqueta. Els factors poden ser ordenats o no.
  • Matrius.
  • Arrays. Són matrius de qualsevol nombre de dimensions.
  • Taules de dades. Son matrius on les variables són de qualsevol tipus i és una estructura similar als conjunts de dades que es troben en els paquets estadístics estàndard. Les columnes són variables i les files són observacions. Una taula de dades pot tenir diferents tipus de variables (p. ex., numèriques, caràcters). Les taules de dades són les principals estructures que utilitzarà per emmagatzemar conjunts de dades. Típicament es crea quan es llegeix i s'importa un fitxer de dades.

Per saber el tipus d'un objecte determinat s'utilitza l'ordre class(<Nom d'objecte>).

Per tant, hi ha moltes maneres diferents per emmagatzemar les variables en R (és a dir, objectes), i cada una d'elles correspon a un tipus d'objecte diferent ("classes"). A continuació és presenten alguns dels objectes més emprats juntament amb la seva descripció i la classe corresponent [1]:

Objecte Descricció Tipus (Class)
Nombre Individual o lletra / paraula Només un sol número o caràcter o paraula o frase entre cometes Numèrica o de caràcters
Vector Un vector de tots números o tots caràcters O tots nombres o tots caràcter
Matriu Té columnes i files - totes les entrades són de la mateixa classe O tots nombres o tots caràcter
Taula de dades Igual que una matriu però columnes poden ser de diferents tipus (classes) data.frame
Llista Un munt de diferents objectes tots agrupats sota un mateix nom list

Operadors modifica

Aritmètics modifica

X + Y : Suma

X - Y : Resta

X * Y : Multiplicació

X / Y : Divisió

X ^ Y : Potència

X %% Y: Mòdul

X %/% Y : Divisió entera

Relacionals modifica

X < Y

X > Y

X <= Y

X >= Y

X == Y

X != Y

Lògics modifica

! X Negació

X && Y

X & Y

X || Y

X | Y

Funcions modifica

sum(nom_variable) Ens suma tots els valors de la variable. Si volem sumar els valors de l'1 al 20 fem:

sum(c(1:20))


sort(nom_variable) Ordena els valors d'una variable.


sqrt(nom_variable) Arrel quadrada.


^ Potència.


mean() Mitja aritmètica.


summary() Fa un resum d'estadística descriptiva de la variable.


sample(nom_variable,longitud) Agafa una mostra aleatòria de la longitud desitjada entre tots els valors de la mostra.


runif(n) Genera una successió de n nombres aleatoris segons una llei uniforme.


rnorm() Genera una successió de n nombres aleatoris segons una llei normal.


length() Diu el nombre de posicions del vector.


Prod() El producte dels elements d'un vector.

prod(c(1:20))

exp(sum(log(c(1:20))))

Seguin la classificació de Kabacoff ([2], pàg 42, 93-102), es classificaran en:

  • Per treballar amb objectes de dades
  • Per gestionar l'espai de treball de R
  • Matemàtiques
  • Estadístiques
  • Probabilitats
  • Per text
  • Per directoris i fitxers externs
  • Altres

Funcions per treballar amb objectes de dades modifica

Algunes de les funcions que cita Kabacoff ([2] pag. 42-43) són:

  • c (<Nom objecte 1er>, <Nom objecte 2n>, ...): combina objectes en un vector.
  • cbind (<Nom objecte 1er>, <Nom objecte 2n>, ...): combina objectes en columnes.
  • rbind (<Nom objecte 1er>, <Nom objecte 2n>, ...): combina objectes en files.
  • class (<Nom objecte>): mostra el tipus d'objecte.
  • head (<Nom objecte>): mostra la part inicial de l'objecte.
  • tail (<Nom objecte>): mostra la part final de l'objecte.
  • length (<Nom objecte>): mostra el nombre d'elements o de components d'un objecte.
  • ls (): llista els objectes.
  • names (<Nom objecte>): mostra els noms dels components d'un objecte.
  • rm (<Nom objecte 1er>, <Nom objecte 2n>, ...): esborra els objectes llistats.
  • str (<Nom objecte>): mostra l'estructura d'un objecte.

Funcions per gestionar l'espai de treball de R modifica

Algunes de les funcions que cita Kabacoff ([2] pag. 12) són:

  • getwd()
  • setwd("El meu directori")
  • ls(): És com llistar totes les variables.
  • rm(): Per esborrar una variable hem d'utilitzar l'instrucció "rm(nom_variable)".
  • rm(<llista d'objectes>)
  • help(options)
  • options()
  • history(<número>): mostra les darrers <nombre> ordres
  • save(<llista d'objectes>, file="<nom de fitxer>"): desa els objectes

Funcions per les dades en R modifica

  • c(valor, valor, ...): combina els valors (numèrics, caràcter) en un vector. La "c" és de "combine". Serveix per crear vectors.
Exemples
  • X = c(0,5, 0,6) # numèric
  • X = c(TRUE, FALSE) ## Lògic
  • X = c(T, F) ## Lògic
  • X = c("a", "b") ## Caràcter
  • names(<DADES>): torna els noms de les columnes de la nostra taula, és a dir, els noms de les variables.
  • ncol(<DADES>): nombre de variables (nombre de columnes) d'una taula de dades.
  • nrow(<DADES>): nombre de registres (nombre de files) d'una taula de dades.

Funcions que realitzen bucles modifica

Quan es treballa de forma interactiva, es complicat escriure bucles amb for o while. Existeixen unes funcions per realitzar-los de forma senzilla:

  • lapply: recorre els elements d'una llista i avaluar una funció en cada un d'ells de forma consecutiva.
  • sapply: igual que lapply, però intenta simplificar el resultat.
  • apply: aplica una funció sobre els marges d'una matriu.
  • tapply: aplica una funció sobre els subconjunts d'un vector.
  • mapply: és una versió multivariada de lapply.

És molt útil utilitzar la funció split amb aquestes funcions, especialment amb lapply o sapply, ja que permet trossejar un objecte.

apply modifica

Tècnicament "retorna un vector o matriu o llista de valors obtinguts mitjançant l'aplicació d'una funció als marges d'una matriu o matriu". A la pràctica, és útil per calcular, p. ex., el valor mitjà o el valor màxim, de:

  • Una sèrie de variables d'una taula de dades (p. ex., la mitjana o el màxim d'una sèrie de variables de cada observació o fila).
  • Una variable d'una taula de dades (p. ex., la mitjana o el màxim d'una variable). Aquest cas és poc útil, ja que és més fàcil l'ordre mean(<Nom de la variable de la taula de dades>).

El paràmetre de la funció apply són:

  • Nom de la taula de dades.
  • Marge sobre el que aplicar la funció:
    • 1: sobre les files.
    • 2: sobre les columnes.
    • c(1, 2): sobre files i columnes.
  • La funció a aplicar (p. ex., mean, max, sum o inclús sort)

Per exemple, per calcular la mitjana de les variables V1 i V2 quantitatives d'una taula de dades:

> DADES = data.frame(V1 = c(3, 7, 2, 4), 
+                    V2 = c(4, 2, 1, 3))
> DADES
  V1 V2
1  3  4
2  7  2
3  2  1
4  4  3

> # Var nova (MIT) amb la mitjana de V1 i V2 per cada observació
> DADES$MIT = apply(DADES, 1, mean)          # marge = 1: files
> DADES
  V1 V2 MIT
1  3  4 3.5
2  7  2 4.5
3  2  1 1.5
4  4  3 3.5

Si existeixen valors desconeguts en el valors d'alguna de les variables, es pot fer servir l'opció na.rm = TRUE a fi de que calculi la mitjana (o altre funció) amb la resta de valors coneguts:

> # Amb valors desconeguts
> DADES = data.frame(V1 = c(3, 7, 2,  4), 
+                    V2 = c(4, 2, 1, NA))
> DADES
  V1 V2
1  3  4
2  7  2
3  2  1
4  4 NA

> # Var nova (MIT) amb la mitjana de V1 i V2 per cada observació
> DADES$MIT = apply(DADES, 1, mean, na.rm = TRUE)          # marge = 1: files
> DADES
  V1 V2 MIT
1  3  4 3.5
2  7  2 4.5
3  2  1 1.5
4  4 NA 4.0

Si només es volgués aplicar la funció a un grup de variable, es poden seleccionar pel seu nom:

> # Només unes variables
> DADES = data.frame(V1 = c(3, 7, 2,  4), 
+                    V2 = c(4, 2, 1, NA),
+                    V3 = c(2, 5, 3,  3))
> DADES
  V1 V2 V3
1  3  4  2
2  7  2  5
3  2  1  3
4  4 NA  3

> # Var nova (MIT) amb la mitjana de V1 i V2 per cada observació (excloent V3)
> DADES$MIT = apply(DADES[, c("V1", "V2")], 1, mean, na.rm = TRUE)
> DADES
  V1 V2 V3 MIT
1  3  4  2 3.5
2  7  2  5 4.5
3  2  1  3 1.5
4  4 NA  3 4.0

Amb la ordre DADES[, c("V1", "V2")] es seleccionen totes les observacions (el valor de les files està en blanc a l'ordre [ , c("V1", "V2")]) de les variable V1 i V2.

lapply modifica

Processa les observacions d'una variable d'una taula de dades (o d'una llista) i avalua una funció en cada element. Sol tenir només dos arguments, una llista (que pot ser, p. ex., una taula de dades o una variable d'una taula de dades) i el nom d'una funció (que pot ser una del propi R o escrita per un mateix), encara que després pot tenir arguments addicionals (indicats a la sintaxis següent per "..."):

lapply(<Nom de la llista>, <Nom de la funció>, ...)

# Si hi han valors desconeguts i la funció els ha d'excloure:
lapply(<Nom de la llista>, <Nom de la funció>, na.rm = TRUE, ...)

Si el primer argument no és una llista, es pot forçar que ho sigui (si és possible), amb as.list.

En l'exemple següent, s'arrodoneix els valors de la pressió arterial diastòlica aplicant la funció round a cada un dels valors i després es calcula la mitjana de les variables d'una taula de dades (edat i pressió arterial diastòlica):

> DADES = data.frame(EDAT = c(50, NA, 20, 18, 30),
+                    PAD  = c(8.3, 10.2, 7.7, 11.4, 8.6))
> DADES
  EDAT  PAD
1   50  8.3
2   NA 10.2
3   20  7.7
4   18 11.4
5   30  8.6

> DADES$PAD = lapply(DADES$PAD, round)
> DADES
  EDAT PAD
1   50   8
2   NA  10
3   20   8
4   18  11
5   30   9

# Crear una llista amb la mitjana de les variables d'una taula de dades
#  (edat i pressió arterial diastòlica):
> DADES_MI = lapply(DADES, mean, na.rm = TRUE)
> DADES_MI
$EDAT
[1] 29.5

$PAD
[1] 9.24

En el primer exemple (arrodonir els valors), el mateix resultat s'obtindria de forma més directa amb l'ordre DADES$PAD = round(DADES$PAD).

En el segon exemple, a la funció se li passa l'argument addicional na.rm = TRUE, ja que la variable edat presenta un valor desconegut. Sense aquest argument, el resultat de la mitjana de l'edat seria NA.

En el següent exemple de Computing for Data Analysis es genera un número variable de nombres aleatoris amb la funció runif (aquesta genera tants nombres aleatoris com el nombre que se li passa com argument):

> X <- 1:4           # Llista de 4 nombres
> X
[1] 1 2 3 4

> lapply(X, runif)   # Generar 4 grups de 1, 2, 3 i 4 nombres aleatoris
[[1]]
[1] 0.6403106

[[2]]
[1] 0.009495756 0.232550506

[[3]]
[1] 0.6660838 0.5142511 0.6935913

[[4]]
[1] 0.5449748 0.2827336 0.9234335 0.2923158

A la funció runif se li passa consecutivament com argument 1, 2, 3 i 4. Per tant, primer genera un sol nombre aleatori (0.6403106), després en genera dos (0.009495756 0.232550506), després 3 (0.6660838 0.5142511 0.6935913) i, finalment, quatre. Si es volgués que els nombres aleatoris prenguessin valors entre 0,1 i 10, es poden passar aquests valors com arguments de la funció lapply:

lapply(X, runif, min=0.1, max=10)

La funció lapply sempre torna una llista d'elements.

sapply modifica

Realitza la mateixa funció que lapply, però intenta tornar el resultat més simple possible. Per exemple [3]:

  • Si el resultat és una llista en la que cada element té una longitud de 1, llavors en lloc de tornar una llista, torna un vector.
  • Si el resultat és una llista en que cada element és un vector de la mateixa longitud i major de 1, la funció torna una matriu.

Exemple [3]:

# Es crea una llista amb elements de diferent longitud (4, 5, 6 i 10):
> X <- list(a = 1:4, b = rnorm(5), c = rnorm(6, 1), d = rnorm(10, 5))
> X
$a
[1] 1 2 3 4

$b
[1] -0.1703292  1.1918528  1.3658102 -0.7606006 -0.9737320

$c
[1]  2.9097989 -0.7529179  1.9556914  3.3340616  2.1016724  2.0919802

$d
 [1] 5.285383 3.440272 5.855748 5.427933 4.712756 4.798824 5.900511 2.745999
 [9] 4.480313 4.637082

# La funció lapply torna un llista de 4 elements de longitud 1 
#  (la mitjana de cada un dels elements de la llista X):
> lapply(X, mean)
$a
[1] 2.5

$b
[1] 0.1565971

$c
[1] 0.9895367

$d
[1] 5.092754

# La funció spally torna un vector de 4 elements:
> sapply(X, mean)
        a         b         c         d 
2.5000000 0.1565971 0.9895367 5.0927545 

# Si s'aplica la funció mean a X, dona error, ja que no es pot aplicar a llistes:
> mean(X)
[1] NA
Warning message:
In mean.default(X) : argument is not numeric or logical: returning NA

Funcions pels valors desconeguts modifica

  • is.na()

complete.cases() modifica

Retorna un vector lògic amb la longitud del nombre d'observacions (files) de la taula de dades i amb al valor és TRUE per les observacions sense valors desconeguts. Aquest vector es pot utilitzar per filtrar les observacions sense valors desconeguts:

> # Dades de 5 persones
> DADES = data.frame(INICIALS=c("PG", "GB", "NR", "IR", "NA"),
+                    SEXE=c(NA, "Home", "Dona", "Dona", "Home"),
+                    EDAT=c(50, NA, 20, 18, 30))
> DADES                                 # Llistar les dades
  INICIALS SEXE EDAT
1       PG <NA>   50
2       GB Home   NA
3       NR Dona   20
4       IR Dona   18
5       NA Home   30
> DADES_SD = complete.cases(DADES)      # Identificar casos sense valors desconeguts
> DADES_SD                              # Tenen tots els valors
[1] FALSE FALSE  TRUE  TRUE  TRUE
> DADES[DADES_SD,]                      # Es seleccionen els casos sense valors desconeguts
  INICIALS SEXE EDAT
3       NR Dona   20
4       IR Dona   18
5       NA Home   30

na.omit() modifica

Omet les files que contenen valors mancants.

> DADES_SD = na.omit(DADES)             # Elimina observacions amb valors mancants
> DADES_SD                              # Llistat de les dades
  INICIALS SEXE EDAT
3       NR Dona   20
4       IR Dona   18
5       NA Home   30

Funcions matemàtiques modifica

Funcions estadístiques modifica

is.na(): valor desconegut modifica

Retorna TRUE si l'argument conté un valor desconegut i FALSE si no ho és:

> EDAT = NA
> EDAT
[1] NA

> is.na(EDAT)
[1] TRUE

> EDAT = 45
> EDAT
[1] 45

> is.na(EDAT)
[1] FALSE

sd: desviació típica modifica

Estima la desviació típica (o desviació estàndard) d'un conjunt de dades (és a dir, l'arrel quadrada de la seva variància):

> sd(c(42,56,58))
[1] 8.717798

Funcions de probabilitats modifica

runif modifica

Genera nombres pseudo-aleatoris d'una distribució uniforme en l'interval definit per un mínim i un màxim (per omissió, de 0 a 1). Sintaxis:

runif(n, min=<valor mínim>, max=<valor màxim>)

on "n" és el nombre de números aleatoris que es volen generar. Exemple:

> set.seed(1234)            # El fixa la llavor 
> runif(5)                  # Generar 5 nombres aleatoris entre 0 i 1
[1] 0.1137034 0.6222994 0.6092747 0.6233794 0.8609154

> set.seed(1234)            # El fixa la llavor 
> runif(5, min=0.1, max=10) # Generar 5 nombres aleatoris entre 0.1 i 10
[1] 1.225664 6.260764 6.131820 6.271456 8.623062

Cada vegada que es generen nombres pseudo-aleatoris els resultats són diferents, ja que la llavor utilitzada és diferent en cada moment. Perquè els resultats siguin reproduïbles, es pot especificar explícitament la llavor, utilitzant el <codi>set.seed (<nombre>)</codi>, tal com es mostre en l'exemple anterior.

Funcions per dates i temps modifica

Funcions per a text modifica

as.character(): Convertir a caràcter modifica

Converteix un tipus de variable en un caràcter:

> X = as.character(3.14)  # Assigna el nombre 3,14 a la variable X
> X                       # Mostrar el valor de la variable X  
[1] "3.14"
> class(X)                # Mostrar el tipus de variable de C
[1] "character"

is.na(): valor desconegut modifica

Retorna TRUE si l'argument conté un valor desconegut i FALSE si no ho és:

> SEXE = NA
> SEXE
[1] NA

> is.na(SEXE)
[1] TRUE

> SEXE = "D"
> SEXE
[1] "D"

> is.na(SEXE)
[1] FALSE

paste(): concatenar caràcters i/o variables modifica

Dos valors de tipus caràcters es poden concatenar amb la funció paste:

> NOM    = "Xavier"
> COGNOM = "Rull" 
> paste(NOM, COGNOM)
[1] "Xavier Rull"

Si no es vol un espai entre els caràcters es pot utilitzar l'opció sep="":

> NOM    = "Xavier"
> COGNOM = "Rull" 
> paste(NOM, COGNOM, sep="")
[1] "XavierRull"

També amb variables i text. Per exemple, es pot combinar la variable XCAMIF (el camí on estan una sèrie de funcions) i el text des.R (el nom del fitxer on està una una funció):

> XCAMIF = "~/r/r_funcions/"
> XCAMI=paste(XCAMIF, "des.R", sep="")
> print(XCAMI)
[1] "~/r/r_funcions/des.R"

Això és útil per no tenir, p. ex., que escriure el camí on està el fitxer des.R si aquest s'ha de carregar.

sub(): substituir caràcter modifica

Si en un cap text es te, per exemple, uns punts que es volen treure (i substituir per un espai, " "), és poden fer servir les funcion sub i gsub. La primera substitueix només el primer punt que troba i la segona els substitueix tots. Per exemple, suposem que en una taula de dades tenim el camp NOM amb els noms amb un punt entre nom i cognom i un punt després del cognom:

> NOM = "Ian.Murdock."
> print(NOM)
[1] "Ian Murdock."

> NOM_SP = sub ("[.]", " ", NOM, ignore.case=FALSE)    # Elimina el primer punt. El ignore.case no cal.
> print(NOM_SP)
[1] "Ian Murdock."

> NOM_SP = gsub("[.]", " ", NOM, ignore.case=FALSE)    # Elimina tots els punts. El ignore.case no cal.
> print(NOM_SP)
[1] "Ian Murdock "

També es poden eliminar més d'un caràcter. P. ex., si en els noms hi ha un punt i una ensaïmada, es poden substituir els dos a la vegada per un espai:

> NOM = "Ian.Murdock@"
> NOM
[1] "Ian.Murdock@"

> NOM_SP = gsub("[., @]", " ", NOM, ignore.case=FALSE)    # Elimina punt i @. El ignore.case no cal.
> print(NOM_SP)
[1] "Ian Murdock "

substring(): Extreure subcaràcters modifica

Extreure o reemplaçar subcadenes d'un caràcter.

> substr("Qualsevol nit pot sortir el sol.", start=11, stop=13) 
[1] "nit"
> substr("Qualsevol nit pot sortir el sol.", 11, 13) 
[1] "nit"

Funcions per directoris i fitxers externs modifica

file.exists modifica

Per verificar si existeixen directoris i fitxers no R:

file.exists(<directori>)
file.exists(<fitxer>)

Exemples:

> file.exists("~/tmp")                                                 # Verifica si existeix el directori ~/sys/analisis/
[1] TRUE                                                               # El directori existeix.

> file.exists("~/tmp/DADES.CSV")                                       # Verifica si existeix el fitxer DADES.CSV al directori ~/tmp/
[1] FALSE                                                              # El fitxer no existeix

És molt útil per crear un directori on es vulgui deixar els resultats, si aquest no existeix:

> CAMI_RES = "~/sys/analisis/r_proves/viquillibre/"                      # Camí on es volen deixar els resultats
> CAMI_RES
[1] "~/sys/analisis/r_proves/viquillibre/"

> if(!file.exists(CAMI_RES)) { dir.create(CAMI_RES,showWarnings=F) }     # Verifica si existeix. El crea si no existeix

Altres tipus de funcions modifica

Càlcul amb funcions modifica

Anem a calcular la mitja aritmètica:

sum(nom_variable)/length(nom_variable)

Calculem la mitja armònica:

1/sum(1/nom_variable)/length(nom_variable)

Definició de variables modifica

Asignar una variable Per asignar el valor "24" a la variable "hores" usem la comanda:

hores<-24

També es pot assignar un valor amb el signe "=". La ordre seria:

hores=24

Perquè mostri el valor de la variable "hores":

hores

Igualment amb:

dies<-365 minuts<-60 segons<-60

Per tant si volem que ens faci el producte hem de posar i hem de saber que com que el resultat surt per pantalla no s'emmagatzema enlloc i per tant haurem de tornar a escriure l'ordre per tornar a calcular el resultat:

dies*hores*minuts*segons

c()

Si volem assignar els valors 7, 35 i 54 a una variable escriurem:

variable<-c(7,35,54)

Si el que volem és que ens faci una llista de l'1 al 20 ho podem aconseguir fent:

llista<-c(1:20)

Però si ho volem per posar un mateix nombre 7 unes 20 vegades fem:

mateix<-c(rep(7,20))

Càlculs amb manipulació de variables modifica

Si tenim una variable amb 5 posicions i fem la instrucció:

a<-c(1:5); a+5;

Ens dóna com a resultat el mateix vector però amb un cinc sumat a totes les posicions. Recordem que aquest valor no es guarda sino que simplement s'imprimeix per pantalla.

Si l'elevem al quadrat ens dóna els quadrats de cada posició.

a^2

En definitiva si fem operacions amb una variable que és un vector ens fa tantes operacions com posicions té el vector.

Matrius modifica

matrix(nom_variable,n_files) Expressa la variable en forma d'una matriu amb les files desitjades.

t() Ens fa la transposada d'una funció.

%*% És el simbol per poder multiplicar dues funcions.

solve() Ens calcula la matriu inversa d'una altra.

Programació modifica

IF

if(cond) expr if(cond) cons.expr else alt.expr


FOR

for(var in seq) expr while(cond) expr repeat expr break next


Exemples:

for(i in 1:5) print(1:i)


for(n in c(2,5,10,20,50)) {

  x <- stats::rnorm(n)
  cat(n,":", sum(x^2),"\n")

}


f = factor(sample(letters[1:5], 10, replace=TRUE)) for( i in unique(f) ) print(i)

L'ordre system permet executar des de dintre el R una ordre de Linux. Per exemple, per llistar el contingut del directori temporal de l'usuari:

> system("dir ~/tmp")
1202022.pdf
1309.pdf
...

Referències modifica

  1. 1,0 1,1 Data types, part 1: Ways to store variables
  2. 2,0 2,1 2,2 Kabacoff RI. R in action, data analysis and graphics with R. Shelter Island, NY: Manning Publications Co; 2011
  3. 3,0 3,1 Roger D. Peng. Computing for Data Analysis.