Excel VBA. Помогите оптимизировать код макроса для создания массива из отдельных ячеек!
Проблема:
Есть набор экспериментальных данных размером несколько гигабайт.
Их просчет в Excel'е с тем макросом, что у меня есть, занимает около месяца!!!
Суть в том, что для анализа данных используется Excel'евская формула TTEST, которая понимает только массивы значений.
ActiveCell.FormulaR1C1 = "=TTEST(RC[-2]:R[" & U - 1 & "]C[-2],RC[-1]:R[" & U - 1 & "]C[-1],2,1)"
Поэтому сначала надо сформировать массивы из отдельных ячеек расположенных в разных местах.
Эту задачу выполняет эта часть макроса, которая занимает порядка 90% просчетного времени!
While Y < (U - 1) ' число испытуемых минус один
X = X - (NHZ + Z) * 2
ActiveCell.Offset(-Y, -X).Select
Selection.Copy
Y = Y + 1
ActiveCell.Offset(Y, X).Select
ActiveSheet.Paste
Wend
Мне кажется, что проблема в том, что эта часть макроса двигает активное окно, каждый раз, когда ищет следующую ячейку! И из-за этого сильно тормозит!
Подскажите, можно ли как-то оптимизировать этот участок кода?
А вот полный код всего макроса:
Sub IHNA_20091223b_TTest()
'
' IHNA_20091223b_TTest Макрос
'
'
Dim S
Dim X As Integer ' оператор указателя на начальную ячейку ввода значений TTesta
Dim Y As Integer ' оператор смещений для формирования массива значений для TTesta
Dim J As Integer ' оператор каретки по оси времени
Dim I As Integer ' оператор каретки по оси герц
Dim Z As Integer ' постоянный зазор между блоками
Dim U As Integer ' число испытуемых
Dim NT As Integer ' время сегмента
Dim NHZ As Integer ' число частот
NT = 1200 ' время сегмента (должно делиться на два)
NHZ = 28 ' число частот
Z = 2 ' постоянный зазор между блоками
U = 45 ' число испытуемых
' начало формирования первого массива значений для ttesta
While Y < (U - 1) ' число испытуемых минус один
X = X - (NHZ + Z) * 2
ActiveCell.Offset(-Y, -X).Select
Selection.Copy
Y = Y + 1
ActiveCell.Offset(Y, X).Select
ActiveSheet.Paste
Wend
' начало формирования воторого массива значений для ttesta
X = U * (NHZ + Z) * 2 - (NHZ + Z) - 1
Y = 0
Range("A6").Select
ActiveCell.Offset(J, I + NHZ + Z).Select
Selection.Copy
ActiveCell.Offset(0, X).Select
ActiveSheet.Paste
While Y < (U - 1) ' число испытуемых минус один
X = X - (NHZ + Z) * 2
ActiveCell.Offset(-Y, -X).Select
Selection.Copy
Y = Y + 1
ActiveCell.Offset(Y, X).Select
ActiveSheet.Paste
Wend
' ttest
ActiveCell.Offset(-U + 1, 1).Select
ActiveCell.FormulaR1C1 = "=TTEST(RC[-2]:R[" & U - 1 & "]C[-2],RC[-1]:R[" & U - 1 & "]C[-1],2,1)"
S = ActiveCell
ActiveCell = S
По поводу движений активного окна: любое обновление окон приложения действительно занимает много времени, причем это касается не только скроллинга, но и изменения значения одной единственной ячейки, например. Поэтому обновление нужно отключать, вот так:
Sub Test
'отключаем
Application.ScreenUpdating=False
'здесь код твоей процедуры
'и не забываем включить
Application.ScreenUpdating=True
End sub
В итоге экран обновится всего один раз, в конце процедуры.
Потом, не стоит делать вот так:
ActiveCell.Offset(-Y, -X).Select
Selection.Copy
можно вот так:
ActiveCell.Offset(-Y, -X).Copy
Зачем выделять ячейку, если смотреть на нее выделенную ты все равно не собираешся? Это ведь тоже время.
Кроме того не стоит копировать здесь данные в буфер, применение буфура, на мой взгляд, имеет смысл, если ты собираешься махом перенести большой массив данных с одного места в другое, или перенести таблицу со всеми ее форматами.
Забирай данные в специально созданную для этого переменную, а потом вставляй куда требуется, вот так:
x=Cells(x1,y1).Value
Cells(x2,y2).Value=x
зависит от ситуации, но в твоем случае очевидно лучше вообще напрямую:
Cells(x1,y1).Value=Cells(x2,y2).Value
Я признаться в код не вникал, но скорее всего в твоем случае, после того как ты учтешь то что выше написано работать программа все равно будет очень медленно.
В связи с этим такое соображение: раз ты все-равно удаляешь в конце все промежуточные таблицы, то не стоит зазря юзать объекты приложения, ведь на это уходит больше всего времени. Лучше делай так:
1. Определяешь в своей программе необходимые массивы
2. Считываешь данные с листа в эти массивы
3. Здесь же в своей программе анализируешь эти массивы
4. Выбрасываешь результат на лист
5. ????????????????
6. ПРОФИТ!
Всё верно, ненужно выделять ячейки и копировать в буфер, ненужно скролить лист, лучше вообще свернуть эксель на время работы макроса.
Но модель экселя сама по себе очень медленная, даже если всё сделать правильно время обработки такого файла будет весьма значительным.
Лучше найти другую форму для хранения этих данных или хотябы конвертировать во чтото более другое перед обработкой