🧐Cliquer ici pour les diapos de cours
Pour cette partie vous tiendrez le rôle d’un gérant de zoo, qui s’interroge sur la comptabilité de son établissement… Vous serez ainsi amenés à manipuler différents tableaux de données, de faire des petits calculs, quelques graphiques de synthèse, etc.
1. Table nutritionnelle par espèce
Le dossier fourni ici sous forme de zip ou ici contient un sous-dossier “fiches_especes”.
Ouvrez quelques-unes de ces fiches. Elles sont toutes organisées selon un modèle commun. Nous souhaiterions mettre toutes les informations qu’elles contiennent sous forme d’un seul et même tableau, dont les premières lignes ressembleraient à ceci:
1.1 Vérifier l’existence d’une fiche pour une espèce
Compléter le code pour vérifier l’existence d’un fichier (dans le
répertoire du serveur “data/fiches_especes/”) pour une espèce donnée. Un
message sera envoyé en cas de non-existence du fichier. Vous pourrez
utiliser la fonction file.exists()
qui teste l’existence
d’un fichier au chemin indiqué en argument.
espece="renard blanc"
chemin=paste0("data/fiches_especes/fiche_",____,".csv")
if(___){
print("La fiche pour cette espèce n'existe pas")
}
espece="renard blanc"
chemin=paste0("data/fiches_especes/fiche_",espece,".csv")
if(!file.exists(chemin)){
print("La fiche pour cette espèce n'existe pas")
}
Vous pouvez également tester ce qui se passe quand il y a effectivement une fiche pour l’espèce considérée (par exemple, “antilope”).
1.2 Lire une fiche pour une espèce
Voici l’allure d’une fiche brute:
Complétez la fonction suivante pour qu’à partir d’un nom d’espèce (par exemple, “antilope”), la fonction lise la fiche correspondante et renvoie son contenu brut:
lit_fiche=function(______){
fiche=readr::read_csv2(paste0("data/fiches_especes/fiche_",
espece,
".csv"), col_names=FALSE)
return(fiche)
}
lit_fiche("antilope")
lit_fiche=function(espece){
fiche=readr::read_csv2(paste0("data/fiches_especes/fiche_",
espece,
".csv"), col_names=FALSE)
return(fiche)
}
fiche=lit_fiche("antilope")
1.3 Lire et nettoyer une fiche: compréhension des opérations
Examinez ces transformations pour la fiche :
fiche_nettoyee=fiche %>%
tidyr::separate(X1,":", into=c("categorie","quantite")) %>%
mutate(quantite=str_replace_all(quantite,"[\\s%kg]","")) %>%
mutate(quantite=as.numeric(quantite))
fiche_nettoyee
1.4 Lire, nettoyer et reformater une fiche : mise en fonction
Chaque fiche comprend dans une même colonne le poids total d’aliment (quand categorie== “quantite”) et les proportions des différents types d’aliments (toutes les autres lignes). C’est en fait un peu mélanger les torchons et les serviettes!!
On va plutôt essayer de revenir dans une logique “tidy” (où une colonne=une variable) et d’indiquer pour chaque catégorie d’aliment la proportion d’une part et le poids d’autre part…
C’est ce que permettent de réaliser les lignes suivantes, ici par exemple pour la fiche “antilope”:
# lecture de la fiche brute
fiche=readr::read_csv2(paste0("data/fiches_especes/fiche_",
"antilope",
".csv"), col_names=FALSE)
fiche
# nettoyage
fiche_nettoyee=fiche %>%
tidyr::separate(X1,":", into=c("categorie","quantite")) %>%
mutate(quantite=str_replace_all(quantite,"[\\s%kg]","")) %>%
mutate(quantite=as.numeric(quantite))
fiche_nettoyee
# recup quantite totale
quantite_totale=fiche_nettoyee %>%
filter(categorie=="quantite") %>%
pull(quantite)
quantite_totale
## [1] 3
# calcul du proportion et du poids pour chaque catégorie d'aliment
fiche_formatee=fiche_nettoyee %>%
filter(categorie!="quantite") %>%
mutate(proportion=quantite/100) %>%
mutate(poids=quantite_totale*proportion) %>%
select(-quantite)
fiche_formatee
Pour l’instant, la fonction lit_fiche()
lit la fiche et
la renvoie dans sa forme brute: il faut donc ajouter
quelques lignes pour que cette fonction renvoie la fiche
formatée (comme on l’attend vu son nom…). La librairie
tidyverse
est déjà chargée dans l’environnement. Je vous
laisse compléter la fonction en vous inspirant des
lignes de code ci-dessus:
# Définir la fonction:
formate_fiche=function(espece){
fiche=lit_fiche(espece)
fiche_formatee=_____
________
________
________
return(fiche_formatee)
}
# Utiliser la fonction:
formate_fiche("lion")
# Définir la fonction:
formate_fiche=function(espece){
fiche=lit_fiche(espece)
fiche_nettoyee=fiche %>%
tidyr::separate(X1,":", into=c("categorie","quantite")) %>%
mutate(quantite=str_replace_all(quantite,"[\\s%kg]","")) %>%
mutate(quantite=as.numeric(quantite))
# recup quantite totale
quantite_totale=fiche_nettoyee %>%
filter(categorie=="quantite") %>%
pull(quantite)
# calcul du proportion et du poids pour chaque catégorie d'aliment
fiche_formatee=fiche_nettoyee %>%
filter(categorie!="quantite") %>%
mutate(proportion=quantite/100) %>%
mutate(poids=quantite_totale*proportion) %>%
select(-quantite)
return(fiche_formatee)
}
# Utiliser la fonction:
formate_fiche("lion")
1.5 Rajouter une option “sommer” pour l’utilisateur
Nous souhaiterions maintenant avoir à notre disposition une fonction
import_fiche()
, basée sur formate_fiche()
, qui
permettrait à l’utilisateur de préciser s’il souhaite les
résultats par catégorie (par exemple en fixant un argument
sommer=FALSE
) ou si au contraire il souhaite obtenir
les résultats sommés (dans ce cas la valeur d’argument
serait sommer=TRUE
).
On obtiendrait ainsi, pour un appel
import_fiche("singe",sommer=FALSE)
Et pour l’appel
import_fiche("singe",sommer=TRUE)
Complétez le code de cette fonction import_fiche()
et
testez-la (vous pouvez faire varier la valeur par défaut de l’argument
sommer
pour en voir l’effet):
import_fiche=function(fiche,______){
fiche=formate_fiche(fiche)
______
______
}
import_fiche("suricate",sommer=TRUE)
import_fiche("suricate",sommer=FALSE)
import_fiche=function(fiche,sommer=FALSE){
fiche=formate_fiche(fiche)
if(sommer){
fiche=fiche %>%
summarise(proportion=sum(proportion),
poids=sum(poids))
}
return(fiche)
}
import_fiche("suricate",sommer=TRUE)
import_fiche("suricate",sommer=FALSE)
2. Table nutritionnelle pour l’ensemble des espèces
2.1 Itérer une fonction sur l’ensemble des espèces
Voici les espèces pour lesquelles vous disposez d’une fiche d’alimentation:
alim_init=tibble::tibble(espece=c("antilope","autruche","chameau","lion","elephant","fennec",
"flamand rose","girafes","guepard","hyene","iguane","lion",
"loup","lynx","otarie","ours polaire","ours","panthere","perroquets",
"phacochere","rhinoceros","serpent","singe","suricate","tigre","tortue"))
Utilisez purrr
pour itérer la fonction
formate_fiche()
sur l’ensemble du vecteur
alim_init$espece
de manière à obtenir une tibble
unique et “plate” en sortie.
La table alim_init
et la fonction
formate_fiche
font déjà partie de
l’environnement.
quanti=________(alim_init$espece, .f=___)
quanti
quanti=map_df(alim_init$espece,.f=formate_fiche)
quanti
2.2 Itérer une fonction DANS une table
Maintenant, nous allons réaliser la même opération en
conservant le nom de l’espèce c’est-à-dire que nous allons
compléter la table alim
elle-même en utilisant la fonction
mutate()
de dplyr :
La table alim_init
et la fonction
formate_fiche
font déjà partie de
l’environnement.
alim = alim_init %>%
mutate(quanti=map(.x=___,.f=___))
head(alim)
alim = alim_init %>%
mutate(quanti=map(.x=espece,.f=formate_fiche))
head(alim)
2.3 Aplatir une nested tibble
Observez la structure de la table alim
. La colonne
quanti
est une colonne-liste (et non une
simple colonne-vecteur), c’est-à-dire qu’elle contient des éléments -ici
des tables- imbriquées (nested) dans la colonne.
Remettez cette table “à plat”.
alim = alim_init %>%
mutate(quanti=map(.x=espece,.f=formate_fiche)) %>%
_____
head(alim)
3 Budget alimentaire “grossier”
- Cette partie (et les deux prochains exercices) ne relèvent pas purement de la programmation fonctionnelle mais vous permettront d’apprécier le fruit de votre travail des parties 1 et 2 en l’intégrant dans un projet (un tout petit peu) réaliste. *
On dispose maintenant d’une table d’alimentation par individu
d’une espèce. Pour gérer l’approvisionnement en nourriture du
zoo, il faut savoir quelles quantités totales des différents types de
nourriture sont nécessaires. On doit donc “croiser” l’information
concernant les besoins d’UN individu d’UNE espèce (table
alim
) avec les effectifs pour chaque espèce (table
effectifs
) et les prix au kilo de chaque catégorie
d’aliment (table prix
):
head(effectifs)
prix
3.1 Réaliser la jointure des tables de nutrition, d’effectifs et de prix
Créer la table alim_tot
en réalisant la jointure de
alim
et effectifs
puis calculer la nouvelle
variable poids_tot
correspondant au poids total de chaque
type d’aliment par espèce.
Les tables alim
, effectifs
, et
prix
font déjà partie de l’environnement.
Si vous avez besoin d’un petit (ra)fraîchissement de vos idées sur la notion de jointure, vous pouvez aller voir ici
alim_tot = alim %>%
full_join(___, by = "espece") %>%
full_join(___, by = "___")
mutate(poids_tot = ___*___,
prix_tot = ___*___)
alim_tot
alim_tot= alim %>%
full_join(effectifs, by = "espece") %>%
full_join(prix, by = "categorie") %>%
mutate(poids_tot = poids*effectifs,
prix_tot = prix*poids_tot)
alim_tot
3.2 Représenter les résultats graphiquement
Produisez un graphique en “colonnes” correspondant aux prix totaux d’aliment par espèce (en faisant varier la couleur de remplissage par type d’aliment).
La table alim_tot
fait déjà partie de
l’environnement.
ggplot(alim_tot, aes(x=___, y=___,fill=___))+
geom_col()+
coord_flip()
ggplot(alim_tot, aes(x=espece, y=prix_tot,fill=categorie))+
geom_col()+
coord_flip()
4. Prix détaillés des aliments
Examinez la table prix_detailles
.
Cette table fournit, pour un aliment:
- son prix de base au kilo
- la catégorie d’aliment auquel il correspond (viande, poisson, fruits, légumes ou fourrages)
- la réduction (en %) en fonction du volume des achats. Par exemple, le prix du premier kilo de viande de mouton acheté est 7.46 euros. Le deuxième kilo voit son prix réduit de 1.1%, son prix est donc de (100 − 1.1)/100 ∗ 7.46 = 7.38 euros. Le troisième kilo voit son prix réduit de 1.1%, son prix est donc de (100-1.1)/100 * 7.38 = 7.30.
- le prix minimal au kilo en dessous duquel il ne peut pas descendre quel que soit le volume acheté.
4.1 Calcul du prix du n-ième kilo d’un aliment
Ecrire une fonction prix_kilo_n()
qui a pour entrée
nom_aliment
: le nom d’un alimentn
un “numéro” de kilo
et qui a en sortie le prix du n-ième kilo de cet aliment.
Il s’agit en fait d’une suite géométrique telle que le n-ième kilo d’un aliment a pour prix prix_base*((100-reduc)/100)^(n-1)
La table prix_detailles
fait déjà partie de
l’environnement.
prix_kilo_n=function(nom_aliment,n){
________
________
________
return()
}
prix_kilo_n("sardine",15) # devrait retourner 5.790952
prix_kilo_n=function(nom_aliment,nieme){
resultat=prix_detailles %>%
filter(aliment==nom_aliment) %>%
mutate(prix=prix_base*((100-reduc)/100)^(nieme-1)) %>%
mutate(prix=max(prix,prix_min)) %>%
pull(prix)
return(resultat)
}
prix_kilo_n("sardine",15) # devrait retourner 5.79 (si arrondi)
prix_kilo_n("sardine",150) # devrait retourner 3.00 (si arrondi)
4.2 Calcul du prix de n kilos d’un aliment
Ecrire une fonction prix()
qui a pour entrée
nom_aliment
: le nom d’un alimentn
un nombre total de kilos
et qui a en sortie le prix de n kilos de cet
aliment. Vous pourrez pour cela réutiliser la fonction
prix_kilo_n()
que vous avez écrite dans l’exercice
précédent.
La table prix_detailles
et la fonction
prix_kilo_n
font déjà partie de
l’environnement.
prix=function(nom_aliment,n){
________
________
return()
}
prix("sardine",50) # devrait retourner 272.84 (si arrondi)
prix=function(nom_aliment,n){
resultat=map_dbl(1:n,
~prix_kilo_n(nom_aliment,.x)) %>%
sum()
return(resultat)
}
prix("sardine",50) # devrait retourner 272.84 (si arrondi)