在 excel 中查找行值之间的文本相似性
Finding text similarities between row values in excel
假设我有 9 行记录。每 3 行具有相同的值。例如:
Mike
Mike
Mike
John
John
John
Ryan
Ryan
Ryan
有什么方法可以搜索这些记录的相似之处吗?例如拼写错误、附加字符、缺失字符等。因此,例如,正确的版本是 Mike
,但列表中可能存在值为 Mke
的记录,这是不正确的(拼写错误)。我怎样才能找到这个并用正确的替换它?
上面的例子明显简化了。我实际上有 ~100 万行。现在为了实现元素的 'grouping',我只是按字母顺序对它们进行排序。
我不知道完全自动化的方法。有一个 Excel "Fuzzy Match" 加载项可能有用:https://www.microsoft.com/en-us/download/details.aspx?id=15011
我用过。它大部分时间都有效,但在处理较大的工作表时遇到困难。
文本相似度可能会变得相当复杂,具体取决于您想要达到的程度。可以在本文 A Survey of Text Similarity Approaches(Gomaa & Fahmy,IJCA 2013)中找到对所有不同算法的完整调查。它可能会伤到你的头,但它是个好东西。
具体VBA可以参考这个previous answer on SO
我遇到了完全相同的问题!通过一些搜索,我可以获得并修改以下 VBA 代码,该代码将启用名为 =Similarity()
的函数。根据两个输入单元格的相似性,此函数将输出一个从 0 到 1 的数字。
- 我的使用方法:
我按字母顺序排列了我的列信息并应用了公式。然后我创建了一个 Conditional Formatting Rule
来突出显示具有高相似率(即:至少 65%)的那些。然后我搜索了每个突出显示的事件并手动修复了我的记录。
用法:
=Similarity(cell1, cell2)
Obs.:相似性指标从 0 到 1(0% 到 100%)
- 示例:
要使用它,您必须:
- 打开 VBE (Alt+F11)
- 插入模块
- 将以下代码粘贴到模块中 Window
代码:
Public Function Similarity(ByVal String1 As String, _
ByVal String2 As String, _
Optional ByRef RetMatch As String, _
Optional min_match = 1) As Single
Dim b1() As Byte, b2() As Byte
Dim lngLen1 As Long, lngLen2 As Long
Dim lngResult As Long
If UCase(String1) = UCase(String2) Then
Similarity = 1
Else:
lngLen1 = Len(String1)
lngLen2 = Len(String2)
If (lngLen1 = 0) Or (lngLen2 = 0) Then
Similarity = 0
Else:
b1() = StrConv(UCase(String1), vbFromUnicode)
b2() = StrConv(UCase(String2), vbFromUnicode)
lngResult = Similarity_sub(0, lngLen1 - 1, _
0, lngLen2 - 1, _
b1, b2, _
String1, _
RetMatch, _
min_match)
Erase b1
Erase b2
If lngLen1 >= lngLen2 Then
Similarity = lngResult / lngLen1
Else
Similarity = lngResult / lngLen2
End If
End If
End If
End Function
Private Function Similarity_sub(ByVal start1 As Long, ByVal end1 As Long, _
ByVal start2 As Long, ByVal end2 As Long, _
ByRef b1() As Byte, ByRef b2() As Byte, _
ByVal FirstString As String, _
ByRef RetMatch As String, _
ByVal min_match As Long, _
Optional recur_level As Integer = 0) As Long
'* CALLED BY: Similarity *(RECURSIVE)
Dim lngCurr1 As Long, lngCurr2 As Long
Dim lngMatchAt1 As Long, lngMatchAt2 As Long
Dim I As Long
Dim lngLongestMatch As Long, lngLocalLongestMatch As Long
Dim strRetMatch1 As String, strRetMatch2 As String
If (start1 > end1) Or (start1 < 0) Or (end1 - start1 + 1 < min_match) _
Or (start2 > end2) Or (start2 < 0) Or (end2 - start2 + 1 < min_match) Then
Exit Function '(exit if start/end is out of string, or length is too short)
End If
For lngCurr1 = start1 To end1
For lngCurr2 = start2 To end2
I = 0
Do Until b1(lngCurr1 + I) <> b2(lngCurr2 + I)
I = I + 1
If I > lngLongestMatch Then
lngMatchAt1 = lngCurr1
lngMatchAt2 = lngCurr2
lngLongestMatch = I
End If
If (lngCurr1 + I) > end1 Or (lngCurr2 + I) > end2 Then Exit Do
Loop
Next lngCurr2
Next lngCurr1
If lngLongestMatch < min_match Then Exit Function
lngLocalLongestMatch = lngLongestMatch
RetMatch = ""
lngLongestMatch = lngLongestMatch _
+ Similarity_sub(start1, lngMatchAt1 - 1, _
start2, lngMatchAt2 - 1, _
b1, b2, _
FirstString, _
strRetMatch1, _
min_match, _
recur_level + 1)
If strRetMatch1 <> "" Then
RetMatch = RetMatch & strRetMatch1 & "*"
Else
RetMatch = RetMatch & IIf(recur_level = 0 _
And lngLocalLongestMatch > 0 _
And (lngMatchAt1 > 1 Or lngMatchAt2 > 1) _
, "*", "")
End If
RetMatch = RetMatch & Mid$(FirstString, lngMatchAt1 + 1, lngLocalLongestMatch)
lngLongestMatch = lngLongestMatch _
+ Similarity_sub(lngMatchAt1 + lngLocalLongestMatch, end1, _
lngMatchAt2 + lngLocalLongestMatch, end2, _
b1, b2, _
FirstString, _
strRetMatch2, _
min_match, _
recur_level + 1)
If strRetMatch2 <> "" Then
RetMatch = RetMatch & "*" & strRetMatch2
Else
RetMatch = RetMatch & IIf(recur_level = 0 _
And lngLocalLongestMatch > 0 _
And ((lngMatchAt1 + lngLocalLongestMatch < end1) _
Or (lngMatchAt2 + lngLocalLongestMatch < end2)) _
, "*", "")
End If
Similarity_sub = lngLongestMatch
End Function
- 根据你的数据集输出:
假设我有 9 行记录。每 3 行具有相同的值。例如:
Mike
Mike
Mike
John
John
John
Ryan
Ryan
Ryan
有什么方法可以搜索这些记录的相似之处吗?例如拼写错误、附加字符、缺失字符等。因此,例如,正确的版本是 Mike
,但列表中可能存在值为 Mke
的记录,这是不正确的(拼写错误)。我怎样才能找到这个并用正确的替换它?
上面的例子明显简化了。我实际上有 ~100 万行。现在为了实现元素的 'grouping',我只是按字母顺序对它们进行排序。
我不知道完全自动化的方法。有一个 Excel "Fuzzy Match" 加载项可能有用:https://www.microsoft.com/en-us/download/details.aspx?id=15011
我用过。它大部分时间都有效,但在处理较大的工作表时遇到困难。
文本相似度可能会变得相当复杂,具体取决于您想要达到的程度。可以在本文 A Survey of Text Similarity Approaches(Gomaa & Fahmy,IJCA 2013)中找到对所有不同算法的完整调查。它可能会伤到你的头,但它是个好东西。
具体VBA可以参考这个previous answer on SO
我遇到了完全相同的问题!通过一些搜索,我可以获得并修改以下 VBA 代码,该代码将启用名为 =Similarity()
的函数。根据两个输入单元格的相似性,此函数将输出一个从 0 到 1 的数字。
- 我的使用方法:
我按字母顺序排列了我的列信息并应用了公式。然后我创建了一个 Conditional Formatting Rule
来突出显示具有高相似率(即:至少 65%)的那些。然后我搜索了每个突出显示的事件并手动修复了我的记录。
用法:
=Similarity(cell1, cell2)
Obs.:相似性指标从 0 到 1(0% 到 100%)
- 示例:
要使用它,您必须:
- 打开 VBE (Alt+F11)
- 插入模块
- 将以下代码粘贴到模块中 Window
代码:
Public Function Similarity(ByVal String1 As String, _
ByVal String2 As String, _
Optional ByRef RetMatch As String, _
Optional min_match = 1) As Single
Dim b1() As Byte, b2() As Byte
Dim lngLen1 As Long, lngLen2 As Long
Dim lngResult As Long
If UCase(String1) = UCase(String2) Then
Similarity = 1
Else:
lngLen1 = Len(String1)
lngLen2 = Len(String2)
If (lngLen1 = 0) Or (lngLen2 = 0) Then
Similarity = 0
Else:
b1() = StrConv(UCase(String1), vbFromUnicode)
b2() = StrConv(UCase(String2), vbFromUnicode)
lngResult = Similarity_sub(0, lngLen1 - 1, _
0, lngLen2 - 1, _
b1, b2, _
String1, _
RetMatch, _
min_match)
Erase b1
Erase b2
If lngLen1 >= lngLen2 Then
Similarity = lngResult / lngLen1
Else
Similarity = lngResult / lngLen2
End If
End If
End If
End Function
Private Function Similarity_sub(ByVal start1 As Long, ByVal end1 As Long, _
ByVal start2 As Long, ByVal end2 As Long, _
ByRef b1() As Byte, ByRef b2() As Byte, _
ByVal FirstString As String, _
ByRef RetMatch As String, _
ByVal min_match As Long, _
Optional recur_level As Integer = 0) As Long
'* CALLED BY: Similarity *(RECURSIVE)
Dim lngCurr1 As Long, lngCurr2 As Long
Dim lngMatchAt1 As Long, lngMatchAt2 As Long
Dim I As Long
Dim lngLongestMatch As Long, lngLocalLongestMatch As Long
Dim strRetMatch1 As String, strRetMatch2 As String
If (start1 > end1) Or (start1 < 0) Or (end1 - start1 + 1 < min_match) _
Or (start2 > end2) Or (start2 < 0) Or (end2 - start2 + 1 < min_match) Then
Exit Function '(exit if start/end is out of string, or length is too short)
End If
For lngCurr1 = start1 To end1
For lngCurr2 = start2 To end2
I = 0
Do Until b1(lngCurr1 + I) <> b2(lngCurr2 + I)
I = I + 1
If I > lngLongestMatch Then
lngMatchAt1 = lngCurr1
lngMatchAt2 = lngCurr2
lngLongestMatch = I
End If
If (lngCurr1 + I) > end1 Or (lngCurr2 + I) > end2 Then Exit Do
Loop
Next lngCurr2
Next lngCurr1
If lngLongestMatch < min_match Then Exit Function
lngLocalLongestMatch = lngLongestMatch
RetMatch = ""
lngLongestMatch = lngLongestMatch _
+ Similarity_sub(start1, lngMatchAt1 - 1, _
start2, lngMatchAt2 - 1, _
b1, b2, _
FirstString, _
strRetMatch1, _
min_match, _
recur_level + 1)
If strRetMatch1 <> "" Then
RetMatch = RetMatch & strRetMatch1 & "*"
Else
RetMatch = RetMatch & IIf(recur_level = 0 _
And lngLocalLongestMatch > 0 _
And (lngMatchAt1 > 1 Or lngMatchAt2 > 1) _
, "*", "")
End If
RetMatch = RetMatch & Mid$(FirstString, lngMatchAt1 + 1, lngLocalLongestMatch)
lngLongestMatch = lngLongestMatch _
+ Similarity_sub(lngMatchAt1 + lngLocalLongestMatch, end1, _
lngMatchAt2 + lngLocalLongestMatch, end2, _
b1, b2, _
FirstString, _
strRetMatch2, _
min_match, _
recur_level + 1)
If strRetMatch2 <> "" Then
RetMatch = RetMatch & "*" & strRetMatch2
Else
RetMatch = RetMatch & IIf(recur_level = 0 _
And lngLocalLongestMatch > 0 _
And ((lngMatchAt1 + lngLocalLongestMatch < end1) _
Or (lngMatchAt2 + lngLocalLongestMatch < end2)) _
, "*", "")
End If
Similarity_sub = lngLongestMatch
End Function
- 根据你的数据集输出: