【问题标题】:Issue counting word frequencies with VBA: same data, different numbers使用 VBA 计算词频:相同的数据,不同的数字
【发布时间】:2018-10-01 07:53:19
【问题描述】:

我在 VBA 中制作了两个不同的脚本来计算 CSV 中包含的单词的频率。两个脚本都运行良好,但我得到每个单词的不同数字,我不知道为什么。以下是导致差异出现的一些步骤

脚本 1:

Sub Dict_Array_1()

Dim Wb As Workbook, Wb1 As Workbook
Dim Ws As Worksheet, Ws1 As Worksheet
Dim Fd As Office.FileDialog
Dim StrFile As String
Dim i As Long, a As Long, LastR As Long
Dim Arr() As Variant
Dim Ban_() As String, T As String
Dim Ban As Object, Dict As Object
Dim Carac As Variant, w As Variant, Key As Variant 

Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.DisplayAlerts = False

Set Wb = ActiveWorkbook
Set Ws = Wb.ActiveSheet


'---------- CSV ---------------------------------------------------------------------------------------------------------------

Set Fd = Application.FileDialog(msoFileDialogFilePicker)
With Fd
  .AllowMultiSelect = False
  .Title = "Select doc"
  .Filters.Clear
  .Filters.Add "Doc CSV (*.csv)", "*.csv"

    If .Show Then

        On Error GoTo ErrOpen 'ignore this
        Set Wb1 = Workbooks.Open(.SelectedItems(1), ReadOnly:=True, Local:=False) 
        On Error GoTo 0

        Set Ws1 = Wb1.Sheets(1)
        With Ws1
            LastR = .Cells(.Rows.Count, "S").End(xlUp).Row 

            Arr = .Range(Cells(1, 19), Cells(LastR, 19)).Value2 
        End With

        Wb1.Close 0
        Set Wb1 = Nothing
        Set Ws1 = Nothing
    Else
        Exit Sub
    End If
End With

'---------------------------------------- COUNT ----------------------------------------------------------------------------------------------------
'Array with words i want to ban
Ban_ = Split("word1,word2,word3,etc", ",")

