Trouver la position où un vecteur passe sous un seuil

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

Nicolas Romillac
Messages : 30
Enregistré le : 07 Jan 2020, 13:53

Trouver la position où un vecteur passe sous un seuil

Messagepar Nicolas Romillac » 28 Mai 2020, 10:13

Bonjour à tous,
comme souvent j'ai une question d'apparence simple sur laquelle je bute depuis plusieurs jours.

J'ai un vecteur (des concentration de chlorophylle), et je souhaite trouver la position à laquelle les valeurs du vecteur passent sous un certain seuil (mettons 50) pour au-moins 2 valeurs consécutives. Pour ajouter de la difficulté, je dois faire cette recherche en partant de la valeur maximum et en lisant le vecteur vers l'arrière.

Pour donner un exemple simplifié, avec le vecteur ci dessous:

Code : Tout sélectionner

c(28, 36, 53, 36, 46, 51, 33, 76, 40)

l'algorithme serait: identifier le max (76), en partant du max, trouver le premier segment de vecteur inférieur à 50 et de longueur >2 (ici 36,46), renvoyer la position du premier élément de ce segment (en partant du max), ici 5.

Identifier la première position sous le seuil en partant du maximum ne me pose pas trop de problème, avec la fonction Position, mais je butte vraiment sur le fait de spécifier un segment de durée minimale.

En réalité la question est un peu plus compliquée puisqu'il s'agit d'un objet zoo, et que je cherche une période de deux semaines sous le seuil (et non deux points), mais je pense qu'en sachant le faire sur un vecteur j'arriverai à m'en sortir avec zoo! (les écologues auront peut-être compris qu'il s'agit d'identifier le début d'un bloom planctonique)

Merci d'avance pour vos solutions éventuelles!

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar Mickael Canouil » 28 Mai 2020, 10:27

Bonjour,

Comme ceci ?

Code : Tout sélectionner

<- c(28, 36, 53, 36, 46, 51, 33, 76, 40)

x_rle <- rle(findInterval(x[which.max(x):1], c(0, 50, Inf)))
x_rle$values <- x_rle$lengths >= 2
x_rle$values
[-grep(TRUE, x_rle$values)[1]] <- FALSE

x
[inverse.rle(x_rle)]
#> [1] 36 46

max(which(inverse.rle(x_rle)))
#> [1] 5  

Si vous voulez une réponse plus détaillée/adaptée :

Cordialement,
Mickaël
mickael.canouil.fr | rlille.fr

Nicolas Romillac
Messages : 30
Enregistré le : 07 Jan 2020, 13:53

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar Nicolas Romillac » 28 Mai 2020, 13:18

Merci beaucoup pour ce morceau de code que je n'aurai pas trouvé tout seul.

Par contre je crois qu'il y a une petite erreur. Si je modifie le vecteur en ajoutant une valeur au début, le résultat est faussé:

Code : Tout sélectionner

x <- c(14,28, 36, 53, 36, 46, 51, 33, 76, 40)
x_rle <- rle(findInterval(x[which.max(x):1], c(0, 50, Inf)))
x_rle$values <- x_rle$lengths >= 2
x_rle$values[-grep(TRUE, x_rle$values)[1]] <- FALSE

x[inverse.rle(x_rle)]
#[1] 53 36
max(which(inverse.rle(x_rle)))
#[1] 5


Je pense que le problème se situe au niveau de x[inverse.rle(x_rle)], et qu'il est résolu en ajoutant rev()

Code : Tout sélectionner

x[rev(inverse.rle(x_rle))]
max(which(rev(inverse.rle(x_rle))))

Nicolas Romillac
Messages : 30
Enregistré le : 07 Jan 2020, 13:53

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar Nicolas Romillac » 28 Mai 2020, 13:34

Un autre problème se pose également si il y a 2 valeurs supérieures au seuil entre le maximum et la première période inférieure au seuil.
Dans ce cas, le code repère la période supérieure au seuil.
Exemple:

Code : Tout sélectionner

x <- c(14,28, 36, 53, 36, 46, 51, 52, 33, 76, 40)
x_rle <- rle(findInterval(x[which.max(x):1], c(0, 50, Inf)))
x_rle$values <- x_rle$lengths >= 2
x_rle$values[-grep(TRUE, x_rle$values)[1]] <- FALSE

x[rev(inverse.rle(x_rle))]
#[1] 51 52
max(which(rev(inverse.rle(x_rle))))
#[1] 8


Le problème se situe probablement dans x_rle$values[-grep(TRUE, x_rle$values)[1]] <- FALSE , où il attribue TRUE au premier groupe de 2 valeurs (indépendamment de leur valeur). Mais j'ai un peu du mal à résoudre ce problème...

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

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar Logez Maxime » 28 Mai 2020, 13:55

Bonjour,

une autre possibilité qui doit pouvoir être grandement améliorée :

Code : Tout sélectionner

library(stringi)
x2 <- (seq_along(x) < which.max(x) & x < 50)*1
x2 <- paste(x2, collapse = "")
res <- stri_locate_all_regex(x2, "1{2,}")[[1]]
res[nrow(res),]
start   end
    4     5
Après il y aura toujours un soucis s'il y a plusieurs valeurs maximales parce que which.max ne renvoie que la position que de la première valeur maximale.

Cordialement,
Maxime

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar Mickael Canouil » 28 Mai 2020, 14:16

Voici :

Code : Tout sélectionner

get_pos <- function(x) {
  if (sum(== max(x)) > 1) warning("Several position for maximum! Computation are based on the first maximum observed.")
  
  x_rle 
<- rle(findInterval(x[which.max(x):1], c(0, 50, Inf)))
  x_rle$values <- x_rle$lengths >= 2
  x_rle$values
[-grep(TRUE, x_rle$values)[1]] <- FALSE
  
  pos_vec 
<- rev(inverse.rle(x_rle))
  
  list
(
    vec = x[1:which.max(x)][pos_vec],
    pos = max(c(1:which.max(x))[pos_vec])
  )

Code : Tout sélectionner

get_pos(c(14, 28, 36, 53, 36, 46, 51, 33, 76, 40))
#> $vec
#> [1] 36 46
#> 
#> $pos
#> [1] 6
get_pos(c(14, 28, 36, 53, 36, 46, 51, 52, 33, 76, 40))
#> $vec
#> [1] 51 52
#> 
#> $pos
#> [1] 8
get_pos(c(14, 28, 36, 76, 53, 36, 46, 51, 52, 33, 76, 40))
#> Warning in get_pos(c(14, 28, 36, 76, 53, 36, 46, 51, 52, 33, 76, 40)): Several
#> position for maximum! Computation are based on the first maximum observed.
#> $vec
#> [1] 14 28 36
#> 
#> $pos
#> [1] 3   
Mickaël
mickael.canouil.fr | rlille.fr

Nicolas Romillac
Messages : 30
Enregistré le : 07 Jan 2020, 13:53

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar Nicolas Romillac » 28 Mai 2020, 16:28

Merci de votre réponse,

entre temps j'ai bidouillé une fonction trouvée sur internet pour arriver au même résultat (du moins je crois je n'ai pas vérifié!)

je la mets ici a tout hasard, si ça peut servir à quelqu'un. l'objet d'entrée doit être un objet zoo et il faut la library lubridate.

Code : Tout sélectionner

start.bloom.th <- function(x){
  library(zoo);library(lubridate)
  threshold<-median(x)+0.05*median(x)
  long<-2
  hit <- which(x[1:which.max(x)] < threshold)
  n <- length(hit)
  ind <- which(hit[-1] - hit[-n] > 1)
  starts <- c(hit[1], hit[ ind+1 ])
  ends <- c(hit[ ind ], hit[n])
  l<-isoweek(time(x[ends]))-isoweek(time(x[starts]))+1
  df<-cbind.data.frame(starts,ends,l)
  df<-df[df[,3]>=long,]
  p<-max(df[,2])+1
  time(x[p])
}

François Bonnot
Messages : 537
Enregistré le : 10 Nov 2004, 15:19
Contact :

Re: Trouver la position où un vecteur passe sous un seuil

Messagepar François Bonnot » 02 Juin 2020, 14:15

Bonjour,
ou encore :

Code : Tout sélectionner

max(which(c(diff(x<50),1)==0 & x<50 & (1:length(x))<which.max(x)))+1
François


Retourner vers « Questions en cours »

Qui est en ligne

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

cron