我有一个项目列表,我想确定它们与此列表中其他项目的相似性。文本相似度分析(Excel)
在相似性列中所示的百分比纯粹是说明性的。我想,对于相似的测试将是沿着线的东西:
并发字母/按字母在 匹配项
总数反而会热衷于在那个上得到意见。
这是在Excel上合理可行的东西吗?我只有一个只包含字母数字值的小数据集(140kb)。
我也接受这种方法,因为我之前没有处理过这样的事情!
P.s.我已经学习Python几个月了,所以使用Python的建议也会很好!
我有一个项目列表,我想确定它们与此列表中其他项目的相似性。文本相似度分析(Excel)
在相似性列中所示的百分比纯粹是说明性的。我想,对于相似的测试将是沿着线的东西:
并发字母/按字母在 匹配项
总数反而会热衷于在那个上得到意见。
这是在Excel上合理可行的东西吗?我只有一个只包含字母数字值的小数据集(140kb)。
我也接受这种方法,因为我之前没有处理过这样的事情!
P.s.我已经学习Python几个月了,所以使用Python的建议也会很好!
下面是一个使用VBA UDF的溶液:
编辑:添加了一个名为arg_lMinConsecutive
的新可选参数,用于确定必须匹配的最少连续字符数。请注意下列公式中的额外参数2
,它表示至少有2个连续字符必须匹配。
Public Function FuzzyMatch(ByVal arg_sText As String, _
ByVal arg_vList As Variant, _
ByVal arg_lOutput As Long, _
Optional ByVal arg_lMinConsecutive As Long = 1, _
Optional ByVal arg_bMatchCase As Boolean = True, _
Optional ByVal arg_bExactCount As Boolean = True) _
As Variant
Dim dExactCounts As Object
Dim aResults() As Variant
Dim vList As Variant
Dim vListItem As Variant
Dim sLetter As String
Dim dMaxMatch As Double
Dim lMaxIndex As Long
Dim lResultIndex As Long
Dim lLastMatch As Long
Dim i As Long
Dim bMatch As Boolean
If arg_lMinConsecutive <= 0 Then
FuzzyMatch = CVErr(xlErrNum)
Exit Function
End If
If arg_bExactCount = True Then Set dExactCounts = CreateObject("Scripting.Dictionary")
If TypeName(arg_vList) = "Collection" Or TypeName(arg_vList) = "Range" Then
ReDim aResults(1 To arg_vList.Count, 1 To 3)
Set vList = arg_vList
ElseIf IsArray(arg_vList) Then
ReDim aResults(1 To UBound(arg_vList) - LBound(arg_vList) + 1, 1 To 3)
vList = arg_vList
Else
ReDim vList(1 To 1)
vList(1) = arg_vList
ReDim aResults(1 To 1, 1 To 3)
End If
dMaxMatch = 0#
lMaxIndex = 0
lResultIndex = 0
For Each vListItem In vList
If vListItem <> arg_sText Then
lLastMatch = -arg_lMinConsecutive
lResultIndex = lResultIndex + 1
aResults(lResultIndex, 3) = vListItem
If arg_bExactCount Then dExactCounts.RemoveAll
For i = 1 To Len(arg_sText) - arg_lMinConsecutive + 1
bMatch = False
sLetter = Mid(arg_sText, i, arg_lMinConsecutive)
If Not arg_bMatchCase Then sLetter = LCase(sLetter)
If arg_bExactCount Then dExactCounts(sLetter) = dExactCounts(sLetter) + 1
Select Case Abs(arg_bMatchCase) + Abs(arg_bExactCount) * 2
Case 0
'MatchCase is false and ExactCount is false
If InStr(1, vListItem, sLetter, vbTextCompare) > 0 Then bMatch = True
Case 1
'MatchCase is true and ExactCount is false
If InStr(1, vListItem, sLetter) > 0 Then bMatch = True
Case 2
'MatchCase is false and ExactCount is true
If Len(vListItem) - Len(Replace(vListItem, sLetter, vbNullString, Compare:=vbTextCompare)) >= dExactCounts(sLetter) Then bMatch = True
Case 3
'MatchCase is true and ExactCount is true
If Len(vListItem) - Len(Replace(vListItem, sLetter, vbNullString)) >= dExactCounts(sLetter) Then bMatch = True
End Select
If bMatch Then
aResults(lResultIndex, 1) = aResults(lResultIndex, 1) + WorksheetFunction.Min(arg_lMinConsecutive, i - lLastMatch)
lLastMatch = i
End If
Next i
If Len(vListItem) > 0 Then
aResults(lResultIndex, 2) = aResults(lResultIndex, 1)/Len(vListItem)
If aResults(lResultIndex, 2) > dMaxMatch Then
dMaxMatch = aResults(lResultIndex, 2)
lMaxIndex = lResultIndex
End If
Else
aResults(lResultIndex, 2) = 0
End If
End If
Next vListItem
If dMaxMatch = 0# Then
Select Case arg_lOutput
Case 1: FuzzyMatch = 0
Case 2: FuzzyMatch = vbNullString
Case Else: FuzzyMatch = CVErr(xlErrNum)
End Select
Else
Select Case arg_lOutput
Case 1: FuzzyMatch = Application.Min(1, aResults(lMaxIndex, 2))
Case 2: FuzzyMatch = aResults(lMaxIndex, 3)
Case Else: FuzzyMatch = CVErr(xlErrNum)
End Select
End If
End Function
仅使用在列A和B中的原始数据,则可以使用此UDF获得列C中的期望的结果和d:
在小区C2和复制下来是这个公式:
=FuzzyMatch($B2,$B$2:$B$6,COLUMN(A2),2)
在单元格D2和复制下来是以下公式:
=IFERROR(INDEX(A:A,MATCH(FuzzyMatch($B2,$B$2:$B$6,COLUMN(B2),2),B:B,0)),"-")
请注意,它们都使用FuzzyMatch
UDF。
非常感谢!这实际上非常适合我正在工作的另一个项目。然而,这并不完全是我为这项工作所做的。我正在尝试匹配并发字母。而不仅仅是匹配事件。所以在上面的例子中柠檬应该等于0%。这个代码是否适合这样做? – Maverick
@Maverick即使对于并发字母,“e”和“n”也会给出至少一次匹配。你的意思是说,至少有两个连续的字母必须匹配? – tigeravatar
是的,我只是认为。我会说至少2个,也许不超过5-10个,但如果可能的话,能够调整的话会很好吗? – Maverick
在Python中,您可以使用Levenshtein距离来获得结果。看看这个答案:
Fuzzy string comparison in Python, confused with which library to use
谢谢,我会研究一下! – Maverick
我真的没有得到整个逻辑,但是如果你需要的逻辑在这里的100%是:
Option Explicit
Sub TestMe()
Dim rngCell As Range
Dim rngCell2 As Range
Dim lngTotal As Long
Dim lngTotal2 As Long
Dim lngCount As Long
For Each rngCell In Sheets(1).Range("A1:A5")
For Each rngCell2 In Sheets(1).Range("A1:A5")
If rngCell.Address <> rngCell2.Address Then
If InStr(1, rngCell, rngCell2) Then
rngCell.Offset(0, 1) = 1
Else
If InStr(1, rngCell2, rngCell) Then
rngCell.Offset(0, 2) = Round(CDbl(Len(rngCell)/Len(rngCell2)), 2)
End If
End If
End If
Next rngCell2
Next rngCell
End Sub
在这里,你去与PIC :
只需使用instr() - https://msdn.microsoft.com/en-us/library/8460tsh1(v=vs.90).aspx – Vityata
感谢那@Vityata。但在VBA上不那么热,所以不确定如何实现? – Maverick