4
votes

Sélectionnez des lignes dans une trame de données en fonction d'un modèle formé par des valeurs consécutives dans une colonne

Je suis dans la moyenne à R et j'aimerais avoir de l'aide pour l'opération suivante.

Disons que j'ai le dataframe suivant:

    ID   Label
    P3   S
    P3   M
    P4   S
    P4   M

Je veux être en mesure de sélectionner des lignes qui apparaissent dans une séquence particulière de la variable Label par rapport à chaque ID.

Pour un modèle "MS" , le le résultat attendu serait

    ID   Label
    P2   M
    P2   M
    P2   S
    P5   M
    P5   M
    P5   S

et pour un modèle "MMS" , le résultat attendu serait

    ID   Label
    P1   M
    P1   S
    P2   M
    P2   S
    P3   M
    P3   S


0 commentaires

3 Réponses :


2
votes

Une option serait de comparer les valeurs de lead et d'obtenir l'index groupé par "ID"

df <- structure(list(ID = c("P1", "P1", "P2", "P2", "P2", "P3", "P3", 
"P3", "P4", "P4", "P5", "P5", "P5"), Label = c("M", "S", "M", 
"M", "S", "M", "S", "M", "S", "M", "M", "M", "S")), 
class = "data.frame", row.names = c(NA, -13L))

données

library(data.table)
i1 <- unique(setDT(df)[, lapply(which(Reduce(`&`, 
  Map(`==`, shift(Label, n = 0:2, type = "lead"), c("M", "M", "S")))), 
       function(i) .I[i:(i+2)]) , by = ID]$V1)
df[i1]
#    ID Label
#1: P2     M
#2: P2     M
#3: P2     S
#4: P5     M
#5: P5     M
#6: P5     S


5 commentaires

Il semble que cette solution nécessite la longueur du motif saisi. J'aurais besoin d'essayer ceci sur mes données (environ 400 000 lignes) et de vous faire savoir comment cela fonctionne.


J'ai édité la question. Avant la correspondance de modèles, je souhaite regrouper les données par ID et effectuer cette correspondance de modèles sur les groupes individuels et créer le sous-ensemble.


@SJEROMEGIDEON. Dans ce cas, vous devez spécifier le by = ID]


Merci akrun! Souhaitez-vous modifier la réponse avec le même changement? Je pense que ce serait utile pour les autres. : D EDIT: ajouter by = ID] donne un mauvais résultat.


Merci! Ça marche. Il suffit de modifier les parties 0: 2 et i + 2 en fonction de la longueur du motif. Je vais essayer cet extrait sur mon ensemble de données de 400 000 lignes et vous informerai du résultat demain.



1
votes

Vous pouvez essayer d'utiliser gregexpr () . Collez d'abord toutes les étiquettes et trouvez la position de départ du modèle que vous recherchez.

pattern="SM"
starts=gregexpr(pattern=pattern,paste(df$Label,collapse=""))[[1]]
positions=as.vector(sapply(starts,function(x){ 
  s=seq(x,x+nchar(pattern)-1)
  if (all(df$ID[s]==df$ID[x])){
    return(s)
  } else {return(rep(NA,nchar(pattern)))}
  }))
positions=positions[which(!is.na(positions))]

df[positions,]
df[positions,]
   ID Label
1  P1     M
2  P1     S
4  P2     M
5  P2     S
6  P3     M
7  P3     S
12 P5     M
13 P5     S

pattern="MMS"
   ID Label
3  P2     M
4  P2     M
5  P2     S
11 P5     M
12 P5     M
13 P5     S

pattern="SM"
   ID Label
9  P4     S
10 P4     M

Modifier

Ma solution précédente n'a pas récupéré le motif entier (juste le début).

> df
   ID Label
1  P1     M
2  P1     S
3  P2     M
4  P2     M
5  P2     S
6  P3     M
7  P3     S
8  P3     S
9  P4     S
10 P4     M
11 P5     M
12 P5     M
13 P5     S


6 commentaires

Hé boski! Merci pour la réponse, mais les valeurs que vous avez prises pour la variable ID sont fausses. Jetez-y un œil.


hé, j'ai mis à jour la réponse pour renvoyer le modèle entier et j'ai également fait les étiquettes comme les vôtres.


Hey! Merci encore. Votre réponse est assez proche. Je suis désolé de ne pas avoir été clair dans ma demande. J'ai édité ma question. Je veux trouver ces modèles après avoir regroupé les données par ID. J'ai ajouté un autre exemple pour clarifier ma demande.


Je ne pense pas avoir compris ce que vous dites. J'obtiens exactement le même résultat que vos exemples. J'ai mis à jour avec votre nouvel ajout. EDIT ok, je vois maintenant


ça fonctionne maintenant. Il vérifie que tous les identifiants sont égaux avant de renvoyer des positions.


Ça marche! Merci! EDIT: J'ai besoin de tester la vitesse d'exécution de cet extrait de code sur mon ensemble de données de 400 000 lignes. Vous fera connaître le résultat.



1
votes

Une solution de base pour la question d'origine pourrait être:

nchar <- nchar("MS")
df %>%
 group_by(ID) %>%
 filter(row_number() %in% c(rep(grepRaw("MS", paste(Label, collapse = ""), all = TRUE), 
            each = nchar) + 0:(nchar - 1)))

  ID    Label
  <fct> <fct>
1 P1    M    
2 P1    S    
3 P2    M    
4 P2    S    
5 P3    M    
6 P3    S    
7 P5    M    
8 P5    S  

Ou écrite sous la forme dplyr :

nchar <- nchar("MS")
df %>%
 filter(row_number() %in% c(rep(grepRaw("MS", paste(Label, collapse = ""), all = TRUE), 
            each = nchar) + 0:(nchar - 1)))

   ID Label
1  P1     M
2  P1     S
3  P2     M
4  P2     S
5  P3     M
6  P3     S
7  P3     M
8  P4     S
9  P5     M
10 P5     S

Abordant également la modification de la question:

nchar <- nchar("MS")
x <- grepRaw("MS", paste(df$Label, collapse = ""), all = TRUE)
y <- rep(x, each = nchar) + 0:(nchar - 1)

df[1:nrow(df) %in% y, ]

  ID Label
1 P1     M
2 P1     S
4 P2     M
5 P2     S
6 P3     M
7 P3     S

nchar <- nchar("SM")
x <- grepRaw("SM", paste(df$Label, collapse = ""), all = TRUE)
y <- rep(x, each = nchar) + 0:(nchar - 1)

df[1:nrow(df) %in% y, ]

   ID Label
2  P1     S
3  P2     M
5  P2     S
6  P3     M
9  P4     S
10 P4     M


1 commentaires

Hey! Merci d'avoir répondu! Ceci est exactement ce que je cherchais.