【问题标题】:Finding text similarities between row values in excel在excel中查找行值之间的文本相似性
【发布时间】:2017-07-28 04:25:39
【问题描述】:

假设我有 9 行记录。每 3 行具有相同的值。例如:

Mike  
Mike  
Mike  
John  
John  
John  
Ryan  
Ryan  
Ryan

有没有办法可以搜索这些记录的相似之处?例如拼写错误、附加字符、缺失字符等。例如,正确的版本是Mike,但列表中可能有一条记录值Mke 不正确(拼写错误)。我怎样才能找到它并用正确的替换它?

上面的例子显然被简化了。我实际上有大约 100 万行。现在为了实现元素的“分组”,我只是按字母顺序对它们进行排序。

【问题讨论】:

    标签: excel duplicates similarity


    【解决方案1】:

    我也遇到了同样的问题!通过几次搜索,我可以获得并修改以下 VBA 代码,该代码将启用名为 =Similarity() 的函数。该函数将根据两个输入单元格的相似度输出一个从 0 到 1 的数字。

    • 我是如何使用它的:

    我按字母顺序排列了我的列信息并应用了公式。然后我创建了一个Conditional Formatting Rule 来突出显示具有高相似率(即:至少 65%)的那些。然后我搜索每个突出显示的事件并手动修复我的记录。

    • 用法:

      =Similarity(cell1, cell2)
      

    Obs.:相似度指标从 0 到 1(0% 到 100%)

    • 示例:

    • 要使用它,您必须:

      1. 打开 VBE (Alt+F11)
      2. 插入模块
      3. 将以下代码粘贴到模块窗口中

    代码:

    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
    
    • 根据您的数据集输出:

    【讨论】:

    • 谢谢你的回答,它工作正常。现在的问题是我有大约 11k 的“正确”值。所以手工工作需要很多时间。你有什么想法我将如何自动化这些东西?
    • 嗯...我对词干提取和词形还原有点了解,但是将他们的算法应用于名称有点困难,因为名称不尊重语法规则。我的意思是,Myike、Myke 和 Miyke 在语法上(和社交上?)都是可以接受的,那么当你的程序找到例如“Myke”时,它如何知道哪个值是“正确的”?它应该用“Mike”代替还是什么?也许您可以从具有“高度相似性”的 11k 行中删除重复项,然后您可以使用“名称字典”、IF()SUBSTITUTE() 和 @ 创建一个用另一个名称替换名称的函数987654333@.
    【解决方案2】:

    文本相似性可能会变得相当复杂,具体取决于您想要达到的程度。在这篇论文A Survey of Text Similarity Approaches(Gomaa & Fahmy, IJCA 2013)中可以找到对所有不同算法的完整调查。它可能会伤到你的头,但这是个好东西。

    VBA具体可以参考这个previous answer on SO

    【讨论】:

      【解决方案3】:

      我不知道有一种完全自动化的方法可以做到这一点。有一个 Excel“模糊匹配”插件可能会有所帮助:https://www.microsoft.com/en-us/download/details.aspx?id=15011

      我用过。它在大多数情况下都有效,但在处理更大的工作表时会遇到困难。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-23
        • 2011-07-31
        • 2015-07-09
        • 1970-01-01
        • 1970-01-01
        • 2020-09-27
        相关资源
        最近更新 更多