Fonction attribution avec conditions entre deux df

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

July Elsa
Messages : 8
Enregistré le : 20 Juin 2023, 18:34

Fonction attribution avec conditions entre deux df

Messagepar July Elsa » 02 Déc 2024, 10:06

Bonjour à tous,

Je vais essayer d'expliquer clairement mon problème.
Je possède deux df :
- "effech" qui correspond à un effort d'échantillonnage mis en place pour suivre des individus par caméra-pièges. Il consiste en un nom de caméra ("cam") et une heure de début d'échantillonnage ("start"). Ce df comporte quelques 30.000 lignes (20 caméras, 1 mois de suivi et une photo toutes les heures).
A ce df j'ai d'ores et déjà associé une aire de détection propre, d'une part, à chaque caméra (calculée sur le terrain), et, d'autre part, entre le jour et la nuit (car la caméra détecte de moins loin la nuit), avec la fonction suivante :

Code : Tout sélectionner

for(i in 1: nrow(effech)){
  if((hour(effech[i,"start"]) %in% c(19:23,0:6))) {
    effech[i,"aire"] <- airejrnuit$aire[which(airejrnuit$condition=="nuit"&airejrnuit$cam==effech[i, "cam"])]
  } else {
    effech[i,"aire"] <- airejrnuit$aire[which(airejrnuit$condition=="jour"&airejrnuit$cam==effech[i, "cam"])]
  }
}

avec airejrnuit un autre df avec des valeurs d'aire de détection pour chaque caméra en fonction du jour ou de la nuit ("condition").

- "airebrume" correspond à des aires de détection réduites, dans certains intervalles de temps, pour certaines caméras. En fait, l'équipe a, à la main, noté les intervalles de temps lorsqu'il y avait du brouillard, pour chaque caméra et attribué une autre distance de détection. Ce df est composé de "cam" (nom de caméra), "heure_debut" (début de l'intervalle de temps), "heure_fin" (fin de l'intervalle de temps) et "aire" (aire réduite avec la brume).

Ce que je veux faire :
À partir de mon df effech, je voudrais attribuer, dans une nouvelle colonne "aire_2", pour un effort d'échantillonnage particulier si celui-ci se trouve dans un intervalle de temps dans "airebrume", et pour la bonne caméra "cam", l'aire de "airebrume" du bon intervalle/caméra, et remettre l'aire initiale (de effech) si le temps considéré n'est pas dans un intervalle de temps avec de la brume.

Ce que j'ai essayé de faire :
J'ai d'abord essayé de reprendre l'idée de ma première fonction, qui marchait très bien, en la modifiant :

Code : Tout sélectionner

for(i in 1:nrow(effech)) {
   if(sum((effech[i,"start"]) %within% interval(ymd_hms(airebrume$date_debut), ymd_hms(airebrume$date_fin)))>0)
     {
     effech[i,"aire_2"] <- airebrume$aire[which(airebrume$cam==effech[i, "cam"])]
   } else {
     effech[i,"aire_2"] <- effech[i,"aire"]}
}

Je reçois ce message d'erreur : "Error in `[<-.data.frame`(`*tmp*`, i, "aire_2", value = c(3, : replacement has 4 rows, data has 1". Ce que je comprends, car il se retrouve avec les 4 lignes de airebrume pour la caméra correspondante à attribuer à une seule ligne dans effech et il ne sait pas laquelle choisir.
J'ai donc fait des tentatives comme ci-après pour essayer de lui faire comprendre de prendre la ligne de airebrume correspondante à l'intervalle testé, mais ça ne marche pas ("Error in x[[jj]][iseq] <- vjj : replacement has length zero") et ça prend beaucoup de temps. Pour ce dernier point, j'aimerai bien aussi qu'il ne teste déjà qu'avec le nom de la même caméra de effech dans airebrume pour que ça prenne moins de temps (mon jeu de données complet est très conséquent), si jamais....

Code : Tout sélectionner

for(i in 1:nrow(effech)) {
  for(t in 1:nrow(airebrume)){
    x<-interval(ymd_hms(airebrume[t,"date_debut"]), ymd_hms(airebrume[t,"date_fin"]))
  if(sum((effech[i,"start"]) %within% x)>0)
    {
    effech[i,"aire_2"] <- airebrume[x, "aire"][which(airebrume[x, "cam"]==effech[i, "cam"])]
  } else {
    effech[i,"aire_2"] <- effech[i,"aire"]}
}
}


Enfin, j'ai essayé de faire sans boucle mais je reçois un message indiquant que mes vecteurs de remplacement ne sont pas des multiples des vecteurs initiaux, et donc j'ai un résultat mais les valeurs sont attribuées "en boucle" et ça n'a pas de sens. À nouveau, je n'arrive pas à lui faire comprendre de prendre la ligne correspondant à l'intervalle testé.

Code : Tout sélectionner

effech %>% group_by(cam) %>% mutate(aire_2 =
  ifelse((sum(effech$start %within% interval(ymd_hms(airebrume$date_debut), ymd_hms(airebrume$date_fin))>0) & (airebrume$cam==effech$cam)), airebrume$aire, effech$aire))


Je sollicite donc votre aide ! j'espère que j'ai été claire (quitte à être longue...)

En vous remerciant par avance :)

Recréer les df effech et airebrume (versions simplifiées, et avec déjà l'aire jour/nuit pour effech) :

Code : Tout sélectionner

library(dplyr)
effech<-data.frame("cam" = c("1_2023", "1_2023", "1_2023", "1_2023", "2_2023", "2_2023", "2_2023", "2_2023"), "start" = c("2023-11-12 16:00:00", "2023-11-15 23:00:00", "2023-11-23 05:00:00", "2023-11-26 06:00:00", "2023-11-23 09:00:00", "2023-12-03 08:00:00", "2023-12-04 20:00:00", "2023-11-29 05:00:00"), "aire" = c(829, 207, 207, 207, 1624, 1624, 33, 33))
effech$start=as.POSIXct(effech$start, format="%Y-%m-%d %H:%M:%S", tz="GMT")

airebrume<-data.frame("cam" = c("1_2023", "1_2023", "1_2023", "1_2023", "2_2023", "2_2023", "2_2023", "2_2023"), "date_debut" = c("2023-11-15 22:01:00", "2023-11-21 22:01:00", "2023-11-17 13:01:00", "2023-11-25 22:01:00", "2023-11-28 22:01:00", "2023-11-30 22:01:00", "2023-12-03 13:01:00", "2023-12-04 22:01:00"), "date_fin" = c("2023-11-16 07:30:00", "2023-11-22 07:30:00", "2023-11-17 16:30:00", "2023-11-26 07:30:00", "2023-11-29 07:30:00", "2023-12-01 07:30:00", "2023-12-03 16:30:00", "2023-12-05 07:30:00"), "aire" = c(3, 3, 75, 3, 8, 8, 48, 8))
airebrume$date_debut=as.POSIXct(airebrume$date_debut, format="%Y-%m-%d %H:%M:%S", tz="GMT")
airebrume$date_fin=as.POSIXct(airebrume$date_fin, format="%Y-%m-%d %H:%M:%S", tz="GMT")

Logez Maxime
Messages : 3154
Enregistré le : 26 Sep 2006, 11:35

Re: Fonction attribution avec conditions entre deux df

Messagepar Logez Maxime » 09 Déc 2024, 10:10

Bonjour,

une solution possible :

Code : Tout sélectionner

library(lubridate)
l1 <- lapply(split(airebrume, airebrume$cam), function(x) interv <- interval(x$date_debut, x$date_fin))

l2 <- split(1:nrow(airebrume), airebrume$cam)

effech$aire2 <- NA_integer_
for (i in 1:nrow(effech)) {
  auxi <- which(effech$start[i] %within% l1[[effech$cam[i]]])
  if (length(auxi) < 0.5)
    next
  effech$aire2[i] <- airebrume$aire[l2[[effech$cam[i]]]][auxi]
}
Cordialement,
Maxime

July Elsa
Messages : 8
Enregistré le : 20 Juin 2023, 18:34

Re: Fonction attribution avec conditions entre deux df

Messagepar July Elsa » 10 Déc 2024, 08:47

Bonjour,

Merci beaucoup pour votre réponse qui semble fonctionner. Entre temps, j'ai sollicité l'aide de ChatGPT (étant vraiment à court de solution) et il m'a proposé le code ci-après, qui fonctionne (pour info). Il faudra voir laquelle des deux propositions a la vitesse d'exécution la plus rapide, car mon jeu de données final sera de plusieurs dizaines de milliers de données.

Code : Tout sélectionner

# Ajouter une colonne "aire_2" avec les valeurs par défaut
effech <- effech %>% mutate(aire_2 = aire)

# Parcourir les caméras dans airebrume
for (cam in unique(airebrume$cam)) {
  # Sous-ensembles pour une caméra spécifique
  sub_effech <- effech %>% filter(cam == !!cam)
  sub_airebrume <- airebrume %>% filter(cam == !!cam)
 
  # Appliquer les changements pour les intervalles
  for (i in seq_len(nrow(sub_airebrume))) {
    interval_brume <- interval(sub_airebrume$date_debut[i], sub_airebrume$date_fin[i])
    aire_brume <- sub_airebrume$aire[i]
   
    # Modifier les lignes de effech correspondant à cet intervalle
    effech <- effech %>%
      mutate(aire_2 = ifelse(
        cam == !!cam & start %within% interval_brume,
        aire_brume,
        aire_2
      ))
  }
}


Merci encore et bonne journée,
Elsa

Logez Maxime
Messages : 3154
Enregistré le : 26 Sep 2006, 11:35

Re: Fonction attribution avec conditions entre deux df

Messagepar Logez Maxime » 10 Déc 2024, 10:35

re,

arff à ce rythme les gens comme moi vont bientôt être désœuvrés. Par curiosité, tiens nous au courant pour savoir si on doit déjà envisager une reconversion ;-)

D'ailleurs pour tout ce qui concerne la première partie de ton message tu irais bien plus vite avec des fonctions merge and co que via ta boucle.
Cordialement,
Maxime


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 1 invité