# Creem (si no existeixen) les carpetes on desarem les dades i els resultats
dir.create("dades", showWarnings = FALSE)
dir.create("sortides", showWarnings = FALSE)

Exercici 1. Paquets

Enunciat: comproveu quins paquets teniu instal·lats, instal·leu MASS i survival, comproveu la informació que contenen i busqueu informació sobre Rcmdr des de la consola.

A la consola, library() sense arguments obre una finestra amb tots els paquets instal·lats. Dins d’un document és més útil fer-ho amb installed.packages(), que retorna una matriu (una fila per paquet):

paquets <- rownames(installed.packages())
length(paquets)   # quants paquets tinc instal·lats
## [1] 178
head(paquets, 15) # els 15 primers
##  [1] "abind"      "backports"  "base"       "base64enc"  "bit"       
##  [6] "bit64"      "boot"       "broom"      "bslib"      "ca"        
## [11] "cachem"     "car"        "carData"    "careless"   "cellranger"
# Estan instal·lats MASS i survival?
c("MASS", "survival") %in% paquets
## [1] TRUE TRUE

Per instal·lar-los s’usa install.packages(). Posem eval=FALSE al chunk perquè no es reinstal·lin cada vegada que fem Knit:

install.packages("MASS")
install.packages("survival")
install.packages("Rcmdr")

Un cop instal·lats, cal carregar-los amb library() per usar-los, i podem veure’n la informació (descripció, funcions i conjunts de dades):

library(MASS)
library(survival)

packageDescription("MASS")$Title
## [1] "Support Functions and Datasets for Venables and Ripley's MASS"
packageDescription("survival")$Title
## [1] "Survival Analysis"
packageVersion("MASS")
## [1] '7.3.66'
library(help = "MASS")      # índex complet del paquet
library(help = "survival")

Per buscar informació sobre Rcmdr (R Commander) des de la consola (són ordres interactives, per això eval=FALSE):

??Rcmdr                 # cerca a tota l'ajuda instal·lada
help.search("Rcmdr")    # equivalent a ??Rcmdr

Exercici 2. Importar dades

Per poder practicar sense dependre de fitxers externs, primer creem dos fitxers d’exemple a la carpeta dades/ (un .txt separat per tabuladors i un .csv).

write.table(airquality, "dades/airquality.txt", sep = "\t", row.names = FALSE)
write.csv(MASS::Pima.tr, "dades/pima.csv", row.names = FALSE)

a) Arxiu de text i summary() de tres variables

read.table() llegeix fitxers de text. header = TRUE indica que la primera fila conté els noms de les variables i sep = "\t" que el separador és el tabulador.

dades_txt <- read.table("dades/airquality.txt", header = TRUE, sep = "\t")
head(dades_txt)
# summary() de tres variables: seleccionem les columnes pel nom
summary(dades_txt[, c("Ozone", "Temp", "Wind")])
##      Ozone             Temp            Wind       
##  Min.   :  1.00   Min.   :56.00   Min.   : 1.700  
##  1st Qu.: 18.00   1st Qu.:72.00   1st Qu.: 7.400  
##  Median : 31.50   Median :79.00   Median : 9.700  
##  Mean   : 42.13   Mean   :77.88   Mean   : 9.958  
##  3rd Qu.: 63.25   3rd Qu.:85.00   3rd Qu.:11.500  
##  Max.   :168.00   Max.   :97.00   Max.   :20.700  
##  NA's   :37

b) Arxiu .csv i fivenum() de dues variables

Amb read.csv() (R base) o readr::read_csv() llegim fitxers .csv. fivenum() retorna els cinc números de Tukey: mínim, Q1, mediana, Q3 i màxim.

library(readr)
dades_csv <- read_csv("dades/pima.csv", show_col_types = FALSE)
head(dades_csv)
fivenum(dades_csv$glu)  # glucosa en plasma
## [1]  56.0 100.0 120.5 144.0 199.0
fivenum(dades_csv$bmi)  # índex de massa corporal
## [1] 18.20 27.55 32.80 36.50 47.90

Exercici 3. Conjunt anorexia: tipus de dades, NA/NULL i recodificació

