2017-10-13 1 views
3

J'ai un vecteur de chaînes extrêmement désordonnées. Voici un exemple:Comment puis-je extraire la partie sans correspondance d'une chaîne dans R avec des expressions régulières?

library(tidyverse) 
library(stringr) 
strings <- tibble(
    name = c("lorem 11:07:59 86136-1-sed", 
      "ipsum 14:35:57 S VARNAME-ut", 
      "dolor 10:37:53 1513 -2-perspiciatis", 
      "sit 10:48:25", 
      "amet 13:52:1365293-2-unde", 
      "consectetur 11:53:1 16018-2-omnis", 
      "adipiscing 11:19 17237-2-iste" 
      ) 
) 
strings_out <- strings %>% 
    mutate(heads = str_extract(name, "^.*?\\s\\d{1,2}:\\d{1,2}:\\d{1,2}")) %>% 
    mutate(ends = str_replace(name, "^.*?\\s\\d{1,2}:\\d{1,2}:\\d{1,2}", "")) 
strings_out[,2:3] 
#> # A tibble: 7 x 2 
#>     heads       ends 
#>     <chr>       <chr> 
#> 1  lorem 11:07:59     86136-1-sed 
#> 2  ipsum 14:35:57     S VARNAME-ut 
#> 3  dolor 10:37:53   1513 -2-perspiciatis 
#> 4  sit 10:48:25        
#> 5  amet 13:52:13     65293-2-unde 
#> 6 consectetur 11:53:1     16018-2-omnis 
#> 7    <NA> adipiscing 11:19 17237-2-iste 

Donc ici, j'ai des chaînes qui comportent un texte, suivi d'un temps qui peuvent ou peuvent ne pas être correctement saisi, puis un peu plus du texte. Je veux extraire juste les extrémités des chaînes après le temps, cependant elles n'ont aucun modèle qui semble correspondre bien à une expression régulière potentielle en utilisant str_extract. Je peux facilement faire correspondre la première moitié des chaînes, montré dans heads. Cependant, la seule façon que j'ai trouvé pour extraire la dernière moitié est d'utiliser str_replace avec une chaîne vide, comme indiqué dans ends.

J'ai essayé d'inclure toutes les erreurs courantes que j'ai remarquées dans cette liste: pas de motif quant à la césure, l'espacement ou le contenu des chaînes après le temps, pas d'espace garanti entre le temps et la moitié désirée chiffres manquants ou même deux-points.

Ce que je voudrais faire est de pouvoir utiliser str_extract pour obtenir quelque chose de proche de ce que j'ai obtenu avec str_replace. La principale différence est que pour les erreurs où ce travail ne regex toujours pas, str_extract me donne un NA qui est facile à filtrer et corriger manuellement, mais str_replace seulement des copies dans toute la chaîne comme on le voit dans la ligne 7.

I Je pense que je pourrais faire cela avec des méthodes plus hacky, comme obtenir tous les NA et corriger manuellement dans Excel ou quelque chose, mais j'ai été surpris que je ne pouvais pas comprendre comment retourner la partie inégalée d'une chaîne en général, malgré un tas de recherches et en essayant différentes expressions régulières qui incluent (^) et [^]. Des idées?

+2

Quel est le résultat escompté? –

+0

Désolé si ce n'était pas clair. Le résultat souhaité est la deuxième colonne, à l'exception du retour NA où la première colonne en a une. Je me suis rendu compte que je pouvais simplement les remplacer par «NA» aux indices corrects, mais j'ai trouvé curieux que le concept de «retourner la partie inégalée de la chaîne» semblait difficile à faire. –

Répondre

1

En général, vous voudrez probablement regarder dans lookarounds, mais vos données pourraient avoir besoin de plus de structure pour qu'elles soient utiles.

Voici un exemple rapide je l'ai écrit avant de réaliser le temps n'a pas toujours un espace après:


library(tidyverse) 
library(stringr) 
strings <- tibble(
    name = c("lorem 11:07:59 86136-1-sed", 
      "ipsum 14:35:57 S VARNAME-ut", 
      "dolor 10:37:53 1513 -2-perspiciatis", 
      "sit 10:48:25", 
      "amet 13:52:1365293-2-unde", 
      "consectetur 11:53:1 16018-2-omnis", 
      "adipiscing 11:19 17237-2-iste" 
) 
) 
strings_out <- strings %>% 
    mutate(heads = str_extract(name, "^.*?\\s\\d{1,2}:\\d{1,2}:\\d{1,2}"), 
     ends = str_extract(name, "(?<=:\\d{1,2})[\\s\\S]+$")) 

strings_out[c(1,3)] 
#> # A tibble: 7 x 2 
#>         name     ends 
#>         <chr>    <chr> 
#> 1   lorem 11:07:59 86136-1-sed   86136-1-sed 
#> 2   ipsum 14:35:57 S VARNAME-ut   S VARNAME-ut 
#> 3 dolor 10:37:53 1513 -2-perspiciatis 1513 -2-perspiciatis 
#> 4      sit 10:48:25     <NA> 
#> 5   amet 13:52:1365293-2-unde     <NA> 
#> 6 consectetur 11:53:1 16018-2-omnis  16018-2-omnis 
#> 7  adipiscing 11:19 17237-2-iste   17237-2-iste 

Le problème est des lignes comme la ligne ici 5. Sans plus de structure, nous ne pouvons pas savoir si l'heure est 13:52:13 ou 13:52:1, car les deux sont des options présentes dans d'autres chaînes. Déterminer ce qui est correct n'est pas un problème qui peut être résolu avec des expressions régulières.

+1

Hmm ok Je vois ce que vous voulez dire à propos de la ligne 5. Autant que je sache, quand le temps est suivi de chiffres, il n'y en a pas plus de cinq, c'est pourquoi j'ai écrit la regex comme je l'ai fait. Je pense que le lookbehind est un outil dont je n'avais pas connaissance auparavant, ce qui permet d'utiliser 'str_extract' comme je le voulais. Je suppose qu'il n'y a peut-être pas un bon moyen de "extraire inégalé et il suffit de modifier l'expression rationnelle pour trouver le match désiré? –

1

Vous pouvez aussi essayer ceci:

library(tidyverse) 
library(stringr) 

regex = "^\\w+\\s\\d{2}:\\d{2}:*\\d{0,2}" 

strings %>% 
    mutate(head = str_extract(name, regex), 
     end = str_replace(name, paste0(regex, "\\s?"), ""), 
     end = str_replace(end, "^\\s*$", NA_character_)) 

Résultat:

# A tibble: 7 x 3 
           name    head     end 
           <chr>    <chr>    <chr> 
1   lorem 11:07:59 86136-1-sed  lorem 11:07:59   86136-1-sed 
2   ipsum 14:35:57 S VARNAME-ut  ipsum 14:35:57   S VARNAME-ut 
3 dolor 10:37:53 1513 -2-perspiciatis  dolor 10:37:53 1513 -2-perspiciatis 
4      sit 10:48:25  sit 10:48:25     <NA> 
5   amet 13:52:1365293-2-unde  amet 13:52:13   65293-2-unde 
6 consectetur 11:53:1 16018-2-omnis consectetur 11:53:1  16018-2-omnis 
7  adipiscing 11:19 17237-2-iste adipiscing 11:19   17237-2-iste 

Note:

fonctionne Ma solution pour la ligne 5, mais vous devrez décider si vous voulez extraire 13:52:13 ou 13:52:1 dans ce cas. Dans les deux cas, une simple modification de l'expression régulière peut être effectuée, mais comme indiqué par @Zach, il n'y a pas de manière automatique.

0

Vous pouvez l'avoir avec une seule ligne supplémentaire:

strings["rx"] <- str_match(strings$name, "\\d*:\\d*(?::\\d+)?(.*)")[,2] 
strings 

qui donne

# A tibble: 7 x 2 
           name     rx 
           <chr>     <chr> 
1   lorem 11:07:59 86136-1-sed   86136-1-sed 
2   ipsum 14:35:57 S VARNAME-ut   S VARNAME-ut 
3 dolor 10:37:53 1513 -2-perspiciatis 1513 -2-perspiciatis 
4      sit 10:48:25      
5   amet 13:52:1365293-2-unde    -2-unde 
6 consectetur 11:53:1 16018-2-omnis   16018-2-omnis 
7  adipiscing 11:19 17237-2-iste   17237-2-iste 
+0

qu'en est-il de la ligne 5? – useR