Форум программистов, компьютерный форум, киберфорум
VBA
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.67/6: Рейтинг темы: голосов - 6, средняя оценка - 4.67
437 / 144 / 9
Регистрация: 12.01.2009
Сообщений: 678
Записей в блоге: 1

Оптимизация процедуры агрегирования иерархических данных

15.05.2013, 13:39. Показов 1291. Ответов 12
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Всем доброго времени суток.
Есть много данных которые имеют иерархический вид (см вложение как пример).
Периодически сотрудники меняют значения и хотят чтобы происходил пересчет сумм в итоговых значениях.
Проще говоря значения элемента на уровне 3 равняется сумме всех элементов уровня 4, которые лежать ниже, но выше следующего элемента на уровне 3.
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
Option Explicit
 
Sub ColumnsSummation()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
 
Dim current_col As Byte
For current_col = 2 To ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column
 
    'Так писать конечно нельзя. Процедура которая вызывается 5 раз это
    'полный ахтунг, но работает)
    Call summation(current_col)
    Call summation(current_col)
    Call summation(current_col)
    Call summation(current_col)
    Call summation(current_col)
    
Next current_col
 
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
 
Sub summation(current_col As Byte)
Dim level_column: level_column = 1
'Dim current_col: current_col = 10
Dim row As Long, row1 As Long
Dim curr_level As Integer, level As Integer
Dim sum As Currency
 
For row = 2 To ActiveSheet.Cells(Rows.Count, 1).End(xlUp).row
 
    sum = 0
    curr_level = ActiveSheet.Cells(row, level_column)
    
    row1 = row + 1
    level = ActiveSheet.Cells(row1, level_column)
    Do While (level <> curr_level) And level <> 0 'попробуем исключить это условие
        If level - curr_level = 1 Then
            If IsNumeric(ActiveSheet.Cells(row1, current_col)) Then
                sum = sum + ActiveSheet.Cells(row1, current_col)
            Else
                MsgBox "В ячейке " & ActiveSheet.Cells(row1, current_col).Address & " стоит не числовое значение" & vbCrLf & _
                    "Процедура суммирования будет завершена." & vbCrLf & _
                    "Исправте значение и запустите её заново."
                End
            End If
        End If
        row1 = row1 + 1
        level = ActiveSheet.Cells(row1, level_column)
    Loop
 
    If curr_level <> 5 Then 'исключаем самый нижний уровень значения которого являются не агрегированными.
        ActiveSheet.Cells(row, current_col) = sum
    End If
Next row
 
End Sub
Что меня в этом коде смущает, так это то, что происходит вызов одной процедуры 5 раз.
Можно конечно обобщить посчитав количество различных значений в столбце "Уровень" и пробежаться от минимума к максимуму, но сути это не меняет.
Может есть нормальные алгоритмы для данной задачи?
Вложения
Тип файла: xlsx иерархическая структура.xlsx (10.0 Кб, 12 просмотров)
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
15.05.2013, 13:39
Ответы с готовыми решениями:

Выборка иерархических данных
Добрый день!!! Имеется файл json такого типа: его необходимо привести к вот такому виду:

Отображение иерархических данных
все работает , но как сделать чтоб подтаблица была не только у &quot;item1&quot; ,но и у остальных. int main(int argc, char *argv) { ...

Итератор связей иерархических данных
Здравствуйте, есть модель данных. public class Sections { public int Id { get; set; } public string...

12
200 / 98 / 2
Регистрация: 24.09.2011
Сообщений: 261
15.05.2013, 14:04
если структура этого документа не меняется, а меняются только цифры в столбцах 2-4, то проще всего сделать формулами. Они автоматически перерассчитываются
1
 Аватар для Апострофф
9908 / 3924 / 742
Регистрация: 11.10.2011
Сообщений: 5,907
15.05.2013, 14:41
Цитата Сообщение от analyst Посмотреть сообщение
Процедура которая вызывается 5 раз это полный ахтунг

Не по теме:

Если всё работает и нет претензий к производительности, то отойди и ничего не трогай - первая заповедь настоящего программиста



Добавлено через 2 минуты
Но если уж никак, то сделай цикл, в чем проблема (правда, скорости это не прибавит)
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Option Explicit
 
Sub ColumnsSummation()
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
 
Dim current_col As Byte
For current_col = 2 To ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column
    Call summation2(current_col)
Next current_col
 
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
End Sub
 
 
Sub summation2(current_col As Byte)
Dim i As Integer
For i = 1 To 5
  Dim level_column: level_column = 1
  'Dim current_col: current_col = 10
  Dim row As Long, row1 As Long
  Dim curr_level As Integer, level As Integer
  Dim sum As Currency
   
  For row = 2 To ActiveSheet.Cells(Rows.Count, 1).End(xlUp).row
   
      sum = 0
      curr_level = ActiveSheet.Cells(row, level_column)
      
      row1 = row + 1
      level = ActiveSheet.Cells(row1, level_column)
      Do While (level <> curr_level) And level <> 0 'попробуем исключить это условие
          If level - curr_level = 1 Then
              If IsNumeric(ActiveSheet.Cells(row1, current_col)) Then
                  sum = sum + ActiveSheet.Cells(row1, current_col)
              Else
                  MsgBox "В ячейке " & ActiveSheet.Cells(row1, current_col).Address & " стоит не числовое значение" & vbCrLf & _
                      "Процедура суммирования будет завершена." & vbCrLf & _
                      "Исправте значение и запустите её заново."
                  End
              End If
          End If
          row1 = row1 + 1
          level = ActiveSheet.Cells(row1, level_column)
      Loop
   
      If curr_level <> 5 Then 'исключаем самый нижний уровень значения которого являются не агрегированными.
          ActiveSheet.Cells(row, current_col) = sum
      End If
  Next row
Next i
End Sub
1
437 / 144 / 9
Регистрация: 12.01.2009
Сообщений: 678
Записей в блоге: 1
15.05.2013, 14:46  [ТС]
Цитата Сообщение от sulfur Посмотреть сообщение
если структура этого документа не меняется, а меняются только цифры в столбцах 2-4, то проще всего сделать формулами. Они автоматически перерассчитываются
Формулы работают медленнее чем макрос когда книга большая. Тем более что нужно постоянно отключать автопересчет во время работы. К тому же данные загружаются из олап кубов и каждый раз проставлять формулы можно, но стоит ли? Хотя вариант исполнения интересен.

Цитата Сообщение от Апострофф Посмотреть сообщение

Не по теме:

Если всё работает и нет претензий к производительности, то отойди и ничего не трогай - первая заповедь настоящего программиста



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

Я скорее думал, что есть специальные алгоритмы для работы с деревьями, которые это делают более оптимально.
0
200 / 98 / 2
Регистрация: 24.09.2011
Сообщений: 261
15.05.2013, 14:54
Цитата Сообщение от analyst
Я скорее думал, что есть специальные алгоритмы для работы с деревьями, которые это делает более оптимально.
ну, чтобы оптимизировать эти переходы достаточно хранить где-нибудь для каждого ряда, ряд его родителя. Тогда не придется каждый раз проходить по рядам в поисках глубины на единицу меньше. Т.е. при экспорте (вы же эту таблицу откуда-то экспортируете, да?) создайте еще один столбец под названием "Родитель" и записывайте туда номер ряда родителя. Тогда в макросе обход будет намного быстрее, особенно на "густых" деревьях.
Понятно объяснил?)
1
200 / 98 / 2
Регистрация: 24.09.2011
Сообщений: 261
15.05.2013, 15:04
примерно так
Вложения
Тип файла: xlsx иерархическая структура.xlsx (9.6 Кб, 5 просмотров)
1
200 / 98 / 2
Регистрация: 24.09.2011
Сообщений: 261
15.05.2013, 15:07
Цитата Сообщение от sulfur
Тогда в макросе обход будет намного быстрее, особенно на "густых" деревьях.
уточнение: сам по себе ваш макрос от этого быстрее не станет, его надо переделывать так, чтобы он правильно использовал этот новый столбец "Родитель"
1
437 / 144 / 9
Регистрация: 12.01.2009
Сообщений: 678
Записей в блоге: 1
15.05.2013, 15:15  [ТС]
Цитата Сообщение от sulfur Посмотреть сообщение
уточнение: сам по себе ваш макрос от этого быстрее не станет, его надо переделывать так, чтобы он правильно использовал этот новый столбец "Родитель"
Спасибо. А как этот алгоритм должен работать?
Читать значение ячейки и прибавлять её значение к родителю, а полученную сумму вставлять в соответствующую ячейку?
0
200 / 98 / 2
Регистрация: 24.09.2011
Сообщений: 261
15.05.2013, 15:38
analyst, ну да, именно так.

Добавлено через 20 минут
при этом способе вам придется идти снизу вверх. Т.е. сначала найти все строки 5 уровня глубины, обработать их, потом искать 4 уровень, обработать его и т.д. Все эти поиски тоже можно оптимизировать и будет еще быстрее.
Для этого вместо столбца "Родитель" надо добавить столбец "Потомки" и при экспорте записывайте в него номера рядов потомков. И тогда искать ничего не придется - алгоритм начнет с самого первого уровня, сразу же в нем прочитает номера рядов потомков и перейдет к ним. Только это надо делать рекурсией. Вы наверное не знаете что это, поэтому сейчас напишу вам рабочий пример
1
 Аватар для KoGG
5645 / 1627 / 418
Регистрация: 23.12.2010
Сообщений: 2,448
Записей в блоге: 1
15.05.2013, 15:43
Так будет в разы быcтрее:
Кликните здесь для просмотра всего текста
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
Option Explicit
 
Sub ColumnsSummation()
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual
    Call summation2(2, ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Column)
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
End Sub
 
Sub summation2(First_col&, Last_col&)
 'Sub summation2(current_col As Byte)
  Dim i%, current_col&
  ReDim sum(1 To 5, First_col To Last_col) As Currency
  Dim level_column: level_column = 1
  'Dim current_col: current_col = 10
  Dim row As Long
  Dim curr_level%, Last_level%
  For row = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).row To 2 Step -1
    curr_level = ActiveSheet.Cells(row, level_column)
    For current_col = First_col To Last_col
        If Not IsNumeric(ActiveSheet.Cells(row, current_col)) Then
             MsgBox "В ячейке " & ActiveSheet.Cells(row, current_col).Address & " стоит не числовое значение" & vbCrLf & _
                 "Процедура суммирования будет завершена." & vbCrLf & _
                 "Исправте значение и запустите её заново."
             End
        End If
        If curr_level = Last_level Then
            sum(curr_level, current_col) = sum(curr_level, current_col) + ActiveSheet.Cells(row, current_col)
        ElseIf curr_level = 5 Then 'исключаем самый нижний уровень значения которого являются не агрегированными.
            sum(curr_level, current_col) = ActiveSheet.Cells(row, current_col)
        ElseIf curr_level <> Last_level Then
            ActiveSheet.Cells(row, current_col) = sum(curr_level + 1, current_col)
            If curr_level Then sum(curr_level, current_col) = sum(curr_level, current_col) + sum(curr_level + 1, current_col)
            sum(curr_level + 1, current_col) = 0
        End If
    Next current_col
    Last_level = curr_level
  Next row
End Sub
2
437 / 144 / 9
Регистрация: 12.01.2009
Сообщений: 678
Записей в блоге: 1
15.05.2013, 16:23  [ТС]
Цитата Сообщение от KoGG Посмотреть сообщение
Так будет в разы быcтрее:
Да... 54 сек. против 7 сек. на реальных данных.
И в чем же секрет (пока сходу не понял)?
0
 Аватар для KoGG
5645 / 1627 / 418
Регистрация: 23.12.2010
Сообщений: 2,448
Записей в блоге: 1
15.05.2013, 16:38
- параллельное суммирование, по каждой строке проходим только один раз. На цикл по уровням не тратим время. DO:LOOP исключаем.
1
200 / 98 / 2
Регистрация: 24.09.2011
Сообщений: 261
15.05.2013, 17:07
а вот мой вариант:
Visual Basic
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Option Explicit
 
Const КолонкаПотомков = 5
 
Sub ColumnsSummation()
    Call СобратьСуммыУПотомков(2) 'начинаем рекурсию со второго ряда
End Sub
 
Private Sub СобратьСуммыУПотомков(ByVal ТекущийРяд As Long, Optional ByRef ТекущийАккумулятор As Range)
    
    Set ТекущийАккумулятор = Cells(ТекущийРяд, 2) 'для примера будем считать только сумму по второну столбцу, но можно добавить аккумуляторы и для других
    
    If Cells(ТекущийРяд, КолонкаПотомков) <> "" Then 'если есть потомки
        
        ТекущийАккумулятор = 0 'обнуляем в этом ряду сумму, а то вдруг она уже посчитана на устаревших данных, а нам то надо считать с нуля
        
        Dim РядПотомка
        For Each РядПотомка In Split(Cells(ТекущийРяд, КолонкаПотомков), " ") 'разделяем строку вида "2 12 33" на массив строк "2", "12" и "33" и обходим его
            
            Dim АккумуляторПотомка As Range 'Эта переменной будет присоено значение в следующей процедуре. Она нужна для возврата значения оттуда
            Call СобратьСуммыУПотомков(РядПотомка, АккумуляторПотомка)
            
            ТекущийАккумулятор = ТекущийАккумулятор + АккумуляторПотомка
        Next РядПотомка
    End If
