« Questions-r » : différence entre les versions
Ligne 39 : | Ligne 39 : | ||
A une époque, ça s'appelait pas pivot_wider() et pivot_longer() mais spread() et gather() | A une époque, ça s'appelait pas pivot_wider() et pivot_longer() mais spread() et gather() | ||
(mais c'était moins explicite comme nom) | (mais c'était moins explicite comme nom) | ||
'''Question de martiste''' | |||
J'arrive à aller chercher différents fichiers excel et à les "rbind" avec ça : | |||
temp_data <- list.files(path_to_files, pattern = '*.xlsx') %>% | |||
purrr::map_dfr(~readxl::read_xlsx(paste0(path_to_files, .x))) | |||
Maintenant, chaque fichier contient un champ "date"... Comment pourrais-je faire pour qu'au lieu de les ajouter à la suite les uns des autres, je les joignent par date ? | |||
J'ai 15 fichiers excels, que, jusqu'ici, je lis à la suite et colle les uns après les autres. Ce qu'il me faudrait, à présent, c'est une sorte de 'recast' en fait, mais avec comme identifiant commun une variable lubridate. Pour qu'au lieux d'avoir une colonne qui m'indique le nom du site (= le nom du fichier en entrée) et une autre une valeur de température, je me retrouve avec un tableau 'large' avec comme nom de colonne chacune des stations contenant sa valeur de température. | |||
'''Réponse de defuneste et sduneme''' | |||
do.call et/ou pivot_wider [[https://tidyr.tidyverse.org/articles/pivot.html#wider]] | |||
C'est qu'est ce que j'avais essayé au départ, mais ça n'marchait pas... | |||
Parce que j'avais une variable 'id' qui empêchait le regroupement de se faire correctement | |||
<syntaxhighlight lang="r"> | |||
df %>% | |||
select(timestamp, station, temperature) %>% | |||
pivot_wider(names_from=station, values_from=temperature) | |||
</syntaxhighlight> | |||
'''Solution non-purrr''' | |||
Chargement des library / installation | |||
<syntaxhighlight lang="r"> | |||
# install.packages("tidyverse") | |||
# install.packages("readxl") | |||
# les données sont dans un répertoire enfant: | |||
file_path <- list.files(path = "data", | |||
pattern = "*.xlsx", | |||
full.names = TRUE) # pas mal pour t'economiser un paste0 | |||
# une fonction qui lis un xlss | |||
lis_un_fichier_excel <- function(un_chemin_de_fichier) { | |||
readxl::read_xlsx(un_chemin_de_fichier) | |||
} | |||
# on teste | |||
lis_un_fichier_excel(file_path[1]) | |||
# une liste de df | |||
une_liste_de_tableau <- lapply(file_path, "lis_un_fichier_excel") | |||
# on ajoute les lignes | |||
mon_tableau_en_tidy <- do.call("rbind", une_liste_de_tableau) | |||
mon_tableau_en_tidy | |||
</syntaxhighlight> | |||
== Complétion des données d'une série temporelle == | == Complétion des données d'une série temporelle == |
Version du 10 janvier 2022 à 17:27
Cette page reprend la "substantifique moelle" des échanges ayant eu lieu sur le channel questions-r du Discord d'ISIG.
Manipulation d'un nom de fichier
Question posée par martiste
Comment récupérer un morceau du nom du fichier ? Par exemple à partir de "zarsqdfqsfd33vezre20618402.csv on veut récupérer 20618402.
Réponse de lvaudor et sdunesme
Combi des expressions régulières et de la fonction basename():
"za/rsqd/fqsfd33vezre20618402.csv" %>%
basename() %>% # donne "fqsfd33vezre20618402.csv"
stringr::str_replace(".csv","") %>% # donne "fqsfd33vezre20618402"
stringr::str_extract(pattern=".{8}(?=\\.$)") # donne "20618402"
Reformattage d'un fichier en "longueur"
Question de mcottet
J'ai une bdd avec plusieurs variables catégorielles qui portent une info similaire (est ce que les gens ont vu l'espèce A un peu, bcp, passionnément, à la folie ; idem pour l'espèce B, C...). Et j'aimerais faire une somme pour savoir combien de fois l'individu 1 a répondu un peu à l'ensemble des espèces proposées, bcp à l'ensemble des espèces... il y a 18 espèces (donc 18 variables)
Réponse de lvaudor
tidyr::pivot_longer()
Dans ta version de BDD, tu as 18 variables différentes Mais en fait, tu pourrais considérer que ces 18 colonnes correspondent en fait à 2 colonnes (une colonne "espece" et une colonne "valeur") Tu cherches de ce fait à répéter l'ensemble des autres colonnes (toutes celles qui ne font pas partie des 18 sus-mentionnées) 18 fois, mais "en hauteur". Du coup, ton tableau qui est "plutôt large" devient un tableau "plutôt long"
Dans l'appel à la fonction, tu précises quelles sont les variables que tu récupères pour les mettre "en long" et toutes les autres, il les répète à l'identique autant de fois que nécessaire
A une époque, ça s'appelait pas pivot_wider() et pivot_longer() mais spread() et gather() (mais c'était moins explicite comme nom)
Question de martiste
J'arrive à aller chercher différents fichiers excel et à les "rbind" avec ça : temp_data <- list.files(path_to_files, pattern = '*.xlsx') %>%
purrr::map_dfr(~readxl::read_xlsx(paste0(path_to_files, .x)))
Maintenant, chaque fichier contient un champ "date"... Comment pourrais-je faire pour qu'au lieu de les ajouter à la suite les uns des autres, je les joignent par date ?
J'ai 15 fichiers excels, que, jusqu'ici, je lis à la suite et colle les uns après les autres. Ce qu'il me faudrait, à présent, c'est une sorte de 'recast' en fait, mais avec comme identifiant commun une variable lubridate. Pour qu'au lieux d'avoir une colonne qui m'indique le nom du site (= le nom du fichier en entrée) et une autre une valeur de température, je me retrouve avec un tableau 'large' avec comme nom de colonne chacune des stations contenant sa valeur de température.
Réponse de defuneste et sduneme
do.call et/ou pivot_wider [[1]]
C'est qu'est ce que j'avais essayé au départ, mais ça n'marchait pas... Parce que j'avais une variable 'id' qui empêchait le regroupement de se faire correctement
df %>%
select(timestamp, station, temperature) %>%
pivot_wider(names_from=station, values_from=temperature)
Solution non-purrr
Chargement des library / installation
# install.packages("tidyverse")
# install.packages("readxl")
# les données sont dans un répertoire enfant:
file_path <- list.files(path = "data",
pattern = "*.xlsx",
full.names = TRUE) # pas mal pour t'economiser un paste0
# une fonction qui lis un xlss
lis_un_fichier_excel <- function(un_chemin_de_fichier) {
readxl::read_xlsx(un_chemin_de_fichier)
}
# on teste
lis_un_fichier_excel(file_path[1])
# une liste de df
une_liste_de_tableau <- lapply(file_path, "lis_un_fichier_excel")
# on ajoute les lignes
mon_tableau_en_tidy <- do.call("rbind", une_liste_de_tableau)
mon_tableau_en_tidy
Complétion des données d'une série temporelle
Question de Théo
j'ai une série temporelle de hauteurs d'eau à pas de temps 15 min, mais il y a plein de trous dedans et je voudrais la compléter, en ajoutant l'heure manquante, et la hauteur d'eau manquante (moyenne de t-15 et t+15 si t est la valeur manquante).
Réponse de martiste
il faut que tu commences par te créer un index de temps à l'intervalle voulu, depuis ta première valeur jusqu'à la dernière, à laquelle tu joins ton jeu de données réel. Les trous vont alors apparaître en 'NA'. Et si tu n'as que quelques trous de jamais plus de 2 intervalles, tu peux utiliser la fonction zoo::na_approx() qui fera l'affaire...
library(dplyr)
H_15_raw = readr::read_delim("H_15MIN_SB_CSV.csv", delim = ";") %>%
mutate(Date = lubridate::dmy_hm(Date))
timestamp = seq(from = min(H_15_raw$Date), to = max(H_15_raw$Date), by = "15 min") %>%
as_tibble() %>%
select(Date = value)
H_15_corr =
left_join(timestamp, H_15_raw, by ="Date") %>%
mutate(H_CM_SB_BH = zoo::na.approx(H_CM_SB_BH))
Regarde ?na.approx pour plus de détails sur son fonctionnement
Programmation fonctionnelle
Question de martiste
J'ai un jeu de données avec des températures à pas de temps horaire, le tout sur plusieurs années (et plusieurs stations). J'aimerais connaître, pour chaque année et pour chaque station, le nombre de séquences où la température a dépassé les 19°C, ainsi que la durée de la séquence la plus longue. Avec une boucle, j'aurais créé un nouveau champ pour lequel j'incrémente une valeur de séquence à chaque fois que T>19 et que T-1<19. Je regarde la valeur max de ma séquence, et la longueur max de ces séquences également. Mais comment faire sans boucle ?
Réponse de lvaudor
get_sequences=function(donnees, Temp){
result = donnees %>%
mutate(change = case_when(chmes_valeur > Temp & lag(chmes_valeur <= Temp) ~ 1,
TRUE ~ 0)) %>%
mutate(sequence = case_when(chmes_valeur > Temp ~ cumsum(as.numeric(change)),
TRUE ~ NA_real_))
return(result)
}
result= dat %>%
group_by(chmes_coderhj,chmes_anneebiol) %>%
tidyr::nest() %>%
mutate(data=purrr::map(data,get_sequences,Temp=19)) %>%
tidyr::unnest(cols=c(data))
ou bien (autre syntaxe possible pour map), comme ça:
result= dat %>%
group_by(chmes_coderhj,chmes_anneebiol) %>%
tidyr::nest() %>%
mutate(data=purrr::map(data,~get_sequences(.,Temp=19))) %>%
tidyr::unnest(cols=c(data))
Différence entre avec et sans '~' devant la fonction:
- soit map attend le nom d'une fonction
- Dans ce cas l'argument sur lequel il itère doit être le premier argument de la fonction
- soit map attend une formule (précédée d'un "~", donc)
- Dans ce cas il y a un peu plus de souplesse sur la position de l'argument sur lequel on itère
Tu pourrais dire ~ get_sequences(data, Temp=.) pour itérer sur une variable qui donnerait un seuil de température, par exemple et non plus itérer sur le premier argument, "data"
Si tu donnes le nom de la fonction, tu peux quand-même rajouter des arguments "secondaires" mais tu les rajoutes directement comme arguments optionnels à la fonction "map", pas dans un simulacre d'appel à la fonction que tu itères
L'utilisation des formules c'est un subtilité introduite pour gérer les fonctions qui n'ont pas les arguments "dans l'ordre le plus pratique au regard de ce qu'on souhaite faire".
Question complémentaire
Et, pour finir, si je veux en sortir 1) le nombre total de séquences, et 2) la durée de la séquence la plus longue, est-ce que tu me conseilles de l'inclure dans la fct 'get_sequences', ou de repartir des résultats de cette fonction et d'alimenter une nouvelle ?
Réponse Le nombre de total de séquences et la durée de la séquence la plus longue, ce sont des "agrégations" de ce que tu as fait avant. Or, il est possible que tu veuilles utiliser les résultats de get_sequences tels quels (par exemple pour des graphiques). Donc il vaut mieux faire en sorte que cette agrégation ait lieu dans un second temps => définition d'une nouvelle fonction par exemple.
En revanche, je pense que tu pourrais faire tourner ce calcul avant de "unnest" car tes séquences n'ont pas des identifiants uniques en l'état (les identifiants ne sont uniques que au sein des sous-jeux de données imbriqués)
avec n_distinct, je chope le nb total de séquences Pour choper la durée de la séquence la plus longue, tu suggérerais quoi ?
... %>%
group_by(sequence) %>%
summarise(duration=max(time)-min(time)) %>%
ungroup() %>%
mutate(n_sequences=n()) %>%
top_n(1,duration)
le mutate (n_sequences=n()) te rajoute le nombre de séquences sans résumer (ça répète autant de fois que nécessaire la valeur n_sequences) du coup après quand tu gardes que la top duration t'as quand-même l'info qu'il te faut
Annonces de nouveau packages R
banqueHydro
[[2]]
Pour "scraper" les données du site banqueHydro (en attendant l'API Hubeau [[3]]).
Annonces de formations/tutos/conf
* [SIGR] recommandé par defuneste * [command line utilities in R] recommandé par defuneste