Страницы: 1 2 След.
RSS
суммирование макросом, нужен быстрый алгоритм
 
Привет, коллеги. Прошу помочь с алгоритмом, теоретической подготовки не хватает.

Суть в следующем. Есть книга с двумя (на самом деле гораздо больше) листами. На одном из них (назовем его Лист1)  несколько десятков или даже сотен тысяч записей, которые постоянно обновляются и пополняются копированием-вставкой из внешнего источника. Данные на этом листе не упорядочены, и содержат графы  "дата", "ID",  некая "сумма", и много всего прочего. На другом листе (Лист2) нужно макросом сформировать типа сводного отчета, в котором  данные первого листа подсуммируются по "дате" + "ID".
Есть рабочее решение, но оно слишком медленно, поэтому и прошу подсказки. Суть моего макроса состоит в том, что содержимое обоих листов считывается в два массива, потом двумя вложенными FOR даные суммируются  (внешний for пробегает больший массив1, а внутренний накапливает в меньшем массиве2 суммы по заданным условиям), затем меньший массив заливается обратно на Лист2 конструкцией типа cells(...).resize(...).value = arr. Этот макрос работает несколько минут, что очень долго, учитывая прочие условия задачи.
Со сводными таблицами знаком, в данном случае это неприменимо, потому что Лист2 имеет фиксированную разметку  - это  достаточно сложный  отчет, который нужно заполнить цифрами по одной  кнопке, нажатой пользователем.

С функцией СУММЕСЛИМН тоже знаком, но если её использовать на этом объеме данных, перерасчет всей книги на шести ядрах core i5-8500 c 16GB памяти занимает почти 2 минуты, что также недопустимо, т.к. перерасчет будет запускаться на каждое редактирование, фильтрацию и т.п. (application.calculation =xlcalculationmanual не предлагать, противоречит прочим неозвученным условиям)

В общем, требуется именно макрос, который будет работать по кнопке, и по возможности "мгновенно".
Накидайте пожалуйста идей или ссылок на описание, какой алгоритм суммирования по нескольким условиям самый быстрый. С реализацией в VBA буду разбираться сам. Спасибо.
Изменено: Alexcx - 27.05.2024 19:29:07
 
Попробуйте отключить события

перед обработкой данных отключаем обработку событий Эксел
Application.EnableEvents = False
    выполняем свои макросы
потом включаем обработку событий Эксел
Application.EnableEvents = True
Изменено: LAD - 27.05.2024 23:29:23
 
Лучше данные накапливать в словаре, ключ словаря - конкатенация даты и ID (через какой нибудь символ).
Владимир
 
Если применимо, то цикл по меньшему массиву в целях ускорения должен быть внешним
 
Сумма массива: WorksheetFunction.Sum(arr).
Вместо resize укажите адрес массива ячеек.
 
Alexcx,
1. Как написано выше
Цитата
sokol92 написал:
Лучше данные накапливать в  словаре , ключ словаря - конкатенация даты и ID (через какой-нибудь символ).
\ Это сильно ускорит исбавив от второго цикла, пусть даже и маленького
2. Не понятно что у вас там по критериям выбирается, но возможно SQL запрос сделает все без словарей и прочего. Связать две таблички по двум полям и взять нужные столбцы не сложно. Тоже можно и через PQ а не SQL.
3.
Цитата
LAD написал:
Application.EnableEvents = False
может помочь если действительно после вставки результата начинается пересчет функций зависящих
По вопросам из тем форума, личку не читаю.
 
Для накопления/хранения/обработки данных предназначены СУБД, в простейшем случае excel, SQL можно применять и там и там, словари - специфическая вещь. Для начала попробуйте в excel PQ и ADO.

еще попробуйте отключать обновление экрана на время обработки:
Application.ScreenUpdating = False
Ваш код
Включаем обновление экрана:
Application.ScreenUpdating =True
Изменено: LAD - 28.05.2024 09:45:04
 
Цитата
LAD написал:
еще попробуйте отключать обновление экрана на время обработки:
В какое момент это поможет ?  - практически ни в какой.
Цитата
LAD написал:
Для начала попробуйте в excel PQ и ADO.
а зачем меня пересказывать?
По вопросам из тем форума, личку не читаю.
 
LAD похож на бота с довольно бестолковыми советами  :)
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
Jack Famous написал:
LAD  похож на бота с довольно бестолковыми советами
вот и мне так показалось. Советы не совсем бестолковые, но от орванные от темы.
По вопросам из тем форума, личку не читаю.
 
