itérer sur ligne précédente

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

Jérémy Jachacz
Messages : 153
Enregistré le : 15 Avr 2014, 12:56

itérer sur ligne précédente

Messagepar Jérémy Jachacz » 07 Avr 2020, 15:27

Bonjour,

Je cherche a traduire une formule excel dans R et j'avoue que pour la première fois, R est plus lent (ou du moins je trouve pas solution rapide).
j'utilise data.table et autre package de manipulation de données.
Là je rencontre un problème car je dois utiliser une colonne que je suis en train de créer..

Voici un exemple des données (désolé pour la qualité de l'exemple reproductible)
date_fichier delai Cond1 NUM CODE
18/02/19 na FL 1 ABC-xxxxxxxx
09/04/19 48 FL 2 ABC-xxxxxxxx
05/06/19 56 FL 3 ABC-xxxxxxxx
27/07/19 30 FL 3 ABC-xxxxxxxx
13/09/19 49 FL 4 ABC-xxxxxxxx
19/11/19 64 FL 5 ABC-xxxxxxxx
06/05/19 na FL 1 ABC-yyyyyyyy
02/09/19 106 non 2 ABC-yyyyyyyy
10/12/19 107 non 2 ABC-yyyyyyyy
06/05/19 na FL 1 ABC-zzzzzzzzzz
02/09/19 109 non 2 ABC-zzzzzzzzzz
10/12/19 107 non 2 ABC-zzzzzzzzzz

La colonne que je souhaite c'est la colonne NUM qui est obtenue selon cette formule excel : à mettre colonne NUM LIGNE 2
=SI(E2<>E3;1;SI(OU(C2="non";B3<35);D2;D2+1))

Après avoir trier le tableau par CODE et date_fichier,
L'objectif est : [-1] on va dire que cela signifie ligne précédente
PAR (by de data.table) CODE,
selon si COND1[-1] == "non" OU delai < 35 alors NUM alors NUM = NUM[-1]
sinon NUM = NUM[-1]+1

Voilà pour la traduction excel vs ce que l'on veut obtenir.

Dans R, j'ai vu la fonction shift avec data.table mais elle ne fonctionne pas sur une colonne en cours de mise à jour..
j'ai tenté d'y aller en plusieurs fois, mais cela pose problème car pour certains CODE j'ai +200 lignes.. donc ce qui veut dire faire autant de "plusieurs fois"

J'ai pu faire la boucle for qui fonctionne sans doute très bien mais sur 131K lignes ce n'est pas optimale et surtout trop long,
une boucle repeat est trop longue aussi..

Merci
Jérémy
Statisticien (69)

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

Re: itérer sur ligne précédente

Messagepar Mickael Canouil » 07 Avr 2020, 17:07

Bonjour,

vous avez réalisé quelque-chose comme suit ? (je ne suis pas absolument pas sûr que ce soit le bon résultat par rapport votre formule)

Code : Tout sélectionner

library(data.table)
dta <- fread("date_fichier delai Cond1 NUM CODE
18/02/19 na FL 1 ABC-xxxxxxxx
09/04/19 48 FL 2 ABC-xxxxxxxx
05/06/19 56 FL 3 ABC-xxxxxxxx
27/07/19 30 FL 3 ABC-xxxxxxxx
13/09/19 49 FL 4 ABC-xxxxxxxx
19/11/19 64 FL 5 ABC-xxxxxxxx
06/05/19 na FL 1 ABC-yyyyyyyy
02/09/19 106 non 2 ABC-yyyyyyyy
10/12/19 107 non 2 ABC-yyyyyyyy
06/05/19 na FL 1 ABC-zzzzzzzzzz
02/09/19 109 non 2 ABC-zzzzzzzzzz
10/12/19 107 non 2 ABC-zzzzzzzzzz"
)

Code : Tout sélectionner

dta[, logical_cond := Cond1 == "non" | delai < 35, by = CODE]
dta[order(CODE, date_fichier), NUM_update := ifelse(shift(logical_cond, fill = FALSE), shift(NUM), NUM + 1), by = CODE]
dta
#>     date_fichier delai Cond1 NUM           CODE logical_cond NUM_update
#>  1:     18/02/19    na    FL   1   ABC-xxxxxxxx        FALSE          2
#>  2:     09/04/19    48    FL   2   ABC-xxxxxxxx        FALSE          3
#>  3:     05/06/19    56    FL   3   ABC-xxxxxxxx        FALSE          4
#>  4:     27/07/19    30    FL   3   ABC-xxxxxxxx         TRUE          4
#>  5:     13/09/19    49    FL   4   ABC-xxxxxxxx        FALSE          5
#>  6:     19/11/19    64    FL   5   ABC-xxxxxxxx        FALSE          6
#>  7:     06/05/19    na    FL   1   ABC-yyyyyyyy        FALSE          2
#>  8:     02/09/19   106   non   2   ABC-yyyyyyyy         TRUE          3
#>  9:     10/12/19   107   non   2   ABC-yyyyyyyy         TRUE          3
#> 10:     06/05/19    na    FL   1 ABC-zzzzzzzzzz        FALSE          2
#> 11:     02/09/19   109   non   2 ABC-zzzzzzzzzz         TRUE          3
#> 12:     10/12/19   107   non   2 ABC-zzzzzzzzzz         TRUE          3    

Votre problème semble être plus orienté optimisation, mais si vous pouviez fournir le code à optimiser, ça serait plus facile.

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

Jérémy Jachacz
Messages : 153
Enregistré le : 15 Avr 2014, 12:56

Re: itérer sur ligne précédente

Messagepar Jérémy Jachacz » 07 Avr 2020, 17:37

Bonjour,

Merci pour votre réponse,
j'ai pas été clair, mais réalité la colonne NUM ne fait pas partie des données de depart c'est cette colonne que je cherche à obtenir à partir des autres.

voici le code de la boucle for i qui fait le travail mais qui n'est vraiment pas la meilleur solution :

Code : Tout sélectionner


# trop long
extr_all_res$NUM[1]=1 # initialise premiere ligne

for(i in 2:nrow(extr_all_res)){
  ############## cas particulier CODE NA
  if(is.na(extr_all_res$CODE[i])){
    next
  }else if(is.na(extr_all_res$CODE[i-1])){
    extr_all_res$NUM[i]=1
    next
  }
 
  ###############
  if(extr_all_res$CODE[i]!=extr_all_res$CODE[i-1]){
    extr_all_res$NUM[i]=1 # si nouveau code alors on part de 1
   
  }else if(extr_all_res$cond1[i-1]=="non" | extr_all_res$delai[i]<35){
    extr_all_res$NUM[i]=extr_all_res$NUM[i-1]
   
  }else{
    extr_all_res$NUM[i]=extr_all_res$NUM[i-1]+1
  }
}
Statisticien (69)

Gabriel Terraz
Messages : 591
Enregistré le : 26 Sep 2011, 15:11

Re: itérer sur ligne précédente

Messagepar Gabriel Terraz » 07 Avr 2020, 18:40

Salut,

Après avoir lu plusieurs fois tes messages, je ne comprend toujours pas vraiment ce que tu veux faire, on se perd dans tes morceaux de codes excel/R..
Tu pourrais nous doner tout simplement un exemple de ton tableau d'entrée et un exemple de ce que tu veux ?

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

Re: itérer sur ligne précédente

Messagepar Logez Maxime » 07 Avr 2020, 20:30

Bonsoir,

je ne suis pas sur d'avoir compris mais une possibilité :

Code : Tout sélectionner

# mon tableau s'appelle test
nas <- is.na(test$CODE)

gr <- cumsum(!c(FALSE, (test$CODE[-1]==test$CODE[-nrow(test)] & !nas[-1] & !nas[-nrow(test)])))

cond1 <- test$Cond1[-nrow(test)] == "non" | test$delai[-1] <35
cond2 <- c(FALSE, !cond1)

res <- ave(cond2*1, gr, FUN = function(x) {
x[1] <- 0
1+cumsum(x)})

# ou
res <- unlist(tapply(cond2*1, gr,  function(x) {
x[1] <- 0
1+cumsum(x)}))

# a tester le plus rapide des deux (ça dépendra de la taille du tableau)
res[nas] <- NA
Cordialement,
Maxime

Jérémy Jachacz
Messages : 153
Enregistré le : 15 Avr 2014, 12:56

Re: itérer sur ligne précédente

Messagepar Jérémy Jachacz » 08 Avr 2020, 08:21

Bonjour,

Merci pour vos retour,
Désolé si je n'ai pas été clair, je reprends.

J'ai ce tableau de départ :

Code : Tout sélectionner

test <- fread("date_fichier delai cond1 CODE
18/02/19 NA FL ABC-xxxxxxxx
09/04/19 48 FL ABC-xxxxxxxx
05/06/19 56 FL ABC-xxxxxxxx
27/07/19 30 FL ABC-xxxxxxxx
13/09/19 49 FL ABC-xxxxxxxx
19/11/19 64 FL ABC-xxxxxxxx
06/05/19 NA FL ABC-yyyyyyyy
02/09/19 106 non ABC-yyyyyyyy
10/12/19 107 non ABC-yyyyyyyy
06/05/19 NA FL ABC-zzzzzzzzzz
02/09/19 109 non ABC-zzzzzzzzzz
10/12/19 107 non ABC-zzzzzzzzzz")


A l'aide de cette boucle j'obtiens la colonne NUM :

Code : Tout sélectionner

test$NUM[1]=1 # initialise première ligne
for(i in 2:nrow(test)){
  ############## cas particulier CODE NA
  if(is.na(test$CODE[i])){
    next
  }else if(is.na(test$CODE[i-1])){
    test$NUM[i]=1
    next
  }
 
  ###############
  if(test$CODE[i]!=test$CODE[i-1]){
    test$NUM[i]=1 # si nouveau code alors on part de 1
   
  }else if(test$cond1[i-1]=="non" | test$delai[i]<35){
    test$NUM[i]=test$NUM[i-1]
   
  }else{
    test$NUM[i]=test$NUM[i-1]+1
  }
}


le problème c'est que la boucle sur 131 000 ligne c'est trop long.

@Maxime,
j'ai testé le code il fonctionne sur le premier CODE mais sur les deux CODE suivants cela ne revoie pas ce qui est attendu
J'ai 1 1 1 à la place de 1 2 2, les fonctions sont intéressantes j'explore cette façon

EDIT :
En trifouillant, j'ai trouvé ou cela bloquait, dans le tableau initial, les NA ont été renseigné tel quel "na"

Code : Tout sélectionner

test <- fread("date_fichier delai cond1 CODE
18/02/19 NA FL ABC-xxxxxxxx
09/04/19 48 FL ABC-xxxxxxxx
05/06/19 56 FL ABC-xxxxxxxx
27/07/19 30 FL ABC-xxxxxxxx
13/09/19 49 FL ABC-xxxxxxxx
19/11/19 64 FL ABC-xxxxxxxx
06/05/19 NA FL ABC-yyyyyyyy
02/09/19 106 non ABC-yyyyyyyy
10/12/19 107 non ABC-yyyyyyyy
06/05/19 NA FL ABC-zzzzzzzzzz
02/09/19 109 non ABC-zzzzzzzzzz
10/12/19 107 non ABC-zzzzzzzzzz")

# mon tableau s'appelle test
nas <- is.na(test$CODE)

gr <- cumsum(!c(FALSE, (test$CODE[-1]==test$CODE[-nrow(test)] & !nas[-1] & !nas[-nrow(test)])))
gr
cond1 <- test$cond1[-nrow(test)] == "non" | is.na(test$delai[-1]) | test$delai[-1]<35
cond1
cond2 <- c(FALSE, !cond1)
cond2

res <- ave(cond2*1, gr, FUN = function(x) {
  x[1] <- 0
  1+cumsum(x)})
res


Je reste curieux de voir d'autres solutions si vous étiez en train de réfléchir dessus.
Merci
Jérémy
Statisticien (69)

Jérémy Jachacz
Messages : 153
Enregistré le : 15 Avr 2014, 12:56

Re: itérer sur ligne précédente

Messagepar Jérémy Jachacz » 08 Avr 2020, 09:16

Toujours dans l'optique de comprendre,

Code : Tout sélectionner

gr <- cumsum(!c(FALSE, (test$CODE[-1]==test$CODE[-nrow(test)] & !nas[-1] & !nas[-nrow(test)])))
gr

[1] 1 1 1 1 1 1 2 2 2 3 3 3


J'ai compris le but, je comprends à quoi sert la partie après le FALSE, mais je n'arrive pas comprendre comment cumsum fait pour obtenir son résultat
Statisticien (69)

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

Re: itérer sur ligne précédente

Messagepar Logez Maxime » 08 Avr 2020, 09:34

re,

rendons à César ce qui est a César, c'est un code que j'avais copié chez François.
Ca permet de repérer des séries dans un vecteur :

Code : Tout sélectionner

x<- rbinom(10, 1, 0.7)
x
0 1 1 0 1 0 0 1 1 0

# est-ce que x-1 est égale à x ?
y <- x[-length(x)]==x[-1]
# est-ce que x-1 est différent de x ?
y <- !y
# si oui alors on aura TRUE et TRUE en numérique ça vaut 1
# si on fait la somme cummulée, à chaque différence on rajoute 1
# si c'est équivelent alors on rajoute 0
# ca permet de repérer les séries
cumsum(c(FALSE, y))
[1] 0 1 1 2 3 4 4 5 5 6

# pour comparaison
 rbind(x, cumsum(c(FALSE, y)))
  [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
x    0    1    1    0    1    0    0    1    1     0
     0    1    1    2    3    4    4    5    5     6

Au final est-ce que ça a fonctionner avec des vrais NA ?
Sur les tests que j'avais fait hier, ça avait l'air.

Cordialement,
Maxime

Jérémy Jachacz
Messages : 153
Enregistré le : 15 Avr 2014, 12:56

Re: itérer sur ligne précédente

Messagepar Jérémy Jachacz » 08 Avr 2020, 10:10

Super, merci pour l'explication !

Oui avec les vrais NA cela fonctionne mais il faut ajouter la condition

Code : Tout sélectionner

cond1 <- test$cond1[-nrow(test)] == "non" | is.na(test$delai[-1]) | test$delai[-1]<35


Merci !
Statisticien (69)


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 2 invités