data("anorexia")   # ja hem carregat MASS a l'exercici 1
head(anorexia)

Tipus de dades que conté: str() mostra l’estructura i class() el tipus de cada variable.

str(anorexia)
## 'data.frame':    72 obs. of  3 variables:
##  $ Treat : Factor w/ 3 levels "CBT","Cont","FT": 2 2 2 2 2 2 2 2 2 2 ...
##  $ Prewt : num  80.7 89.4 91.8 74 78.1 88.3 87.3 75.1 80.6 78.4 ...
##  $ Postwt: num  80.2 80.1 86.4 86.3 76.1 78.1 75.1 86.7 73.5 84.6 ...
sapply(anorexia, class)   # aplica class() a cada columna
##     Treat     Prewt    Postwt 
##  "factor" "numeric" "numeric"

Treat és un factor (variable categòrica amb 3 nivells) i Prewt i Postwt són numèriques (pes abans i després, en lliures).

Valors NA i NULL:

sum(is.na(anorexia))        # nombre total de NA
## [1] 0
colSums(is.na(anorexia))    # NA per columna
##  Treat  Prewt Postwt 
##      0      0      0
table(is.na(anorexia))      # 72 files x 3 columnes = 216 valors FALSE
## 
## FALSE 
##   216
is.null(anorexia)
## [1] FALSE

No hi ha cap valor perdut i l’objecte no és nul.

Recodificació de Treat. Amb factor() indiquem els nivells originals (levels) i les noves etiquetes (labels) en el mateix ordre. Ho desem en una còpia (anorexia2) per no modificar l’original, que tornarem a usar a l’exercici 9.

levels(anorexia$Treat)   # nivells originals
## [1] "CBT"  "Cont" "FT"
anorexia2 <- anorexia
anorexia2$Treat <- factor(anorexia2$Treat,
                          levels = c("CBT", "Cont", "FT"),
                          labels = c("Cogn Beh Tr", "Contr", "Fam Tr"))

levels(anorexia2$Treat)
## [1] "Cogn Beh Tr" "Contr"       "Fam Tr"
table(anorexia2$Treat)   # quants pacients hi ha a cada tractament
## 
## Cogn Beh Tr       Contr      Fam Tr 
##          29          26          17

Exercici 4. Exportar dades

a) biopsy a .csv

data("biopsy")
head(biopsy)
write.csv(biopsy, "sortides/biopsy.csv", row.names = FALSE)

b) Melanoma en tres formats diferents

Exportem a .csv, .txt (separat per tabuladors) i .rds (format natiu d’R, conserva els tipus de dades). L’Excel (.xlsx) necessitaria un paquet extra com writexl (writexl::write_xlsx(Melanoma, "sortides/melanoma.xlsx")).

data("Melanoma")
write.csv(Melanoma, "sortides/melanoma.csv", row.names = FALSE)
write.table(Melanoma, "sortides/melanoma.txt", sep = "\t", row.names = FALSE)
saveRDS(Melanoma, "sortides/melanoma.rds")

# Comprovem que s'han creat els fitxers i la seva mida
list.files("sortides", pattern = "melanoma", full.names = TRUE)
## [1] "sortides/melanoma.csv" "sortides/melanoma.rds" "sortides/melanoma.txt"

c) summary() de age desat en un .doc

capture.output() captura el text que R imprimiria a la consola i l’escriu en un fitxer.

summary(Melanoma$age)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    4.00   42.00   54.00   52.46   65.00   95.00
capture.output(summary(Melanoma$age), file = "sortides/summary_age.doc")
file.exists("sortides/summary_age.doc")
## [1] TRUE

d) Importar un .csv d’un repositori de Biomedicina