Судя по тому, как на меня окрысились некоторые модераторы и некоторые пользователи в моих советах есть дельные предложения.
 
Цитата
LAD: Судя по тому, как на меня окрысились некоторые модераторы и некоторые пользователи в моих советах есть дельные предложения.
с точностью, но наоборот  :D
Цитата
LAD: еще попробуйте отключать обновление экрана на время обработки:
такой совет (и прочие ваши замечания) говорит о том, что вы не понимаете, для чего это нужно. Обновление экрана имеет смысл отключать при ПОСТОЯННОЙ (в цикле, например) выгрузке на лист. Также, оно НЕМНОГО помогает (больше психологически) при ПЕРЕКЛЮЧЕНИИ между листами в коде (что уже говорит, скорее, о плохом качестве кода).
    Если мы берём данные с листа, считаем всё в памяти и выгружаем обратно, то отключение экрана либо совсем не нужно, либо нужно только для непосредственно выгрузки.

    С пересчётом примерно такая же история.

Цитата
LAD: Вместо resize укажите адрес массива ячеек.
это просто чушь. Никакой разницы в плане скорости выгрузки.
Цитата
LAD: словари - специфическая вещь. Для начала попробуйте в excel PQ и ADO.
не сказал бы, что опреации со словарями сложнее изучения запросов SQL или PQ. Скорее, совсем наоборот.
Цитата
LAD: Сумма массива: WorksheetFunction.Sum(arr)
вот это просто бомба. Применить функцию листа в коде VBA  — это как примотать гири к ногам, в большинстве случаев.

LAD, короче говоря. Предвзятости нет, но псевдознатоков (а пока вы себя показали именно так) лично я очень не люблю — вы даёте некорректные советы и это вредит вопрошающим и форуму.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
написал:
Судя по тому, как на меня окрысились некоторые модераторы и некоторые пользователи в моих советах есть дельные предложения.
Логика покинула чат.
 
Вариант суммирования через словарь словарей.
Скрытый текст
Изменено: БМВ - 28.05.2024 11:59:40
 
Цитата
МатросНаЗебре: Логика покинула чат.
да уж  :)
sokol92 уже дал главный совет. Теперь дело за ТСом. Это практически хрестоматийный пример бесспорного преимущества словарей, поскольку
Цитата
БМВ: Это сильно ускорит, избавив от второго цикла, пусть даже и маленького
Конечно, если заморочиться с альтернативами, то можно и бинарный поиск прикрутить и поиск инстром по длинной строке-сцепке, но суть тут одна — уйти от второго цикла.

UPD:
МатросНаЗебре, вот не терпится тебе  :D

UPD2:
Ссылка на описание словарей с примерами и объяснениями.
sokol92 дал ссылку на официальную страничку, а её (лично мне) гораздо тяжелее воспринимать  :)
Изменено: Jack Famous - 28.05.2024 11:23:14
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Согласен - "логика покинула чат". Меня больше интересует мнение тех, кто просит совета.
 
Цитата
написал:
Меня больше интересует мнение тех, кто просит совета.
Чел спрашивает совета по программированию.
Заинтересует ли его мнение человека, не умеющего в логику.
 
МатросНаЗебре, то, что ты говоришь — логично.
    Сумеет ли человек, не умеющий в логику, понять логичную конструкцию — вот главный вопрос. Философский немного…
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Закончили упражнение ;-)

МатросНаЗебре,  про сполер не забываем!!!

LAD,  если вы считаете что ваши советы помогут ТСу, то  можете оставаться при своем мнении. Это не первая тема с вашими советами которые имеют весьма поверхностный характер решений. А сообщения ради счетчика сообщений - точно лишние
По вопросам из тем форума, личку не читаю.
 
Вариант с одноуровневым словарём - в качестве ключей используются значения двух столбцов, соединённые через разделитель.
Интересно, словарь словарей работает в 1.5 раза быстрее.
Вывод:
словарь словарей - быстрее
просто словарь - понятнее.

Скрытый текст
 
Цитата
МатросНаЗебре: Интересно, словарь словарей работает в 1.5 раза быстрее.
сколько ключей в одноуровневом словаре?
100к и более? Близко к этому?

    Учитывая, что и ID и дата — это целые числа, можно вообще без словарей, на массивах сделать (у массивов ключ — это индекс, то есть ID или дата). Это был бы самый быстрый вариант. И [практически] без ограничений по количеству элементов.
    Можно микс — массив словарей (Dim a() As Dictionary) или словарь с массивами в качестве элементов.

