web-dev-qa-db-fra.com

Comment connaître les opérations effectuées pour calculer la distance Levenshtein entre les chaînes?

Avec la fonction stringdist, je peux calculer la distance Levenshtein entre les chaînes: elle compte le nombre de suppressions, insertions et substitutions nécessaires pour transformer une chaîne en une autre. Par exemple, stringdist("abc abc","abcd abc") = 1 car "d" a été inséré dans la deuxième chaîne.

Est-il possible de connaître les opérations effectuées pour obtenir la distance Levenshtein entre deux chaînes? Ou bien connaître les caractères différents entre les 2 chaînes (dans cet exemple, uniquement "d")? Merci.

library(stringdist)
stringdist("abc abc","abcde acc") = 3

Je voudrais savoir que:

  • "d" a été inséré

  • "e" a été inséré

  • "b" a été remplacé par "c"

Ou plus simplement, j'aimerais avoir la liste ("d", "e", "c").

9
yaki

Ceci est connu sous le nom de algorithme Needleman – Wunsch . Il calcule à la fois la distance entre deux chaînes ainsi que le soi-disant traceback , ce qui vous permet de reconstruire l'alignement.

Comme ce problème survient principalement en biologie lors de la comparaison de séquences biologiques, cet algorithme (et ceux associés) sont implémentés dans le package R {Biostrings} , qui fait partie de Bioconductor .

Étant donné que ce package implémente une solution plus générale que la simple distance de Levenshtein, l'utilisation est malheureusement plus complexe et la vignette d'utilisation est également longue. Mais l'utilisation fondamentale pour vos fins est la suivante:

library(Biostrings)

dist_mat = diag(27L)
colnames(dist_mat) = rownames(dist_mat) = c(letters, ' ')

result = pairwiseAlignment(
    "abc abc", "abcde acc",
    substitutionMatrix = dist_mat,
    gapOpening = 1, gapExtension = 1
)

Cela ne vous donnera pas simplement la liste c('b', 'c', 'c'), car cette liste ne représente pas entièrement ce qui s'est réellement passé ici. Au lieu de cela, il renverra un alignement entre les deux chaînes. Cela peut être représenté comme une séquence avec des substitutions et des lacunes:

score(result)
# [1] 3
aligned(result)
as.matrix(aligned(result))
#      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
# [1,] "a"  "b"  "c"  "-"  "-"  " "  "a"  "b"  "c"
aligned(result)

- Pour chaque caractère de la deuxième chaîne, il fournit le caractère correspondant dans la chaîne d'origine, en remplaçant les caractères insérés par -. Fondamentalement, il s'agit d'une "recette" pour transformer la première chaîne en deuxième chaîne. Notez qu'il ne contiendra que des insertions et des substitutions, pas des suppressions. Pour les obtenir, vous devez effectuer l'alignement dans l'autre sens (c'est-à-dire permuter les arguments de chaîne).

8
Konrad Rudolph

Avec adist(), vous pouvez récupérer les opérations:

drop(attr(adist("abc abc","abcde acc", count = TRUE), "counts"))

ins del sub 
  2   0   1 

De ?adist:

Si le nombre est VRAI, le nombre de transformations est renvoyé en tant qu'attribut "décomptes" de cette matrice, sous la forme d'un tableau tridimensionnel dont les dimensions correspondent aux éléments de x, aux éléments de y et au type de transformation (insertions, suppressions et substitutions), respectivement.

10
tmfmnk