Exemple amb el conjunt Heart Failure Clinical Records de l’UCI (https://archive.ics.uci.edu/dataset/519/heart+failure+clinical+records).

fitxer <- "dades/heart_failure_clinical_records_dataset.csv"
if (file.exists(fitxer)) {
  heart <- read_csv(fitxer, show_col_types = FALSE)
  print(dim(heart))
  print(head(heart))
} else {
  cat("Encara no s'ha descarregat el fitxer", fitxer, "\n")
}
## [1] 299  13
## # A tibble: 6 × 13
##     age anaemia creatinine_phosphokinase diabetes ejection_fraction
##   <dbl>   <dbl>                    <dbl>    <dbl>             <dbl>
## 1    75       0                      582        0                20
## 2    55       0                     7861        0                38
## 3    65       0                      146        0                20
## 4    50       1                      111        0                20
## 5    65       1                      160        1                20
## 6    90       1                       47        0                40
## # ℹ 8 more variables: high_blood_pressure <dbl>, platelets <dbl>,
## #   serum_creatinine <dbl>, serum_sodium <dbl>, sex <dbl>, smoking <dbl>,
## #   time <dbl>, DEATH_EVENT <dbl>

Exercici 5. Operadors amb birthwt

data("birthwt")
head(birthwt)

a) Edat màxima de les mares

max(birthwt$age)
## [1] 45

b) Edat mínima de les mares

min(birthwt$age)
## [1] 14

c) Rang d’edat (diferència entre màxim i mínim; range() retorna els dos extrems)

range(birthwt$age)
## [1] 14 45
max(birthwt$age) - min(birthwt$age)
## [1] 31

d) Fumava la mare del nounat de menys pes? Seleccionem smoke a la fila on bwt és mínim (smoke: 1 = sí, 0 = no).

birthwt$smoke[birthwt$bwt == min(birthwt$bwt)]
## [1] 1

Sí, la mare fumava.

e) Pes del nounat de la mare amb l’edat màxima

birthwt$bwt[birthwt$age == max(birthwt$age)]
## [1] 4990

f) Pesos dels nounats les mares dels quals visiten el metge menys de dues vegades el primer trimestre (ftv < 2)

pesos_ftv <- birthwt$bwt[birthwt$ftv < 2]
pesos_ftv
##   [1] 2523 2557 2600 2622 2637 2637 2663 2665 2722 2733 2751 2769 2769 2778 2807
##  [16] 2821 2836 2863 2877 2906 2920 2920 2920 2948 2948 2977 2977 2922 3033 3062
##  [31] 3062 3062 3062 3090 3090 3100 3104 3132 3175 3175 3203 3203 3203 3225 3225
##  [46] 3232 3234 3260 3274 3317 3317 3331 3374 3374 3402 3416 3444 3459 3460 3473
##  [61] 3544 3487 3544 3572 3572 3586 3600 3614 3614 3629 3637 3643 3651 3651 3651
##  [76] 3651 3699 3728 3756 3770 3770 3770 3790 3799 3827 3884 3912 3940 3941 3941
##  [91] 3969 3997 3997 4054 4054 4111 4174 4238 4593 4990  709 1135 1330 1474 1588
## [106] 1588 1701 1729 1790 1818 1885 1893 1899 1928 1936 1970 2055 2055 2084 2084
## [121] 2100 2125 2187 2187 2211 2225 2240 2240 2282 2296 2296 2325 2353 2353 2367
## [136] 2381 2381 2381 2410 2410 2410 2424 2442 2466 2466 2495 2495
length(pesos_ftv)   # quants nounats compleixen la condició
## [1] 147

Exercici 6. Matriu amb Prewt i Postwt

matrix() omple per columnes per defecte: si li passem els dos vectors concatenats i ncol = 2, la primera columna serà Prewt i la segona Postwt. cbind() fa el mateix de manera més directa i manté els noms.

matr_anorexia <- matrix(c(anorexia$Prewt, anorexia$Postwt), ncol = 2)
colnames(matr_anorexia) <- c("Prewt", "Postwt")
head(matr_anorexia)
##      Prewt Postwt
## [1,]  80.7   80.2
## [2,]  89.4   80.1
## [3,]  91.8   86.4
## [4,]  74.0   86.3
## [5,]  78.1   76.1
## [6,]  88.3   78.1
dim(matr_anorexia)   # 72 files i 2 columnes
## [1] 72  2
# Alternativa equivalent
head(cbind(Prewt = anorexia$Prewt, Postwt = anorexia$Postwt))
##      Prewt Postwt
## [1,]  80.7   80.2
## [2,]  89.4   80.1
## [3,]  91.8   86.4
## [4,]  74.0   86.3
## [5,]  78.1   76.1
## [6,]  88.3   78.1

