Как оптимизировать / ускорить этот код, чтобы я мог обрабатывать большой набор данных? - PullRequest
0 голосов
/ 14 апреля 2019

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

Так что код ниже работает, но мне нужно "пройти" до 41000 (х). Так что это будет немного тяжело, поэтому, прежде чем я попробую, есть ли что-нибудь, что можно сделать с точки зрения оптимизации?

Спасибо!

Sub Finddata()
Dim Startdate As Date
Dim Finalrow As Long
Dim EndDate As Date
Dim Targetperiod As Integer
Dim Company As String
Dim i As Long
Dim d As Integer
Dim x As Long
Dim duplicaterow As Integer
Dim Newduplicaterow As Integer
Dim SourceBook As Workbook
Dim Datasheet As Worksheet, Duplicatesheet As Worksheet

Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False

Set SourceBook = ActiveWorkbook
Set Datasheet = SourceBook.Sheets("Data")
Set Duplicatesheet = SourceBook.Sheets("Duplicate sheet")

Finalrow = Datasheet.Range("A60000").End(xlUp).Row


For x = 2 To 10

Startdate = Datasheet.Range("r" & x)
EndDate = Datasheet.Range("q" & x)
Company = Datasheet.Range("p" & x)
Targetperiod = Datasheet.Range("i" & x)


'Copy data to duplicate sheet
For i = 2 To Finalrow
    If (Cells(i, 17) >= Startdate And Cells(i, 17) <= EndDate And Cells(i, 16) = Company And Cells(i, 9) = Targetperiod) Then
    Range(Cells(i, 1), Cells(i, 19)).Copy
    Duplicatesheet.Range("a10000").End(xlUp).Offset(1, 21).PasteSpecial xlPasteValues
    Duplicatesheet.Range("a10000").End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
    End If
Next i

'Definition
duplicaterow = Duplicatesheet.Range("v10000").End(xlUp).Row

'Removes duplicate analyst names
Duplicatesheet.Range("v1", "an" & duplicaterow).RemoveDuplicates Columns:=14, Header:=xlYes

'Definition
Newduplicaterow = Duplicatesheet.Range("v10000").End(xlUp).Row + 1

'Removes two columns that are not needed
Duplicatesheet.Range("Am:An").EntireColumn.Delete

'Below is for finding most recent observation and Target price.
d = 2

Do While Duplicatesheet.Cells(d, 38).Value <> ""
    Duplicatesheet.Cells(d, 39).FormulaLocal = "=MAX(IF('Duplicate sheet'!n:n='Duplicate sheet'!Ai" & d & ";'Duplicate sheet'!q:q;))"
    d = d + 1
Loop

  Dim c As Range
  For Each c In Duplicatesheet.Range("Am2", "am" & Newduplicaterow)
    c.FormulaArray = c.FormulaR1C1
  Next c

d = 2

Do While Duplicatesheet.Cells(d, 38).Value <> ""
    Duplicatesheet.Cells(d, 39).Value = Duplicatesheet.Cells(d, 39).Value
    Duplicatesheet.Cells(d, 40).Value = Duplicatesheet.Cells(d, 35) & ", " & Duplicatesheet.Cells(d, 39)
    d = d + 1
Loop

d = 2

Do While Duplicatesheet.Cells(d, 38).Value <> ""
    Duplicatesheet.Cells(d, 41).FormulaLocal = "=index('Duplicate sheet'!d:d;match('Duplicate sheet'!AN" & d & ";'Duplicate sheet'!s:s;0);0)"
    Duplicatesheet.Cells(d, 41).Value = Duplicatesheet.Cells(d, 41).Value
    d = d + 1
Loop


'This section creates the values that are needed in the data sheet, for consensus
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 0).FormulaLocal = "=Average(AO2:AO" & Newduplicaterow - 1 & ")"
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 1).FormulaLocal = "=iferror(STDEV.S(AO2:AO" & Newduplicaterow - 1 & ");count(AO2:AO" & Newduplicaterow - 1 & "))"
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 2).FormulaLocal = "=MEDIAN(AO2:AO" & Newduplicaterow - 1 & ")"
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 3).FormulaLocal = "=Min(AO2:AO" & Newduplicaterow - 1 & ")"
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 4).FormulaLocal = "=max(AO2:AO" & Newduplicaterow - 1 & ")"
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 0).Value = Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 0).Value
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 1).Value = Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 1).Value
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 2).Value = Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 2).Value
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 3).Value = Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 3).Value
Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 4).Value = Duplicatesheet.Range("v" & Newduplicaterow + 1).Offset(0, 4).Value

Duplicatesheet.Range("v" & Newduplicaterow + 1, "z" & Newduplicaterow + 1).Copy
Datasheet.Range("t" & x).PasteSpecial xlPasteValues

Duplicatesheet.Range("A2:BB6000").ClearContents

Next x


Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Application.EnableEvents = True

End Sub

1 Ответ

0 голосов
/ 14 апреля 2019

Трудно сказать, не видя структуру и структуру ваших данных и рабочей книги (в случае, если есть более простой / более эффективный способ того, что вы делаете). Но ниже приведены некоторые основные наблюдения.


Вероятно, вы должны использовать Range.AutoFilter ниже (вместо того, чтобы проходить по десяткам тысяч строк по одной строке за раз). Затем скопируйте и вставьте Range.SpecialCells(xlCellTypeVisible) в duplicateSheet за один раз. Вам может потребоваться преобразовать ваши даты в двойные (с функцией CDbl()) при указании критериев фильтра даты. (Вы также можете прочитать массив один раз. С массивом производительность может быть выше, но вам также придется писать больше кода.)

    'Copy data to duplicate sheet
    For i = 2 To finalRow
        If (Cells(i, 17) >= startDate And Cells(i, 17) <= EndDate And Cells(i, 16) = Company And Cells(i, 9) = Targetperiod) Then
            Range(Cells(i, 1), Cells(i, 19)).Copy
            duplicateSheet.Range("a10000").End(xlUp).Offset(1, 21).PasteSpecial xlPasteValues
            duplicateSheet.Range("a10000").End(xlUp).Offset(1, 0).PasteSpecial xlPasteValues
        End If
    Next i

Ниже вы назначаете то, что должно быть формулой массива как формула без массива, для каждой ячейки в столбце AM (по одной строке за раз); затем выполните цикл снова (по одной строке за раз), чтобы превратить формулу без массива в формулу массива; затем выполните цикл снова (по одной строке за раз), чтобы преобразовать формулы в статические значения.

Есть ли причина, по которой вы не можете установить формулу массива для всего диапазона за один раз? Я верю, что Excel позаботится об относительных ссылках на ячейки для вас. Затем превратить формулы указанного диапазона в статические значения. (В качестве альтернативы вы можете прочитать значения диапазона в массив и вычислить условное значение MAX в памяти. Но, как уже было сказано, вам нужно будет написать больше кода.)

Кроме того, некоторые новые версии Excel изначально имеют функцию MAXIFS. Если у вас есть доступ к нему, попробуйте использовать это. Другое наблюдение состоит в том, что ваша формула массива относится ко всему столбцу. Возможно, стоит ограничить диапазон (т. Е. До последней использованной строки в этом столбце), поэтому вы смотрите только <100 тыс. Ячеек (например) вместо 1 млн. </p>

И последнее: конкатенация строк может быть дорогостоящей. Как правило, вы хотите использовать некоторую форму функции JOIN при конкатенации, хотя я не уверен, насколько это повысит производительность, так как вы объединяете только два значения (для каждой итерации цикла).

    'Below is for finding most recent observation and Target price.
    d = 2

    Do While duplicateSheet.Cells(d, 38).Value <> ""
        duplicateSheet.Cells(d, 39).FormulaLocal = "=MAX(IF('Duplicate sheet'!n:n='Duplicate sheet'!Ai" & d & ";'Duplicate sheet'!q:q;))"
        d = d + 1
    Loop

    Dim c As Range
    For Each c In duplicateSheet.Range("Am2", "am" & Newduplicaterow)
      c.FormulaArray = c.FormulaR1C1
    Next c

    d = 2

    Do While duplicateSheet.Cells(d, 38).Value <> ""
        duplicateSheet.Cells(d, 39).Value = duplicateSheet.Cells(d, 39).Value
        duplicateSheet.Cells(d, 40).Value = duplicateSheet.Cells(d, 35) & ", " & duplicateSheet.Cells(d, 39)
        d = d + 1
    Loop

Вероятно, вы можете назначить приведенную ниже формулу всему диапазону за один раз. Следует отметить, что предоставление 0 в качестве третьего аргумента MATCH означает, что вы выполняете линейный поиск (для каждой итерации цикла). Рассмотрите возможность использования словаря или коллекции для более быстрого поиска (словарь, вероятно, более удобен, так как имеет метод Exists). В вашем случае, я думаю, что значения в столбце S будут ключами, а значения в столбце D будут соответствующими значениями ключей.

Кроме того, вы можете преобразовать весь диапазон в статические значения за один раз (вместо зацикливания по одной строке за раз).

    d = 2

    Do While duplicateSheet.Cells(d, 38).Value <> ""
        duplicateSheet.Cells(d, 41).FormulaLocal = "=index('Duplicate sheet'!d:d;match('Duplicate sheet'!AN" & d & ";'Duplicate sheet'!s:s;0);0)"
        duplicateSheet.Cells(d, 41).Value = duplicateSheet.Cells(d, 41).Value
        d = d + 1
    Loop

Надеюсь, это даст вам некоторые идеи о том, с чего начать.

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