'Array with caract i want to ban
Carac = Array(".", ",", ";", ":", "!", "#", "$", "%", "&", "(", ")", "- ", "_", "--", "+", _
                            "=", "~", "/", "\", "{", "}", "[", "]", """", "?", "*", ">>", "»", "«")

Set Ban = CreateObject("Scripting.Dictionary") 'need late binding
Ban.CompareMode = vbTextCompare 'case insensitive
For i = 0 To UBound(Ban_)
    Ban.Add Ban_(i), 1
Next i
Erase Ban_

'Dict to count words
Set Dict = CreateObject("Scripting.Dictionary")
Dict.CompareMode = vbTextCompare 'case insensitive
For a = 1 To UBound(Arr, 1) 
    If Not IsError(Arr(a, 1)) 
        T = Arr(a, 1)
        For i = 0 To UBound(Carac)
            T = Replace(T, Carac(i), "", , , vbTextCompare) 
        Next i
        T = Application.Trim(T) 


        For Each w In Split(T, " ")
            If Not Ban.exists(w) Then
                If Not Dict.exists(w) Then
                    Dict.Add w, 1
                Else
                    Dict.Item(w) = Dict.Item(w) + 1 
                End If
            End If
        Next w
    End If
Next a
Exit Sub

Erase Arr
Erase Carac
Set Ban = Nothing

脚本2基本相同,唯一不同的是我以另一种方式访问​​.CSV:

Sub Dict_ADODB()
Dim Wb As Workbook, Wb1 As Workbook
Dim Ws As Worksheet, Ws1 As Worksheet
Dim Fd As Office.FileDialog
Dim StrFile As String
Dim i As Long, a As Long, LastR As Long
Dim Arr() As Variant
Dim Ban_() As String, T As String
Dim Ban As Object, Dict As Object
Dim Carac As Variant, w As Variant, Key As Variant 
Dim ObjC As Object, ObjR As Object 'Object Connection / Object Recordset
Const adOpenStatic = 3
Const adLockOptimistic = 3

Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
Application.DisplayStatusBar = False
Application.EnableEvents = False
ActiveSheet.DisplayPageBreaks = False
Application.DisplayAlerts = False

Set Wb = ActiveWorkbook
Set Ws = Wb.ActiveSheet


'---------- CSV ---------------------------------------------------------------------------------------------------------------

Set Fd = Application.FileDialog(msoFileDialogFilePicker)
With Fd
  .AllowMultiSelect = False
  .Title = "Select doc"
  .Filters.Clear
  .Filters.Add "Doc CSV (*.csv)", "*.csv"

    If .Show Then
        '----------- ADODB ---
        Set ObjC = CreateObject("ADODB.Connection")
        Set ObjR = CreateObject("ADODB.RecordSet")

        On Error GoTo ErrOpen 
        ObjC.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
                  "Data Source=" & .InitialFileName & ";" & _
                  "Extended Properties=""text;HDR=YES;FMT=Delimited;CharacterSet=65001""" 
        On Error GoTo 0
        'I just need one column
        ObjR.Open "SELECT Message FROM " & Right(.SelectedItems(1), Len(.SelectedItems(1)) - InStrRev(.SelectedItems(1), "\")) & _
                    " WHERE Message IS NOT NULL", _
          ObjC, adOpenStatic, adLockOptimistic
        Arr = ObjR.GetRows() 

        ObjR.Close
        ObjC.Close
        Set ObjR = Nothing
        Set ObjC = Nothing
    Else
        Exit Sub
    End If
End With

'---------------------------------------- COUNT ----------------------------------------------------------------------------------------------------
'Array with word I don't need
Ban_ = Split("word1,word2", ",")

Carac = Array(".", ",", ";", ":", "!", "#", "$", "%", "&", "(", ")", "- ", "_", "--", "+", _
                            "=", "~", "/", "\", "{", "}", "[", "]", """", "?", "*", ">>", "»", "«")

Set Ban = CreateObject("Scripting.Dictionary") 
Ban.CompareMode = vbTextCompare 
For i = 0 To UBound(Ban_)
    Ban.Add Ban_(i), 1
Next i
Erase Ban_

Set Dict = CreateObject("Scripting.Dictionary")
Dict.CompareMode = vbTextCompare 'case insensitive
For a = 0 To UBound(Arr, 2) 
    If Not IsError(Arr(0, a)) Then 
        T = Arr(0, a)
        For i = 0 To UBound(Carac)
            T = Replace(T, Carac(i), "", , , vbTextCompare) 
        Next i
        T = Application.Trim(T) 

        For Each w In Split(T, " ")
            If Not Ban.exists(w) Then
                If Not Dict.exists(w) Then
                    Dict.Add w, 1
                Else
                    Dict.Item(w) = Dict.Item(w) + 1 
                End If
            End If
        Next w
    End If
Next a

Erase Arr
Erase Carac
Set Ban = Nothing
Exit Sub

给你。当我执行 dict.count 时,我确实发现条目的总数是不同的,这只是部分地通过使用“WHERE Message IS NOT NULL”来解释。任何想法为什么将不胜感激!

【问题讨论】:

  • 你能在每次创建后Debug.Print UBound(Arr) 看看这两个数组的大小是多少?
  • 行尾分隔符不一致?
  • Ubound(Arr) 与脚本 1 = 299 988 ; UBound(Arr, 2) with Script 2 =282 975 (注意:我使用 UBound(Arr, 2),因为 GetRows 将记录从记录集中复制到二维数组中,并且“第一个下标标识字段,第二个标识记录号”)。
  • @LoBellin 所以我猜脚本 1 返回的字数比脚本 2 高?

标签: arrays excel ms-access vba


【解决方案1】:

查看发生了什么的最好情况是在这一行写一些日志:

Dict.Add w, 1

例如,如果值达到 200,则编写:

Dim cnt as long
Dict.Add w, 1
cnt = cnt + 1
Debug.Print cnt, w

如果值大于 200,那么即时窗口上只会显示最后的 200,因此对您没有多大帮助。您可以使用日志构建一个字符串,并在记事本中打印该字符串。

Dim cnt       as Long
Dim logString as String
Dict.Add w, 1
cnt = cnt + 1
logString = logString & VbCrLF & cnt, w

最后CreateLogFile logString:

Sub CreateLogFile(Optional strPrint As String)

    Dim fs                      As Object
    Dim obj_text                As Object
    Dim str_filename            As String
    Dim str_new_file            As String
    Dim str_shell               As String

    str_new_file = "\tests_info\"

    str_filename = ThisWorkbook.Path & str_new_file
    If Dir(ThisWorkbook.Path & str_new_file, vbDirectory) = vbNullString Then
         MkDir ThisWorkbook.Path & str_new_file
    End If

    Set fs = CreateObject("Scripting.FileSystemObject")
    Set obj_text = fs.CreateTextFile(str_filename & "\sometext.txt", True)

    obj_text.writeline (strPrint)
    obj_text.Close

    str_shell = "C:\WINDOWS\notepad.exe "
    str_shell = str_shell & str_filename & "\sometext.txt"
    Shell str_shell

End Sub

【讨论】:

  • 我终于发现记录集中有很多长字符串(数百个字符)被忽略(但不知道为什么)。我用 schema.ini 文件部分解决了这个问题,但总体计数仍然存在一些差异......
【解决方案2】:

好的,使用 Schema.ini 似乎解决了我的问题。文档中不清楚的一点是,应该为 CSV 中的每一列设置“colX = Y Type”,直到他想要选择的那一列(起初我只设置了“Col19 = Message”,但它失败了,因为前面的列哪里没有设置...)。

我将代码的相关部分分享给任何感兴趣的人(Excel 2010 / X86 版本):

  Set fs = CreateObject("Scripting.FileSystemObject")
  Set obj_text = fs.CreateTextFile(.InitialFileName & "\Schema.ini", True) 
  obj_text.write ("[" & Right(.SelectedItems(1), Len(.SelectedItems(1)) - InStrRev(.SelectedItems(1), "\")) & "]" & vbNewLine & _
                  "ColNameHeader=False" & vbNewLine & _
                  "CharacterSet=65001" & vbNewLine & _
                  "Format=CSVDelimited" & vbNewLine & _
                  "DecimalSymbol=." & vbNewLine & _
                  "Col1=1 Text" & vbNewLine & _
                  "Col2=2 Text" & vbNewLine & _
                  "Col3=3 Text" & vbNewLine & _
                  "Col4=4 Text" & vbNewLine & _
                  "Col5=5 Text" & vbNewLine & _
                  "Col6=6 Text" & vbNewLine & _
                  "Col7=7 Text" & vbNewLine & _
                  "Col8=8 Text" & vbNewLine & _
                  "Col9=9 Text" & vbNewLine & _
                  "Col10=10 Text" & vbNewLine & _
                  "Col11=11 Text" & vbNewLine & _
                  "Col12=12 Text" & vbNewLine & _
                  "Col13=13 Text" & vbNewLine & _
                  "Col14=14 Text" & vbNewLine & _
                  "Col15=15 Text" & vbNewLine & _
                  "Col16=16 Text" & vbNewLine & _
                  "Col17=17 Text" & vbNewLine & _
                  "Col18=18 Text" & vbNewLine & _
                  "Col19=GOODONE Memo") 'set all the previous cols until the one I need!
  obj_text.Close

  Set ObjC = CreateObject("ADODB.Connection")
  Set ObjR = CreateObject("ADODB.RecordSet")

  ObjC.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
            "Data Source=" & .InitialFileName & ";" & _
            "Extended Properties=""text;HDR=No;"""

  ObjR.Open "SELECT GOODONE FROM " & Right(.SelectedItems(1), Len(.SelectedItems(1)) - InStrRev(.SelectedItems(1), "\")), _
    ObjC, 0, 1 

  Arr = ObjR.GetRows() 

【讨论】:

    猜你喜欢
    • 2020-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-05
    • 1970-01-01
    相关资源
    最近更新 更多