Exercici 7. Data frame Tract_Pulmo

Identificador <- c("I1","I2","I3","I4","I5","I6","I7","I8","I9","I10","I11","I12","I13",
                   "I14","I15","I16","I17","I18","I19","I20","I21","I22","I23","I24","I25")
Edat <- c(23,24,21,22,23,25,26,24,21,22,23,25,26,24,22,21,25,26,24,21,25,27,26,22,29)
Sexe <- c(1,2,1,1,1,2,2,2,1,2,1,2,2,2,1,1,1,2,2,2,1,2,1,1,2) # 1 dona, 2 home
Pes <- c(76.5,81.2,79.3,59.5,67.3,78.6,67.9,100.2,97.8,56.4,65.4,67.5,87.4,99.7,87.6,
         93.4,65.4,73.7,85.1,61.2,54.8,103.4,65.8,71.7,85.0)
Alt <- c(165,154,178,165,164,175,182,165,178,165,158,183,184,164,189,167,182,179,165,
         158,183,184,189,166,175) # altura en cm
Fuma <- c("SÍ","NO","SÍ","SÍ","NO","NO","NO","SÍ","SÍ","SÍ","NO","NO","SÍ","SÍ","SÍ",
          "SÍ","NO","NO","SÍ","SÍ","SÍ","NO","SÍ","NO","SÍ")

Tract_Pulmo <- data.frame(Identificador, Edat, Sexe, Pes, Alt, Fuma)
Tract_Pulmo

Un data frame s’indexa com df[files, columnes]. Si deixem un costat buit, vol dir “totes”.

a) Registres amb edat > 22

Tract_Pulmo[Tract_Pulmo$Edat > 22, ]    # amb indexació lògica

b) Element 3 de la columna 4 (fila 3, columna 4 = Pes)

Tract_Pulmo[3, 4]
## [1] 79.3

c) subset(): edat < 27 i sense la columna Alt. L’argument select indica les columnes; amb el signe - les excloem.

subset(Tract_Pulmo, Edat < 27, select = -Alt)

Exercici 8. ChickWeight

a) Incorporar el conjunt de dades del paquet datasets (es carrega per defecte amb R):

data("ChickWeight", package = "datasets")
str(ChickWeight)
## Classes 'nfnGroupedData', 'nfGroupedData', 'groupedData' and 'data.frame':   578 obs. of  4 variables:
##  $ weight: num  42 51 59 64 76 93 106 125 149 171 ...
##  $ Time  : num  0 2 4 6 8 10 12 14 16 18 ...
##  $ Chick : Ord.factor w/ 50 levels "18"<"16"<"15"<..: 15 15 15 15 15 15 15 15 15 15 ...
##  $ Diet  : Factor w/ 4 levels "1","2","3","4": 1 1 1 1 1 1 1 1 1 1 ...
##  - attr(*, "formula")=Class 'formula'  language weight ~ Time | Chick
##   .. ..- attr(*, ".Environment")=<environment: R_EmptyEnv> 
##  - attr(*, "outer")=Class 'formula'  language ~Diet
##   .. ..- attr(*, ".Environment")=<environment: R_EmptyEnv> 
##  - attr(*, "labels")=List of 2
##   ..$ x: chr "Time"
##   ..$ y: chr "Body weight"
##  - attr(*, "units")=List of 2
##   ..$ x: chr "(days)"
##   ..$ y: chr "(gm)"
head(ChickWeight)

b) Gràfic de dispersió de weight. Amb una sola variable, plot() posa l’índex de l’observació a l’eix X i el valor a l’eix Y.

plot(ChickWeight$weight, col = "steelblue", pch = 20,
     main = "Pes dels pollets", xlab = "Observació", ylab = "Pes (g)")

c) Diagrama de caixa de Time

boxplot(ChickWeight$Time, col = "yellow",
        main = "Temps des del naixement", ylab = "Dies")

Exercici 9. anorexia_treat_df

L’enunciat diu calcular Prewt - Postwt, però amb aquesta resta un guany de pes dona un valor negatiu. Per interpretar-ho més fàcilment calculem Postwt - Prewt, de manera que valors positius = pes guanyat.

dif_pes <- anorexia$Postwt - anorexia$Prewt

anorexia_treat_df <- data.frame(Treat = anorexia$Treat, dif_pes)
head(anorexia_treat_df)
# Individus que han guanyat pes
guanyen <- subset(anorexia_treat_df, dif_pes > 0)
nrow(guanyen)
## [1] 42
# Només els del tractament "Cont" que han guanyat pes (& = i lògic)
anorexia_treat_C_df <- subset(anorexia_treat_df, Treat == "Cont" & dif_pes > 0)
anorexia_treat_C_df

Exercici 10. RPubs

Cas pràctic

a) Crear un conjunt de dades inventat

Usem set.seed() perquè els valors aleatoris siguin sempre els mateixos (resultats reproduïbles). rep() repeteix valors, sample() tria valors a l’atzar i rnorm() genera valors d’una distribució normal.

set.seed(123)
n <- 30

dades <- data.frame(
  Id    = paste0("P", 1:n),                                   # caràcter
  Edat  = sample(18:80, n, replace = TRUE),                   # numèrica
  Gene  = rep(c(1, 2), each = 15),                            # 1 = dona, 2 = home
  Tract = factor(sample(c("A", "B", "C"), n, replace = TRUE)),# factor
  Pes   = round(rnorm(n, mean = 72, sd = 12), 1),             # kg
  Alt   = round(rnorm(n, mean = 168, sd = 9))                 # cm
)
head(dades)

b) Informació del conjunt de dades i de les variables

dim(dades)         # files i columnes
## [1] 30  6
str(dades)         # tipus de cada variable
## 'data.frame':    30 obs. of  6 variables:
##  $ Id   : chr  "P1" "P2" "P3" "P4" ...
##  $ Edat : int  48 32 68 31 20 59 67 71 60 54 ...
##  $ Gene : num  1 1 1 1 1 1 1 1 1 1 ...
##  $ Tract: Factor w/ 3 levels "A","B","C": 2 3 2 1 3 3 1 3 2 1 ...
##  $ Pes  : num  81.9 80.3 78.6 71.3 68.3 67.4 63.7 69.5 56.8 98 ...
##  $ Alt  : num  158 171 172 168 176 186 164 147 177 162 ...
summary(dades)     # resum estadístic
##       Id                 Edat            Gene     Tract       Pes       
##  Length:30          Min.   :20.00   Min.   :1.0   A:13   Min.   :53.40  
##  Class :character   1st Qu.:31.25   1st Qu.:1.0   B: 7   1st Qu.:67.25  
##  Mode  :character   Median :45.50   Median :1.5   C:10   Median :71.40  
##                     Mean   :47.93   Mean   :1.5          Mean   :72.79  
##                     3rd Qu.:65.25   3rd Qu.:2.0          3rd Qu.:78.90  
##                     Max.   :74.00   Max.   :2.0          Max.   :98.00  
##       Alt       
##  Min.   :147.0  
##  1st Qu.:165.0  
##  Median :170.0  
##  Mean   :169.0  
##  3rd Qu.:173.8  
##  Max.   :186.0
table(dades$Gene)  # 15 dones i 15 homes
## 
##  1  2 
## 15 15
table(dades$Tract)
## 
##  A  B  C 
## 13  7 10

c) Nova variable: IMC

IMC = pes (kg) / alçada (m)². Com que Alt és en cm, la dividim per 100. Assignant a dades$IMC la columna s’afegeix directament al data frame.

dades$IMC <- round(dades$Pes / (dades$Alt / 100)^2, 2)
head(dades)

d) Data frames per a homes i dones

Df_Dones <- subset(dades, Gene == 1)
Df_Homes <- subset(dades, Gene == 2)
nrow(Df_Dones)
## [1] 15
nrow(Df_Homes)
## [1] 15
head(Df_Dones)
head(Df_Homes)

e) Combinar-los de nou amb rbind()

rbind() uneix data frames per files (han de tenir les mateixes columnes).

Df_Tot <- rbind(Df_Dones, Df_Homes)
dim(Df_Tot)
## [1] 30  7
identical(Df_Tot, dades)   # recuperem exactament el conjunt original
## [1] TRUE