web-dev-qa-db-fra.com

Correspondance partielle de chaîne animale en R

J'ai une trame de données,

d<-data.frame(name=c("brown cat", "blue cat", "big lion", "tall tiger",
                     "black panther", "short cat", "red bird",
                     "short bird stuffed", "big eagle", "bad sparrow",
                     "dog fish", "head dog", "brown yorkie",
                     "lab short bulldog"), label=1:14)

Je voudrais rechercher la colonne name et si les mots "chat", "lion", "tigre" et "panthère" apparaissent, je veux affecter la chaîne de caractères feline à une nouvelle colonne et la ligne correspondante species.

Si les mots "bird", "eagle", and "sparrow" apparaît, je veux affecter la chaîne de caractères avian à une nouvelle colonne et à la ligne correspondante species.

Si les mots "chien", "yorkie" et "bulldog" apparaissent, je veux affecter la chaîne de caractères canine à une nouvelle colonne et la ligne correspondante species.

Idéalement, je stockerais cela dans une liste ou quelque chose de similaire que je peux conserver au début du script, car comme de nouvelles variantes de l'espèce apparaissent dans la catégorie de nom, ce serait bien d'avoir un accès facile pour mettre à jour ce qui est qualifié en tant que feline, avian et canine.

Cette question est presque résolue ici (Comment créer une nouvelle colonne dans un dataframe basé sur une chaîne partielle correspondant à une autre colonne dans R), mais il ne résout pas la torsion de plusieurs noms présente dans ce problème.

16
testname123

Il peut y avoir une solution plus élégante que cela, mais vous pouvez utiliser grep avec | pour spécifier des correspondances alternatives.

d[grep("cat|lion|tiger|panther", d$name), "species"] <- "feline"
d[grep("bird|eagle|sparrow", d$name), "species"] <- "avian"
d[grep("dog|yorkie", d$name), "species"] <- "canine"

J'ai supposé que vous vouliez dire "aviaire" et j'ai omis "bouledogue" car il contient "chien".

Vous voudrez peut-être ajouter ignore.case = TRUE au grep.

production:

#                 name label species
#1           brown cat     1  feline
#2            blue cat     2  feline
#3            big lion     3  feline
#4          tall tiger     4  feline
#5       black panther     5  feline
#6           short cat     6  feline
#7            red bird     7   avian
#8  short bird stuffed     8   avian
#9           big eagle     9   avian
#10        bad sparrow    10   avian
#11           dog fish    11  canine
#12           head dog    12  canine
#13       brown yorkie    13  canine
#14  lab short bulldog    14  canine
26
ping

Une façon élégante de le faire (je dis élégant-parce que, même si c'est la façon la plus élégante que je connaisse, ce n'est pas génial), c'est quelque chose comme:

#Define the regexes at the beginning of the code
regexes <- list(c("(cat|lion|tiger|panther)","feline"),
                c("(bird|eagle|sparrow)","avian"),
                c("(dog|yorkie|bulldog)","canine"))

....


#Create a vector, the same length as the df
output_vector <- character(nrow(d))

#For each regex..
for(i in seq_along(regexes)){

    #Grep through d$name, and when you find matches, insert the relevant 'tag' into
    #The output vector
    output_vector[grepl(x = d$name, pattern = regexes[[i]][1])] <- regexes[[i]][2]

} 

#Insert that now-filled output vector into the dataframe
d$species <- output_vector

Les avantages de cette méthode sont multiples

  1. Vous ne devez modifier la trame de données qu'une seule fois dans tout le processus, ce qui augmente la vitesse de la boucle (les trames de données n'ont pas de modification en place; pour modifier une trame de données 3 fois, vous devez essentiellement la réétiqueter et la recréer 3 fois).
  2. En spécifiant à l'avance la longueur du vecteur, puisque nous savons ce que ce sera, vous augmentez encore plus la vitesse en vous assurant que le vecteur de sortie n'a jamais besoin de plus de mémoire allouée après sa création.
  3. Parce qu'il s'agit d'une boucle, plutôt que d'appels manuels répétés, l'ajout de plus de lignes et de catégories à l'objet "regexes" ne nécessitera pas de modification supplémentaire du code. Il fonctionnera comme il le fait maintenant.

Le seul inconvénient - et cela s'applique à, je pense, à la plupart des solutions que vous obtiendrez probablement, est que si quelque chose correspond à plusieurs modèles, le dernier modèle de la liste auquel il correspond sera sa balise `` espèce ''.

2
user3471268