End Sub
Добавлено через 22 минуты
KoGG, у вас неправильно считает, если где-нибудь в середине таблицы ветвь закончится на глубине 4 а не на 5(они не всегда заканчиваются на 5)
например проверьте на глубинах:
0
1
2
3
4
3
4
5
5

а так, конечно, ваш способ намного лучше моего
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
15.05.2013, 17:07
Помогаю со студенческими работами здесь

Биндинг иерархических данных в ListBox
Добрый день, всем! Пытаюсь реализовать известный пример MSDN Это код XAML &lt;Window x:Class=&quot;LeagueBinding.MainWindow&quot; ...

Добавление иерархических данных в Treeview
Здравствуйте. Есть данные, надо добавить в treeview. Проблема в том что после двух childNodes не могу добавить, ошибка . Пример добавление...

Оптимизация работы процедуры
Вот код с коментами .Если есть аналог то прошу об этом тоже уведомить //Процедура перемещает каталог на новое место ,все работает ,и...

Типы оптимизация: черная оптимизация, серая оптимизация и белая оптимизация
Много много лет назад, на заре становления профессии &quot;оптимизатора&quot; в какой то умной книжке был создан миф. Это миф о цветовой индефикации...

Алгоритм агрегирования
Здравствуйте! У меня есть такая вот структура данных: Map&lt;String, List&lt;MyModel&gt;&gt; MyModel содержит в себе: double value; int...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
Новые блоги и статьи
Перемещение выделенных строк ТЧ из одного документа в другой
Maks 30.03.2026
Реализация из решения ниже выполнена на примере нетипового документа "ВыдачаОборудованияНаСпецтехнику" с единственной табличной частью "ОборудованиеИКомплектующие" разработанного в конфигурации КА2. . . .
Functional First Web Framework Suave
DevAlt 30.03.2026
Sauve. IO Апнулись до NET10. Из зависимостей один пакет, работает одинаково хорошо как в режиме проекта так и в интерактивном режиме. из сложностей - чисто функциональный подход. Решил. . .
Автоматическое создание документа при проведении другого документа
Maks 29.03.2026
Реализация из решения ниже выполнена на нетиповых документах, разработанных в конфигурации КА2. Есть нетиповой документ "ЗаявкаНаРемонтСпецтехники" и нетиповой документ "ПланированиеСпецтехники". В. . .
Настройка движения справочника по регистру сведений
Maks 29.03.2026
Решение ниже реализовано на примере нетипового справочника "ТарифыМобильнойСвязи" разработанного в конфигурации КА2, с целью учета корпоративной мобильной связи в коммерческом предприятии. . . .
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 26.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 26.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru