7

У меня есть список +-8000 элементов, который является результатом применения многочисленных методов удаления дубликатов (список начинался с более чем 10000 элементов).

Теперь у меня есть следующая проблема -

Пример 1 Джек Даниэль 2. Джек Дэниелс 3. Jack Daniels

Очевидно, что все вышеперечисленное относится к одному предмету, но они все еще технически уникальны. Я попытался вытащить первые 4 буквы и проверить совпадения, однако их было +-4000, и большинство из них были ложными срабатываниями, т.е.

  1. Джек Dxxx
  2. Джек Bxxx

И то и другое потянет Джека, но не будет действительным дубликатом.

Какие-нибудь мысли?

2 ответа2

3

На основании комментария Зоредача, вот мой пример рабочей книги с использованием VBA и расстояния Левенштейна для поиска похожих строк в большом списке. Он основан на @smirkingman и @ Apostolos55 отвечает на StackOverflow.

Расстояние Левенштейна между двумя словами - это минимальное количество односимвольных правок (вставка, удаление, замена), необходимых для преобразования одного слова в другое.

Я реализовал две разные версии. Пожалуйста, проверьте, какая функция быстрее для вашего случая с 8000 значений. Если вам интересно, посмотрите полный код VBA на Github. Увеличьте порог в строке const treshold = 1 если вы хотите, чтобы результаты с более чем 1 требовали редактирования, чтобы получить совпадение где-либо.

  • Синтаксис формулы: =LevenshteinCompare( <cell_to_check> , <range_to_search_in> )
    Пример: =LevenshteinCompare(A2;A$2:A$12) (обратите внимание на фиксированный диапазон)
  • Выходной синтаксис: <number_of_required_edits> - [<match_address>] <match_value>

Private Function Levenshtein(S1 As String, S2 As String)

Dim i As Integer, j As Integer
Dim l1 As Integer, l2 As Integer
Dim d() As Integer
Dim min1 As Integer, min2 As Integer

l1 = Len(S1)
l2 = Len(S2)
ReDim d(l1, l2)
For i = 0 To l1
    d(i, 0) = i
Next
For j = 0 To l2
    d(0, j) = j
Next
For i = 1 To l1
    For j = 1 To l2
        If Mid(S1, i, 1) = Mid(S2, j, 1) Then
            d(i, j) = d(i - 1, j - 1)
        Else
            min1 = d(i - 1, j) + 1
            min2 = d(i, j - 1) + 1
            If min2 < min1 Then
                min1 = min2
            End If
            min2 = d(i - 1, j - 1) + 1
            If min2 < min1 Then
                min1 = min2
            End If
            d(i, j) = min1
        End If
    Next
Next
Levenshtein = d(l1, l2)
End Function


Public Function LevenshteinCompare(S1 As Range, wordrange As Range)

Const treshold = 1
For Each S2 In Application.Intersect(wordrange, wordrange.Parent.UsedRange)
    oldRes = newRes
    newRes = Levenshtein(S1.Value, S2.Value)
    If oldRes < newRes And oldRes <> "" Or S1.Address = S2.Address Then
        newRes = oldRes
        newS2row = oldS2row
    Else
        oldS2 = S2
        oldS2row = S2.Address(0, 0)
    End If
    newS2 = oldS2
Next

If newRes <= treshold Then
    LevenshteinCompare = newRes & " - [" & newS2row & "]  " & newS2
Else
    LevenshteinCompare = ""
End If
End Function

Это было весело ☜ (゚ ヮ ゚ ☜)

0

Используйте = len, и если разница меньше, чем 2, отметьте ее как возможную

и что-то вроде = mid(Value,(len_Value - 7), 4) пометить его как возможный dupe.

В сочетании с тем, что у вас уже есть, вы получите гораздо более выполнимый набор для работы.

редактировать

Формулы, подобные этой, обратите внимание, что "Джек Даниэль" <> "Джек Ягоды" в G2. но выглядит как возможное совпадение везде. Вам нужно будет немного изменить, чтобы удовлетворить ваши конкретные потребности, но он должен получить управляемое число.

C1 = IF(ВЛЕВО (A1,4)= ВЛЕВО (B1,4), "T", "F")

D1 = IF(LEN(A1) - LEN(B1) <= 2, "T", "F")

E1 = LEN(A1)

F1 = LEN(B1)

G1 = IF(MID(A1,(E1 - 7), 4) = MID(B1,(E1 - 7), 4), "T", "F")

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .