--- title: "Basic data structures in R -- matrices, data frames and lists" author: "Claire Vandiedonck & Jacques van Helden" date: "`r Sys.Date()`" output: html_document: self_contained: no code_folding: hide fig_caption: yes fig_height: 6 fig_width: 7 highlight: tango incremental: no keep_md: yes smaller: yes theme: cerulean toc: yes toc_depth: 3 toc_float: yes widescreen: yes beamer_presentation: colortheme: dolphin fig_caption: yes fig_height: 6 fig_width: 7 fonttheme: structurebold highlight: tango incremental: no keep_tex: no slide_level: 2 theme: Montpellier toc: yes pdf_document: fig_caption: yes highlight: zenburn toc: yes toc_depth: 3 revealjs::revealjs_presentation: theme: night transition: none self_contained: no code_folding: hide css: ../../slides.css slidy_presentation: font_adjustment: 0 ## set to negative/positive values for smaller/bigger fonts duration: 45 self_contained: no code_folding: hide fig_caption: yes fig_height: 6 fig_width: 7 highlight: tango incremental: no keep_md: yes smaller: yes theme: cerulean toc: yes widescreen: yes ioslides_presentation: self_contained: no code_folding: hide css: ../../slides.css fig_caption: yes fig_height: 6 fig_width: 7 highlight: tango smaller: yes toc: yes widescreen: yes font-import: http://fonts.googleapis.com/css?family=Risque subtitle: DUBii -- Statistics with R font-family: Garamond transition: linear --- ```{r include=FALSE, echo=FALSE, eval=TRUE} ## Install knitr package if required if (!require(knitr)) { message("Installing missing package: knitr") install.packages("knitr") } library(knitr) options(width = 300) knitr::opts_chunk$set( fig.width = 7, fig.height = 5, out.width = "80%", fig.align = "center", fig.path = "figures/data-structures_", size = "tiny", echo = TRUE, eval = TRUE, warning = FALSE, message = FALSE, results = TRUE, comment = "") # knitr::asis_output("\\footnotesize") ``` ## Exercice 1 - Créez la matrice identité `matIdentite` de dimension 10 lignes x 10 colonnes contenant uniquement le chiffre 0. Puis remplacez uniquement les valeurs de la diagonale par le chiffre $1$. Imprimez la matrice à l'écran. - Créez une matrice `matAleatoire` contenant des valeurs tirées aléatoirement de dimension 10 lignes x 10 colonnes, dont les éléments suivent une loi normale de moyenne 0 et de variance $5$. Imprimez la matrice à l'écran en arrondissant à 2 décimales. **Fonctions recommandées :** `matrix()`, `diag()`, `print()`, rnorm()`, `round()` ### Solutions En cas d'urgence poussez sur **Code** pour révéler la solution. ```{r solution_1} matIdentite <- matrix(data = 0, ncol = 10, nrow = 10) diag(matIdentite) <- 1 #diag() retourne un vecteur dont on peut remplacer les valeurs 0 par 1 print(matIdentite) cat("2 variantes plus efficaces pour creer la matrice : \n") cat("------variante 1:\n") diag(1,10,10) cat("------variante 2:\n") diag(10)# en fait, par defaut, si on n'utilise qu'un seul argument dans la fonction diag(), la commande retourne une matrice de 0 sauf pour la diagonale contenant des 1 ``` #### Matrice de nombres aléatoires ```{r result_1b} ## Generate a 10x10 matrix with random normal numbers matAleatoire <- matrix( nrow = 10, ncol = 10, data = rnorm(n = 100, mean = 0, sd = sqrt(5))) # as long as you specify the arguments, you may change the order to pass the arguments to the function ## Print the results rounded at 2 decimals print(round(matAleatoire, digits = 2)) ``` ## Exercice 2 ### Exercice 2.1 Créez deux vecteurs aléatoires nommés `x1` et `x2`, contenant chacun $n = 10.000$ valeurs aléatoires respectivement compatibles: a. avec une loi normale centrée réduite pour `x1`; b. avec une loi uniforme définie sur l’intervalle $[0, 10]$ pour `x2`. Vérifiez la distribution empirique de ces échantillons en dessinant des histogrammes. Vérifiez si les paramètres de vos échantillons aléatoires correspondent à vos attentes (et à leur *espérance statistique*). **Fonctions recommandées :** `rnorm()`, `runif()`, `cbind()`, `rbind()`, `dim()`, `mean()`, `var()`, `min()`, `max()`, `summary()`, `hist()`, `summary`... ### Solutions ```{r} n <- 10000 ## define vector sizes x1 <- rnorm(n = n, mean = 0, sd = 1) ## normal random x2 <- runif(n = n, min = 0, max = 10) ## uniform random ``` ```{r rnorm_hist_simple, fig.width=7, fig.height=4, out.width="60%", fig.cap="(too) simple istogram of normally distributed random numbers."} hist(x = x1, breaks = 100) ``` ```{r rnorm_hist, fig.width=7, fig.height=4, out.width="60%", fig.cap="Histogram of normally distributed random numbers. "} hist(x = x1, breaks = 100, las = 1, col = "palegreen", main = "Random normal numbers", xlab = "Value" ) ``` ```{r runif_hist, fig.width=7, fig.height=4, out.width="60%", fig.cap="Histogram of uniformly distributed random numbers. "} hist(x = x2, breaks = 10, las = 1, col = "cyan", main = "Uniformly distributed random numbers", xlab = "Value" ) ``` ```{r x1_statistics} summary(x1) ``` ### Exercice 2.2 Créez une matrice `m1` qui contient les 10 premières valeurs de `x1` (colonne 1 de `m1`) et les 10 dernières valeurs de `x2` (colonne 2 de `m1`). Verifiez qu'il s'agit bien d'une matrice et affichez ses dimensions. **Fonctions recommandées :** `matrix()`, `class()`, `dim()`, `cbind()`,… #### Solution ```{r} ## Prepare an empty matrix m1 <- matrix(nrow = 10, ncol = 2) ## Assign values to the first column m1[, 1] <- head(x = x1, n = 10) ## equivalent to m1[, 1] <- x1[1:10] ## Assign values to the second column m1[, 2] <- tail(x = x1, n = 10) ## Print the result rounded to 3 decimals print(round(m1, digits = 3)) ## Check it is a matrix and print its dimensions class(m1) dim(m1) ## Alternatively and more quickly you may use the function cbind() to directly generate the matrix m1 in one command line m1 <- cbind(x1[1:10], x2[91:100]) print(round(m1, digits = 3)) class(m1) dim(m1) ``` ### Exercice 2.3 Créez une matrice `m2` qui contient - les 16ème, 51ème, 79ème, 31ème et 27ème valeurs de `x1` (colonne 1 de `m2`) et - les 30ème, 70ème, 12ème, 49ème et 45ème de `x2` (colonne 2 de `m2`). **Fonctions recommandées :** `matrix()`, `cbind()`,… #### Solutions ```{r} m2 <- matrix(nrow = 5, ncol = 2) ## Assign values m2[, 1] <- x1[c(16, 51, 79, 31, 27)] m2[, 2] <- x2[c(30, 70, 12, 49, 45)] ## Print the result print(m2) ## or more directly using the cbind() function without creating an empty matrix first m2 <- cbind(x1[c(16, 51, 79, 31, 27)], x2[c(30, 70, 12, 49, 45)]) print(m2) ``` ### Exercice 2.4 Concaténez à la suite (l’une en dessous-de l’autre) les matrices `m1` et `m2`, afin d’obtenir une nouvelle matrice `m3`. Quelles sont les dimensions (nombre de lignes et de colonnes) de `m3` ? **Fonctions recommandées :** `rbind()`, `cbind()`, `dim()`, `ncol()`, `nrow()`, `str()` #### Solutions ```{r} m3 <- rbind(m1, m2) print(m3) dim(m3) nrow(m3) ncol(m3) ``` #### pour aller plus loin: Les operateurs classiques (+, -, *) font des operations terme a terme si les deux matrices ont la meme taille, par exemple: `m1 + rbind(m2,m2)` alors que `m1+m2` retourne une erreur Pour faire du calcul matriciel, on utilise un operateur specifique `%*%` pour multiplier. La premiere matrice a autant de lignes qu'il y a de colonnes dans la 2eme matrice. La matrice resultante a autant de lignes que la 1ere matrice et de colonnes que la 2eme matrice. *Quelques exemples:* ```{r} m4 <- m1 %*% t(m2)# m1 avec la transposee de m2 qui a 2 lignes et 5 colonnes dim(m4) m5 <- m1 %*% t(m1)# m1 avec sa propre tranposee dim(m5) m6 <- t(m1) %*% m1 # ou l'inverse! Pas de commutativite sur ces calculs! dim(m6) ``` ## Exercice 3 **Fonctions recommandées :** -`data(WorldPhones)`, - `class()`, - `dim()`, - `rownames()`, - `colnames()`, - `str()`, - `sum()`, - `apply()`, - `names()`, - `max()`, - `min()`, - `which()`, - `which.max()`, - `which.min()` - ... ### Exercice 3.1 Importez dans votre session R les données nommées `WorldPhones` (pré-existantes dans R). Affichez le contenu de la variable `WorldPhones`. Quelle est sa structure et sa classe ? ### Exercice 3.2 Calculez le nombre total de numéros de téléphone attribués : a. au cours des différentes années (vecteur `nbrTelAn`) b. pour chaque continent (vecteur `nbrTelCont`) ### Exercice 3.3 Quel est le continent qui a le plus / moins de numéros attribués ? ### Exercice 3.4 Dans combien de continents y a-t-il plus de : 20.000, 50.000 et 200.000 numéros de téléphone attribués ? ### Solutions exercice 3 ```{r} data("WorldPhones") print(WorldPhones) class(WorldPhones) dim(WorldPhones) rownames(WorldPhones) colnames(WorldPhones) ``` ```{r} # nombre d'appels pour le continent 1 sum(WorldPhones[,1]) # nombre d'appels pour le continent 2 sum(WorldPhones[,2]) # etc. # pour aller plus rapidement : # repetition de la commande sum() en ligne = marginal sums per year nbrTelAn <- apply(WorldPhones, 1, sum) print(nbrTelAn) # repetition de la commande sum() en colonne = marginal sums per continent nbrTelCont <- apply(WorldPhones, 2, sum) print(nbrTelCont) ``` ```{r} # continent avec le plus de numeros names(nbrTelCont)[which(nbrTelCont == max(nbrTelCont))] # continent avec le moins de numeros names(nbrTelCont)[which(nbrTelCont == min(nbrTelCont))] # ou plus simplement avec la commande which.max() ou which.min() names(which.max(nbrTelCont)) names(which.min(nbrTelCont)) # ## Another way: with head and tail tail(sort(nbrTelCont), n = 1) head(sort(nbrTelCont), n = 1) ``` ```{r} sum(nbrTelCont > 20000) sum(nbrTelCont > 50000) sum(nbrTelCont > 200000) ``` ## Exercice 4 - Téléchargez le fichier [minitable1.txt](https://du-bii.github.io/module-3-Stat-R/stat-R_2020/data/minitable1.txt). Il est également disponible dans `/shared/projects/dubii2020/data/module3/seance1/` - Ouvrez-le avec un éditeur de texte ou un calculateur pour identifier sa structure et les cases non remplies. - Importez le dans R dans un objet `test.data` et vérifiez sa structure et son contenu. - Comment les données manquantes ont-elles été lues ? Remplacez-les par NA si elles n’ont pas été lues comme une donnée manquante. - Déplacez la colonne 1 en dernière colonne. - Renommez les colonnes colA, colB, colC, colD, colE... dans le nouvel ordre obtenu. - Supprimez la deuxième ligne. - Ajoutez une colonne de valeurs numériques obtenues en divisant les valeurs de la `colE` par les valeurs de la `colA`. - Dans la `colC`, remplacez les valeurs `toto` par `tata` et vice-versa. - Dans la `colC`, remplacez les lettres $t$ par des $m$. - Triez le dataframe par ordre croissant de la colonne `colE`. - Convertissez la colonne `colB` en valeurs numériques. - Dans un vecteur sumcolA, calculer la somme de la `colA`. - Faites un sous-dataframe `test.data2` contenant les lignes pour lesquelles les éléments de la `colE` sont inférieurs ou égaux à ceux de la `colA`. - Sauvegardez le data frame `test.data2` en fichier texte avec des `;` comme séparateurs de champs. **Fonctions recommandées dans l’ordre :** `read.table()`, `str()`, `is.na()`, `paste()`, `gsub()`, `order()`, `as.integer()`, `sum()`, `subset()`, `write.table()` ### Solutions exercice 4 ```{r} test.data <- read.table("/shared/projects/dubii2020/data/module3/seance1/minitable1.txt", sep = "\t", header = T, stringsAsFactors = F, fill = T) str(test.data) test.data is.na(test.data) # la case [3,5] n'est pas lue comme une donnee manquante -> cf. help de read.table() pour l'argument na.strings de la fonction read.table(): par defaut, seules les donnees "NA" d'un vecteur de caracteres sont considerees comme des donnees manquantes, alors que pour les logical, integer, numeric and complex fields les cases vides sont bien lues comme des donnees manquantes # pour lire la case [3,5] comme une donnee manquante, on aurait pu taper la commande: #test.data <- read.table("test.txt",sep="\t", header=T, stringsAsFactors=F, fill=T, na.strings="") test.data[3,5] <- NA test.data <- test.data[,c(2:5,1)] names(test.data) <- paste("col", LETTERS[1:5], sep ="") test.data <- test.data[-2,] test.data$EbyA <- test.data$colE/test.data$colA test.data[which(test.data$colC == "toto"),3] <- "otot" test.data[which(test.data$colC == "tata"),3] <- "toto" test.data[which(test.data$colC == "otot"),3] <- "tata" test.data$colC <- gsub("t","m",test.data$colC) test.data <- test.data[order(test.data$colE),] test.data$colB <- as.numeric(test.data$colB) sumcolA <- sum(test.data$colA,na.rm = T) test.data2 <- subset(test.data, colE <= colA) # ou test.data2 <- test.data[which(test.data$colE <= test.data$colA),] write.table(test.data2,file = "test2.txt", sep = ";", col.names = T,quote = F) ``` ## Exercice 5 Sauvegardez dans une liste `session1_list` tous les objets créés pendant les exercices 1 à 4 en les mettant dans des sous-listes correspondant à chaque exercice que vous nommerez `exo1`, `exo2`, `exo3` et `exo4`. **Fonctions recommandées :** `list()`, `names()` ### Solutions exercice 5: ```{r} ls()# check what is in your environment session1_list <- list() #stepwise for exo1: exo1 <- list("matIdentite" = matIdentite, "matAleatoire" = matAleatoire) session1_list[["exo1"]] <- exo1 # or in one command for exo2: session1_list[["exo2"]] <- list("x1" = x1, "x2" = x2, "m1" = m1, "m2" = m2, "m3" = m3, "m4" = m4, "m5" = m5, "m6" = m6) # you may also add elements to the list one by one, either or not giveing the name directly with different ways: session1_list[["exo3"]] <- list("WorlPhones" = WorldPhones) session1_list[[3]][["nbrTelAn"]] <- list(nbrTelAn) session1_list[[3]][3] <- list(nbrTelCont) names(session1_list[[3]])[3] <- "nbrTelCont" # or all elements for exo4 and naming it after: session1_list[[4]] <- list("test.data" = test.data, "sumcolA" = sumcolA, "test.data2" = test.data2) names(session1_list)[4] <- "exo4" ```