МатросНаЗебре, жду итогов твоих развлечений  ;)
Изменено: Jack Famous - 28.05.2024 12:25:20
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
написал:
сколько ключей в одноуровневом словаре?
Я тестировал на 300 000 шт.
Цитата
написал:
ID и дата — это целые числа, можно вообще без словарей, на массивах сделать
Идея норм. Иногда так и делал, что-то вроде redim arr(45000 to 46000) Действительно, должно быть быстрее.
 
Цитата
МатросНаЗебре: Я тестировал на 300 000 шт.
пф. Ну так я ж давно уже рассказываю, что около 100к и далее начинаются торомоза. Ничего нового … Обходится массивом (не словарём) словарей или суперсловарями от BedVit'а.

Цитата
МатросНаЗебре: redim arr(45000 to 46000)
правильно. Нужно найти минимальную и максимальную даты/ID и задать по ним массив. Или задать от 1 и использовать переменную для смещения, что не так удобно и немного (незаметно) медленнее.
Изменено: Jack Famous - 28.05.2024 12:38:52
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
написал:
жду итогов твоих развлечений
На моём тестовом наборе 1 млн строк, 300 тыс уникальных, не хватило памяти под вариант с массивами.
По всей видимости, вариант с массивами будет быстрым, но сильно зависит от входных данных. При большом количестве повторов работать будет, а при большом количестве уникальных может упереться в ресурсы.
 
Цитата
МатросНаЗебре: На моём тестовом наборе 1 млн строк, 300 тыс уникальных, не хватило памяти под вариант с массивами.
я ни за что не поверю, что 300к ключей влезают в словарь, но не влезают в массив  :)
    Обратная ситуация — возможна.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Цитата
написал:
влезают в словарь, но не влезают в массив
Возможно, если массив сильно разреженный. Для такого памяти не хватило (45292 to 62523, 10000 to 99999).
 
МатросНаЗебре, 20 тыс * 90 тыс это меньше 2 млрд. Что-то тут не так. Массив такого же размера, но от 1 — даёт создать? Может, ты забыл память очистить и что-то в глобальных/статических переменных висит? Тип массива должен быть Double.

UPD:
у меня тоже не влезает))) максимум — 10к * 50к. Сейчас опишу альтернативный алгоритм — щадящий к памяти, но медленнее, конечно (не прям сильно).

UPD2:
Получается сложно. Я бы просто массив словарей использовал или суперсловари от Виталия.
Если суперсловари то всё просто: ключ = ID & "•" & Date, значение = копим сумму.

Если массив словарей (Dim aDic() As Dictionary), то 2 варианта:
    • не факт, что хватит памяти:
         в массиве столько элементов, сколько уникальных дат (ReDim aDic (45292 to 62523) ). По дате/числу мы получаем словарь с парами "ID (для этой даты) — сумма". Далее, копим, как обычно.
    • оптимальный по памяти:
          в массиве столько элементов/словарей, чтобы в каждом из них было не более 100 тыс элементов (лучше 80). То есть, если уникальных комбинаций 300к, то будет 3 или 4 элемента в массиве. Далее алгоритм тот же, что и суперсловарей, только мы последовательно проверяем каждый словарь, пока не найдём нужный ключ.

    Сразу видно, насколько суперсловари удобнее  :)

    При желании, можно оптимизировать 2ой вариант — чтобы точно знать, где искать. Например, в 1ом словаре будут даты или ID с N1 по N2 и так далее … Для того, чтобы в динамике понимать, в каком словаре, какой диапазон дат — можно завести отдельный одномерный Long-массив.
Изменено: Jack Famous - 28.05.2024 14:43:48
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
 
Естественно, кроме уважаемых оппонентов "в логику" не умеет ни кто, а от них ждут конкретной помощи или хотя бы совета дельного.
Изменено: LAD - 28.05.2024 16:50:32
 
LAD,  Пока предупреждение за посты не имеющие ценности. Следующий сопроводится баном.

МатросНаЗебре, На каких данных был тест?
Просто для прикола хотелось бы проверить SQL и коллекции.
По вопросам из тем форума, личку не читаю.
 
БМВ, коллекции нет смысла проверять, а вот скуль — интересно посмотреть
Изменено: Jack Famous - 28.05.2024 22:10:26
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: 1 2 След.
Наверх