StringDistances.jl/README.md

142 lines
6.8 KiB
Markdown
Raw Normal View History

2015-10-22 18:38:04 +02:00
[![Build Status](https://travis-ci.org/matthieugomez/StringDistances.jl.svg?branch=master)](https://travis-ci.org/matthieugomez/StringDistances.jl)
2015-10-23 03:03:57 +02:00
[![Coverage Status](https://coveralls.io/repos/matthieugomez/StringDistances.jl/badge.svg?branch=master)](https://coveralls.io/r/matthieugomez/StringDistances.jl?branch=master)
2015-10-30 16:20:26 +01:00
[![StringDistances](http://pkg.julialang.org/badges/StringDistances_0.4.svg)](http://pkg.julialang.org/?pkg=StringDistances)
2015-10-22 18:38:04 +02:00
2015-11-04 18:40:30 +01:00
This Julia package computes various distances between strings.
2015-10-22 18:38:04 +02:00
2015-10-25 16:23:46 +01:00
## Distances
2015-10-22 18:38:04 +02:00
2015-11-04 18:40:30 +01:00
#### Edit Distances
2015-11-05 16:51:32 +01:00
- [Hamming Distance](https://en.wikipedia.org/wiki/Hamming_distance)
- [Levenshtein Distance](https://en.wikipedia.org/wiki/Levenshtein_distance)
- [Damerau-Levenshtein Distance](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance)
2015-11-04 18:40:30 +01:00
#### Q-Grams Distances
2015-11-05 19:02:50 +01:00
Q-gram distances compare the set of all substrings of length `q` in each string.
2015-11-03 16:55:37 +01:00
- QGram Distance
2015-11-05 16:51:32 +01:00
- [Cosine Distance](https://en.wikipedia.org/wiki/Cosine_similarity)
- [Jaccard Distance](https://en.wikipedia.org/wiki/Jaccard_index)
- [Overlap Distance](https://en.wikipedia.org/wiki/Overlap_coefficient)
- [Sorensen-Dice Distance](https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient)
2015-10-25 16:23:46 +01:00
2015-11-05 16:51:32 +01:00
#### Others
- [Jaro Distance](https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance)
2015-11-06 16:55:08 +01:00
- [RatcliffObershelp Distance](https://xlinux.nist.gov/dads/HTML/ratcliffObershelp.html)
2015-11-04 18:40:30 +01:00
2015-10-25 16:23:46 +01:00
## Syntax
2015-11-09 15:07:14 +01:00
#### evaluate
The function `evaluate` returns the litteral *distance* between two strings (a value of 0 being identical). While some distances are bounded by 1, other distances like `Hamming`, `Levenshtein`, `Damerau-Levenshtein`, `Jaccard` can be higher than 1.
```julia
using StringDistances
evaluate(Hamming(), "martha", "marhta")
#> 2
evaluate(QGram(2), "martha", "marhta")
#> 6
```
2015-11-04 18:40:30 +01:00
2015-11-09 15:07:14 +01:00
#### compare
2015-11-09 20:40:13 +01:00
The higher level function `compare` returns *a similarity score* between two strings. The similarity score is always between 0 and 1. A value of 0 being completely different and a value of 1 being completely similar. The output of compare is generally 1 - normalized distance, with some care for `NaN` values.
2015-11-04 18:40:30 +01:00
```julia
using StringDistances
compare(Hamming(), "martha", "marhta")
#> 0.6666666666666667
compare(QGram(2), "martha", "marhta")
#> 0.4
```
## Modifiers
2015-11-09 14:56:44 +01:00
The package defines a number of ways to modify similarity scores:
2015-11-04 18:40:30 +01:00
- [Winkler](https://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance) boosts the similary score of strings with common prefixes
2015-10-25 16:23:46 +01:00
```julia
2015-11-04 18:40:30 +01:00
compare(Jaro(), "martha", "marhta")
#> 0.9444444444444445
compare(Winkler(Jaro()), "martha", "marhta")
#> 0.9611111111111111
2015-10-25 16:23:46 +01:00
```
2015-11-09 14:56:44 +01:00
The Winkler adjustment was originally defined for the Jaro similarity score but this package defines it for any string distance.
2015-10-25 16:23:46 +01:00
```julia
2015-11-04 18:40:30 +01:00
compare(QGram(2), "william", "williams")
#> 0.9230769230769231
compare(Winkler(QGram(2)), "william", "williams")
#> 0.9538461538461539
2015-10-25 16:23:46 +01:00
```
2015-11-04 18:40:30 +01:00
2015-11-09 14:56:44 +01:00
- The Python library [fuzzywuzzy](http://chairnerd.seatgeek.com/fuzzywuzzy-fuzzy-string-matching-in-python/) defines a few modifiers for the `RatcliffObershelp` similarity score. This package replicates them and extends them to any string distance:
2015-11-04 18:40:30 +01:00
2015-11-06 16:55:08 +01:00
- [Partial](http://chairnerd.seatgeek.com/fuzzywuzzy-fuzzy-string-matching-in-python/) returns the maximal similarity score between the shorter string and substrings of the longer string.
2015-11-04 18:40:30 +01:00
```julia
2015-11-05 16:51:32 +01:00
compare(Levenshtein(), "New York Yankees", "Yankees")
#> 0.4375
compare(Partial(Levenshtein()), "New York Yankees", "Yankees")
2015-11-04 18:40:30 +01:00
#> 1.0
```
2015-11-06 16:55:08 +01:00
- [TokenSort](http://chairnerd.seatgeek.com/fuzzywuzzy-fuzzy-string-matching-in-python/) adjusts for differences in word orders by reording words alphabetically.
2015-11-04 18:40:30 +01:00
```julia
2015-11-05 16:51:32 +01:00
compare(RatcliffObershelp(), "mariners vs angels", "angels vs mariners")
#> 0.44444
2015-11-04 18:40:30 +01:00
compare(TokenSort(RatcliffObershelp()),"mariners vs angels", "angels vs mariners")
#> 1.0
```
2015-11-06 16:55:08 +01:00
- [TokenSet](http://chairnerd.seatgeek.com/fuzzywuzzy-fuzzy-string-matching-in-python/) adjusts for differences in word orders and word numbers by comparing the intersection of two strings with each string.
2015-11-04 18:40:30 +01:00
```julia
2015-11-05 16:51:32 +01:00
compare(Jaro(),"mariners vs angels", "los angeles angels at seattle mariners")
#> 0.559904
compare(TokenSet(Jaro()),"mariners vs angels", "los angeles angels at seattle mariners")
#> 0.944444
2015-11-04 18:40:30 +01:00
```
2015-11-06 16:55:08 +01:00
- [TokenMax](http://chairnerd.seatgeek.com/fuzzywuzzy-fuzzy-string-matching-in-python/) combines scores using the base distance, the `Partial`, `TokenSort` and `TokenSet` modifiers, with penalty terms depending on string lengths.
2015-11-06 16:47:15 +01:00
```julia
2015-11-06 16:55:08 +01:00
compare(TokenMax(RatcliffObershelp()),"mariners vs angels", "los angeles angels at seattle mariners")
2015-11-06 16:47:15 +01:00
#> 0.855
```
2015-11-05 19:02:50 +01:00
- You can compose multiple modifiers:
```julia
compare(Winkler(Partial(Jaro())),"mariners vs angels", "los angeles angels at seattle mariners")
#> 0.7378917378917379
compare(TokenSet(Partial(RatcliffObershel())),"mariners vs angels", "los angeles angels at seattle mariners")
#> 1.0
```
## Tips
2015-11-06 03:03:45 +01:00
- Each distance is tailored to a specific problem. Edit distances works well with local spelling errors, the Ratcliff-Obsershelp distance works well with edited texts, the Jaro Winkler distance was invented for short strings such as person names, the QGrams distances works well with strings composed of multiple words and fluctuating orderings.
2015-11-09 14:56:44 +01:00
- Most distances perform poorly when comparing company or individual names, where each string is composed of a few words.
2015-11-05 19:02:50 +01:00
2015-11-06 15:40:06 +01:00
- While word order is mostly irrelevant in this situation, edit distances heavily penalize different orderings. Instead, either use a distance robust to word order (like QGram distances), or compose a distance with `TokenSort`, which reorders the words alphabetically.
2015-11-05 23:17:57 +01:00
```julia
compare(RatcliffObershelp(), "mariners vs angels", "angels vs mariners")
#> 0.44444
compare(TokenSort(RatcliffObershelp()),"mariners vs angels", "angels vs mariners")
#> 1.0
compare(Cosine(3), "mariners vs angels", "angels vs mariners")
#> 0.8125
```
2015-11-09 14:56:44 +01:00
- General words (like "bank", "company") may appear in one string but no the other. One solution is to abbreviate these common names to diminish their importance (ie "bank" -> "bk", "company" -> "co"). Another solution is to use the `Overlap` distance, which compares the number of common qgrams with the length of the shorter strings. Another solution is to use the `Partial` or `TokenSet` modifiers.
2015-11-06 03:03:45 +01:00
2015-11-09 14:56:44 +01:00
`TokenMax(RatcliffObershelp())`, corresponding to the `WRatio` function in the Python library `fuzzywuzzy`, solves these two issues and may work best in this situation.
2015-11-06 16:55:08 +01:00
2015-11-06 02:40:56 +01:00
- Standardize strings before comparing them (lowercase, punctuation, whitespaces, accents, abbreviations...)
2015-11-05 19:02:50 +01:00
2015-11-05 16:51:32 +01:00
## References
2015-11-05 19:02:50 +01:00
- [The stringdist Package for Approximate String Matching](https://journal.r-project.org/archive/2014-1/loo.pdf) Mark P.J. van der Loo
- [fuzzywuzzy blog post](http://chairnerd.seatgeek.com/fuzzywuzzy-fuzzy-string-matching-in-python/)
2015-11-05 16:51:32 +01:00