Цикл расширенного фильтра оптимизации скорости выполнения с вычислением каждой записи

Проблема: код выполняется примерно за 30 секунд на запись. Нужно оптимизировать скорость. Электронная таблица построена вокруг 1 страницы из 100 000 записей, одна запись сравнивается с 100 другими записями из списка с использованием расширенного фильтра и поиска. Вычисляются различные корректировки, а затем значения из расчета возвращаются обратно на «страницу вывода» примерно для 60 000 записей. Выпуск составляет 60 000 записей * 30 секунд = 500 часов. Спасибо.

Sub EquityAutomated()
    Dim i As Long
    Dim StartNo As Long
    Dim EndNo As Long
    StartNo = InputBox("Enter the row on the Hsheet sheet you want the equity analysis to start on")
    EndNo = InputBox("Enter the row on the Hsheet sheet you want the equity analysis to end on")
    Dim wsProtestTest As Worksheet: Set wsProtestTest = Worksheets("ProtestTestData")
    Dim wsES As Worksheet:          Set wsES = Worksheets("EquitySpreadsheet")
    Dim wsEL As Worksheet:          Set wsEL = Worksheets("EquityList")
    Dim wsDa As Worksheet:          Set wsDa = Worksheets("Res")
    Dim subTotalsDa As Range:       Set subTotalsDa = wsDa.Range("A10:A647649")
    Dim fltrRng As Range:           Set fltrRng = wsDa.Range("A9:T647649")
    Dim fltrCritRng As Range:       Set fltrCritRng = wsDa.Range("A1:T2")
    Dim valRngDa As Range:          Set valRngDa = wsDa.Range("T10:T647649")
    Dim fullSrtRng As Range:        Set fullSrtRng = wsDa.Range("A9:S647649")
    Dim sortValRng As Range:        Set sortValRng = wsDa.Range("T9")
    Dim fullSortRngVal As Range:    Set fullSortRngVal = wsDa.Range("A10:T647649")
    Dim equityRankRng As Range:     Set equityRankRng = wsEL.Range("P5")
    Dim equityOutOfRng As Range:    Set equityOutOfRng = wsEL.Range("P4")
    Dim MedianRng As Range:         Set MedianRng = wsEL.Range("O6")
    Dim propValRng As Range:        Set propValRng = wsEL.Range("D5")
    Dim diffRng As Range:           Set diffRng = wsEL.Range("O7")
    Dim MinRng As Range:            Set MinRng = wsEL.Range("O8")
    Dim MaxRng As Range:            Set MaxRng = wsEL.Range("O9")
    Dim avgRng As Range:            Set avgRng = wsEL.Range("O10")
    Dim LogRng As Range:          Set LogRng = wsES.Range("B10")
    Dim Support3kLowerRng As Range:    Set Support3kLowerRng = wsEL.Range("O11")
    Application.ScreenUpdating = False
    For i = StartNo To EndNo
        LogRng = wsProtestTest.Cells(i + 2, 1).Value2
        subTotalsDa.ClearContents
        Application.Calculate
            If Not Application.CalculationState = xlDone Then
                DoEvents
            End If
        Application.Calculation = xlManual
        fltrRng.AdvancedFilter Action:=xlFilterInPlace, _
            CriteriaRange:=fltrCritRng, Unique:=False
        Application.Calculation = xlCalculationAutomatic
        Application.Calculate
        subTotalsDa.SpecialCells(xlCellTypeVisible).FormulaR1C1 = _
            "=Subtotal(3,R10C2:RC[1])"
        valRngDa.SpecialCells(xlCellTypeVisible).Formula = _
            "=INDEX(EquitySpreadsheet!$C$12:$GT$29,16,(MATCH(INDIRECT(ADDRESS(ROW(),1)),EquitySpreadsheet!$C$12:$GS$12)+1))"
        With wsDa.Sort
            .SortFields.Clear
            .SortFields.Add Key:=valRngDa, SortOn:=xlSortOnValues, _
                Order:=xlAscending, DataOption:=xlSortNormal
            .SetRange fullSortRngVal
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .Apply
        End With
        With wsProtestTest
            .Cells(i + 2, 29) = equityRankRng: .Cells(i + 2, 30) = equityOutOfRng: .Cells(i + 2, 31) = Support3kLowerRng
            .Cells(i + 2, 32) = MedianRng:     .Cells(i + 2, 33) = propValRng
            .Cells(i + 2, 34) = diffRng:      .Cells(i + 2, 35) = MinRng
            .Cells(i + 2, 36) = MaxRng:       .Cells(i + 2, 37) = avgRng
        End With
    Next
    Application.ScreenUpdating = True
End Sub

Редактировать: Фактически, что происходит: 1. Журнал используется для извлечения критериев для расширенного фильтра. 2. Столбец скорректированных значений очищается (промежуточные итоги), чтобы его можно было повторно заполнить позже после вычисления 3. Расширенный фильтр запускается с использованием критериев из более раннего 4. После запуска фильтра данные в возвращенных ячейках вытягиваются на лист (промежуточный итог используется для производить идентификацию для индекса/соответствия 1,2,3 и т. д. для каждой возвращаемой записи). Индекс/совпадение используется для заполнения различных элементов настройки, а затем используются базовые формулы для определения надлежащей настройки объекта (квадратные метры и т. д.). 5. Различные суммы суммируются на расчетном листе, чтобы получить «указанное значение». Затем это заполняет «valRng», используя соответствие индекса, которое вы видите в макросе. 6. Отфильтрованные данные сортируются в порядке возрастания на основе valRng. 7. Значения выносятся на итоговый лист для архивирования, так как остальная часть рабочей книги обновляется с каждой новой записью. (С разделом wsProtestTest).


