{smcl} help {hi:strgroup} {hline} {title:Title} {p 4 4 2}{cmd:strgroup} {hline 2} Match strings based on their Levenshtein edit distance. {title:Syntax} {p 4 4 2}{cmd:strgroup} {it:varname} [if] [in] , {cmdab:gen:erate(}{it:newvarname}{cmd:)} {cmdab:thresh:old(}{it:#}{cmd:)} [{cmd:first} {cmdab:norm:alize([shorter|longer|none])} {cmd:noclean} {cmd:force}] {p 2 2 1}{cmd:by} is allowed; see help {help by}. {title:Description} {p 4 4 2}{cmd:strgroup} performs a fuzzy string match using the following algorithm: {p 8 14 2}1. Calculate the Levenshtein edit distance between all pairwise combinations of strings in {it:varname}. {p 8 14 2}2. Normalize the edit distance as specified by {cmd:normalize([shorter|longer|none])}. The default is to divide the edit distance by the length of the shorter string. {p 8 14 2}3. Match a string pair if their normalized edit distance is less than or equal to the user-specified threshold. {p 8 14 2}4. If string A is matched to string B and string B is matched to string C, then match A to C. {p 8 14 2}5. Assign each group of matches a unique number and store this result in {it:newvarname}. {p 4 4 2}For example, the Levenshtein edit distance between "widgets" and "widgetts" is 1. The lengths of these two strings are 7 and 8, respectively. Assuming {cmd:normalize(shorter)}, they are matched by {cmd:strgroup} if 1/7 <= threshold. {p 4 4 2}See {help levenshtein:levenshtein} for an explanation of the Levenshtein edit distance. {title:Options} {p 4 8 2} {cmd:generate(}{it:newvarname}{cmd:)} specifies the name of a new variable to store the results. {p 4 8 2} {cmd:threshold(}{it:#}{cmd:)} sets the threshold level for matching. {p 4 8 2} {cmd:first} instructs {cmd:strgroup} to only match strings that share the same first character. This typically reduces the amount of time required for {cmd:strgroup} to run by several orders of magnitude, at the cost of perhaps incorrectly not matching strings. For example, "widgets" and "qidgets" will not be matched if you specify {cmd:first} because they do not begin with the same character. {p 4 8 2} {cmd:normalize([shorter|longer|none])} is used to define the normalization of the Levenshtein edit distance. With {cmd:shorter} all edit distances are divided by the length of the shorter string in the pair; this is also the default. {cmd:longer} divides the edit distance by the length of the longer string. {cmd:none} specifies that no normalization is needed. {p 4 8 2} {cmd:noclean} instructs {cmd:strgroup} not to trim leading and trailing blanks when comparing string pairs. Trimming can reduce run time. {p 4 8 2} {cmd:force} forces {cmd:strgroup} to run even if when comparing more than 10,000 observations. This may take a while and may cause memory problems if your dataset is too large. {title:Remarks} {p 4 4 2} {cmd:strgroup} does not match missing strings. {cmd:strgroup} is case sensitive. {p 4 4 2} The Levenshtein edit distance is calculated using byte-based comparisons, and some non-ASCII characters are larger than one byte in Unicode. For example, the edit distance between the Unicode characters '$' and '£' is 2, not 1: {col 8}{cmd:. {stata levenshtein "$" "£"}} {p 4 4 2} To avoid this issue, use Stata's string functions to convert multi-byte characters to single-byte characters: {col 8}{cmd:. {stata levenshtein "$" "`=ustrto("£","latin1",1)'"} {title:Notes} {p 4 4 2} As explained above, {cmd:strgroup} calculates the Levenshtein edit distance between all pairwise combinations of strings in {it:varname}. Let N be the number of observations being compared. Then the amount of memory and number of calculations required by {cmd:strgroup} is proportional to (N)(N-1)/2, an expression that increases with the square of N. Thus, large datasets need to be divided into subsets in order to facilitate calculations. The {cmd:first} option automates this by subsetting strings according to their first characters. Alternatively, the user can run {cmd:strgroup} on subsets of the data by using the {cmd:if}, {cmd:in} and/or {cmd:by} options. {p 4 4 2} {cmd:strgroup} is implemented as a C {help plugin:plugin} in order to minimize memory requirements and to maximize speed. Plugins are specific to the hardware architecture and software framework of your computer, i.e., they are not cross-platform. Define a platform by two characteristics: machine type and operating system. Stata stores these characteristics in {cmd:c(machine_type)} and {cmd:c(os)}, respectively. {cmd:strgroup} supports the following platforms at this time: {col 10}{hi:Machine type}{col 40} {hi:Operating system} {col 10}PC{col 40} Windows {col 10}PC (64-bit x86-64){col 40} Windows {col 10}PC (64-bit x86-64){col 40} Unix {col 10}Macintosh{col 40} MacOSX {col 10}Macintosh (Intel 64-bit){col 40} MacOSX {title:Example} {p 4 4 2} Merge two datasets together and identify potential matches that didn't merge. {col 8}{cmd:. {stata sysuse auto, clear}} {col 8}{cmd:. {stata tempfile t}} {col 8}{cmd:. {stata keep make price}} {col 8}{cmd:. {stata replace make = make + "a" in 5}} {col 8}{cmd:. {stata replace make = "gibberish" in 10}} {col 8}{cmd:. {stata save "`t'"}} {col 8}{cmd:. {stata sysuse auto, clear}} {col 8}{cmd:. {stata keep make}} {col 8}{cmd:. {stata merge make using "`t'", sort}} {col 8}{cmd:. {stata list if _merge!=3}} {col 8}{cmd:. {stata strgroup make if _merge!=3, gen(group) threshold(0.25)}} {col 8}{cmd:. {stata list if _merge!=3}} {title:Acknowledgements} {p 4 4 2} The code used to calculate the Levenshtein edit distance is based on a Python extension written by David Necas (Yeti). His code is publicly available at {browse "http://code.google.com/p/pylevenshtein/source/browse/trunk/Levenshtein.c":http://code.google.com/p/pylevenshtein/source/browse/trunk/Levenshtein.c}. {p 4 4 2} Thanks to Dimitriy Masterov for compiling the 64-bit Windows version of the plugin. {title:Citation of strgroup} {p 4 4 2} {cmd:strgroup} is not an official Stata command. It is a free contribution to the research community. You may cite it as: {col 8} Reif, J., 2010. strgroup: Stata module to match strings based on their Levenshtein edit distance. {browse "http://ideas.repec.org/c/boc/bocode/s457151.html":http://ideas.repec.org/c/boc/bocode/s457151.html}. {title:Author} {p 4 4 2}Julian Reif, University of Illinois {p 4 4 2}jreif@illinois.edu {title:Also see} {p 4 4 2} {help levenshtein:levenshtein}, {help regexm:regexm}