Skip to Content
Course content

142: Fuzzy String Matching for Data Deduplication

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I actually measure how "different" two strings are in R?

When you're dealing with data deduplication, you can't just use == because "Acme Corp" and "Acme Corporation" aren't identical, even though they're clearly the same entity. This is where fuzzy matching comes in. In R, the stringdist package is the gold standard for this.

The most common way to measure difference is the Levenshtein distance. Think of this as the number of single-character edits (insertions, deletions, or substitutions) required to change one word into another. I usually start here because it's intuitive.

library(stringdist)

# Let's see the distance between two variations of a name
dist <- stringdist("Jonathon Smith", "Jonathan Smith", method = "lv")
print(dist) 
# Result is 1, because we only need to change 'o' to 'a'

Depending on your data, you might want to try "jw" (Jaro-Winkler), which gives more weight to strings that match at the beginning. In my experience, Jaro-Winkler is often better for names since people are more likely to typo the end of a word than the start.

How do I handle a whole dataframe instead of just comparing two strings?

Comparing two strings is easy, but you probably have a column of 10,000 rows that you need to deduplicate. You can't possibly write 10,000 stringdist calls. Instead, you can use stringdistmatrix to create a grid of every possible combination, or more efficiently, use stringdist on two vectors.

Here is how I typically approach finding duplicates within a single column:

# A messy list of company names
companies <- c("Apple Inc.", "Apple Incorporated", "Google LLC", "Google", "Microsoft Corp", "Microsoft")

# We'll create a distance matrix
dist_matrix <- stringdistmatrix(companies, companies, method = "jw")

# Now we find pairs where the distance is very low (but not 0, which is just the string matching itself)
matches <- which(dist_matrix > 0 & dist_matrix < 0.1, arr.ind = TRUE)
print(matches)

Just a heads-up: distance matrices grow quadratically. If you have 100,000 rows, a matrix will eat your RAM for breakfast. For massive datasets, you'll want to look into "blocking"—sorting the data by something like a Zip Code first, then only fuzzy matching within those blocks.

How do I decide where to draw the line for a "match"?

This is the hardest part of the process because there is no "correct" mathematical answer; it's a business decision. If your threshold is too loose, you'll merge two different customers into one (a False Positive). If it's too strict, you'll leave duplicates in your data (a False Negative).

I always recommend a "sampling and auditing" workflow. Pick a threshold—say, 0.1 for Jaro-Winkler—and extract a random sample of the matches it found. Manually inspect them. If you see too many wrong merges, tighten the threshold (lower the number). If you're still seeing obvious duplicates that weren't caught, loosen it.

Here is a quick way to visualize the distribution of distances to help you pick a number:

# Convert the matrix to a vector to see the spread of distances
all_dists <- as.vector(dist_matrix)
hist(all_dists, breaks = 50, main = "Distribution of String Distances")

Usually, you'll see a huge spike at 0 (exact matches) and then a gap before the "near misses" start. That gap is exactly where you want to place your threshold.




📋 Practical Task

Exercise: Merging Messy Client Company Names

You have been given a vector of client names that were entered manually by different sales reps. Your goal is to identify which names are likely duplicates and group them together.

client_names <- c("Global Tech Industries", "Global Tech Ind.", "Global Tech", 
                   "Vertex Solutions", "Vertex Solutons", "Vertex Solutions Inc", 
                   "OmniCorp", "Omni Corp", "Omni Corporation")

Your Task:

  • Load the stringdist package.
  • Create a distance matrix using the Jaro-Winkler ("jw") method.
  • Identify all pairs of indices that have a distance greater than 0 but less than 0.2.
  • Print the actual names of the matched pairs to verify that "Global Tech" variants and "Vertex" variants are being grouped correctly.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.