person Shawn007    schedule 23.01.2016    source источник
comment
Я ценю понимание. Я видел идею массива, когда копался. Однако я не думаю, что это будет работать в моем приложении. Препятствие в том, что мне нужно 100 записей из набора данных (comps) по сравнению с 1 (subject). Затем значения вносятся в расчетную сетку для корректировки сопоставимости (с использованием индекса/соответствия и формул для сравнения характеристик субъекта и сравнения). Скорректированные значения затем выводятся на другую вкладку. Кажется, массив не выполняет этого, поправьте меня, если я ошибаюсь.   -  person Shawn007    schedule 24.01.2016
comment
647 649 записей (100 000 сверху). Это уже было отфильтровано для удаления некритических данных (остались только необходимые строки и столбцы).   -  person Shawn007    schedule 24.01.2016
comment
Добавлено примечание внизу исходного поста, чтобы, надеюсь, прояснить, что происходит.   -  person Shawn007    schedule 24.01.2016
comment
Низкая скорость вашего макроса связана с тем, как настроена ваша рабочая книга. Сортировка, фильтрация, вычисления, чтение и запись между VBA и Excel занимают много системных ресурсов. На данный момент я не могу придумать приемлемого решения вашей проблемы без радикального пересмотра всей рабочей тетради.   -  person SilentRevolution    schedule 24.01.2016
comment
Есть ли способ, которым я мог бы выполнить вычисления с большим количеством ресурсов (облако, обновить компьютер и т. д.)? Модель работает, просто приходится каждый раз просчитывать. Видите ли вы небольшие улучшения в коде?   -  person Shawn007    schedule 24.01.2016
comment
Я не уверен, как повлияет обновление вашего оборудования или есть ли у Excel облачные возможности, и я не собираюсь советовать по этому поводу. Что касается кода, я не вижу быстрых исправлений, которые могли бы повлиять на 500 часов.   -  person SilentRevolution    schedule 24.01.2016


Ответы (1)


Я не могу понять ваш общий процесс расчета, но некоторые вещи выделяются как нуждающиеся в изменении:

  • Вместо обработки ячеек в диапазоне по одной (например, C6: C11) используйте массив вариантов
  • Run the whole loop in Manual calculation mode and use application.Calculate as infrequently as possible.
    • Find an alternative way of achieving your desired results that does not involve sorting and filtering 650000 rows 60000 times!
    • ДВССЫЛ — это изменчивая функция, которая замедлит вычисления: найдите другой способ выполнения вычислений, который не включает ДВССЫЛ.
person Charles Williams    schedule 24.01.2016
comment
Я воспользовался вашим советом по Косвенному. Я упростил его до: valRngDa.SpecialCells(xlCellTypeVisible).Formula = _ =INDEX(EquitySpreadsheet!$C$12:$GT$29,16,RC[1]*2) - person Shawn007; 24.01.2016
comment
Однако я не думаю, что структура рабочей книги позволяет использовать массивы или устанавливать режим ручного расчета. Я описал почему выше. Каждый раз, когда записи возвращаются, выполняются вычисления и архивируются перед переходом к следующей итерации цикла. Без пересчета в цикле одно и то же значение будет возвращаться снова и снова. Если у вас есть другие предложения, я был бы признателен. Я придумал альтернативный метод, но, кажется, не могу его придумать (намекает на вопрос). - person Shawn007; 24.01.2016
comment
Чтобы уточнить мои первоначальные комментарии: A) установите для расчета значение «Вручную» и используйте только Application.Calculate каждый раз, когда вам нужно пересчитать рабочую книгу. Б) снова посмотрите на массивы - например, вы можете использовать массив для C6:C11 вместо отдельных переменных и ячеек. В) Мне трудно представить, что несколько необычная методология, которую вы используете, является единственно возможным способом решения проблемы. - person Charles Williams; 25.01.2016
comment
Я не вижу, где я могу использовать массив. Проблема расчета описана выше. Существуют вычисления, которые каждый раз выполняются с использованием возвращаемых данных. Если возвращаются новые данные, требуется новый расчет. Я долго думал об альтернативах и ничего не придумал. Это может быть связано с моей неопытностью писать в vba. Любые предложения приветствуются. - person Shawn007; 26.01.2016
comment
Может быть, вы не поняли, что заставляете Excel делать как минимум 16 пересчетов рабочей книги для каждой итерации вашего цикла? А почему вы думаете, что нельзя использовать массив для C6:C11? - person Charles Williams; 26.01.2016
comment
Я не вижу, где я вообще использую диапазон C6:C11, может быть, я просто слепой. Я отмечу, где мне нужно пересчитать, и посмотрю, смогу ли я минимизировать вычисления. Спасибо за советы. - person Shawn007; 26.01.2016
comment
Я думаю, что понимаю комментарии к массиву, записываю значения в массив, а не на рабочий лист внутри цикла. Затем вне цикла запишите массив на рабочий лист. Это имеет смысл. Я могу использовать другой массив для каждого столбца без проблем с производительностью, верно? Это должно было бы иметь дело только с одномерными массивами. - person Shawn007; 27.01.2016
comment
Улучшения в методе расчета сократили время на треть (сейчас 10 секунд). - person Shawn007; 27.01.2016