Создание таблиц в Envision


Главная » Ресурсы » Здесь

Самый простой способ создания таблицы в Envision — это считывание таблицы из файлов, однако в Envision можно создавать таблицы «с нуля». Точнее говоря, созданные таблицы можно задавать и обрабатывать в одном сценарии. Создание таблиц может пригодиться, когда в исходных таблицах нет нужных данных для определенных расчетов.


Обзор синтаксиса

Обычный синтаксис для создания таблиц основан на операторах table и extend, которые представлены следующим образом:
table T = extend.range(Orders.42)
Здесь важно не путать ключевое слово table с типом файла, как в show table. После этого таблицу T можно использовать в сценарии, как любую таблицу, загруженную из файла.
show table "Number of lines" with sum(T.1)

extend.range()

Самый простой способ создать таблицу — использовать расширение диапазона extend.range(): для каждой строки исходной таблицы создается и нумеруется N строк. Расширение диапазона необходимо использовать, когда нужно вставить в таблицу новые строки.

Синтаксис данной функции выглядит так:
table T = extend.range(Orders.K)
T.Quantity = Orders.Quantity // косвенная проекция
show table "Line numbers" with T.N, T.Quantity
Аргумент должен быть целым числом, которое может различаться в разных строках исходной таблицы. Это позволяет контролировать количество добавляемых строк.

Таблица T вводится как расширение таблицы, изначально заданной как аргумент. Таким образом, как видно из строки 2 выше, простые связи между таблицами работают, потому что все строки в T привязаны к соответствующим строкам в Orders. Поле T.N заполнено по умолчанию, и в нем показан номер строки. Первое значение в этом поле — 1, и все последующие значения увеличиваются на 1.

Будьте осторожны: функция extend.range() может создавать очень большое количество строк. Во-первых, создание очень больших таблиц отнимает много времени, во-вторых, данная операция может быть невыполнима из-за ограничений вашей учетной записи Lokad. На практике, если K превышает среднее значение более чем на 10 единиц, то лучше не использовать функцию extend.range().

В следующем примере показано, как отдельные строки таблицы можно дублировать, сохраняя содержимое исходной таблицы.
read "/sample" all
expect Grid[*]

table G = extend.range(Grid.Min == 0 ? 2 : 1)
G.Min = Grid.Min // привязка 'G' к 'Grid'
G.Max = Grid.Max
G.Probability = Grid.Probability
G.Probability = 0 where G.N == 2
show table "My Grid" with Id, G.Probability, G.Min, G.Max

extend.distrib()

Функция распределения Envision позволяет использовать мощные алгебраические расчеты, которые позволяют избежать запутанных вычислений вероятности в списках. Тем не менее существуют ситуации, где простого списка вероятностей вполне достаточно, и его использование даже предпочтительно. Расширение распределения extend.distrib() преобразует вектор распределения в таблицу, как показано в следующем сценарии:
table T = extend.distrib(D)
show table "Distribution details" with Id, T.Min, T.Max, T.Probability
Аргумент D должен представлять собой обычный вектор распределения, создаваемый системой вероятностного прогнозирования Lokad. Таблица T вводится как расширение оригинальных таблиц — скрытой таблицы Items в сценарии выше. Таблица T заполняется тремя полями:

  • T.Min: нижняя граница сегмента, выраженная целым числом и включающая его
  • T.Max: верхняя граница сегмента, выраженная целым числом и включающая его
  • T.Probability: сумма распределения по диапазону с включением крайних значений

Поле Probability отражает сумму распределений по возвращенному диапазону.

Для более компактной записи распределения длина всех сегментов равна 1, поэтому T.Min == T.Max. Тем не менее, если распределение идет по более высоким значениям, то использование сегментов длиной в 1 приведет к созданию миллионов строк, обработка которых нецелесообразна. Именно поэтому при работе с такими случаями распределения Envision автоматически объединяет значения по более крупным сегментам. Алгоритмы прописаны таким образом, чтобы размеры таблиц оставались в разумных пределах.

Функция extend.distrib() всегда выделяет нулевой сегмент. Как следствие, сегмент [0;0] всегда получает отдельную строку в созданной таблице. Это полезно во многих ситуациях, связанных с бизнесом, когда нулевой спрос является крайним случаем (как и безграничное покрытие запасами), для которого требуется отдельный алгоритм.

Затем добавляется три дополнительных перегрузки для extend.distrib(), чтобы лучше контролировать детализацию созданной таблицы.

Промежуток

Первая перегрузка нужна для создания списка приоритетных закупок с учетом текущего уровня запасов. Синтаксис данной функции выглядит так:
table T = extend.distrib(D, S)
Первый аргумент D был описан выше. Второй аргумент S должен представлять собой целое число. При наличии второго аргумента создаваемая таблица всегда будет включать две строки, предназначенные для двух сегментов [0;0] и [1;S]. Остальные сегменты создаются автоматически, начиная с S+1, как описано выше. Значение по умолчанию для данного аргумента, если он не указан, равно нулю.

На практике аргумент S часто задается как сумма наличных запасов и заказанных товаров. При размещении заказов нужно рассматривать вероятности возникновения только таких уровней спроса, которые превышают текущий уровень запасов.

Коэффициент

Вторая перегрузка нужна для таких случаев, когда используются коэффициенты партий. В таких случаях таблица должна повторять сегменты определенного размера. Можно использовать следующий синтаксис:
table T = extend.distrib(D, S, M)
Аргументы D и S были описаны выше. Третий аргумент M должен представлять собой целое число. Он отражает желаемую длину сегмента. Таким образом, таблица включает в себя список сегментов [0;0], [1;S], [S+1;S+M] [S+M+1;S+2M] и т. д. Если аргумент M равен нулю, то функция автоматически выбирает размер сегментов.

На практике, если указать длину сегмента 1, то это может привести к проблемам с обработкой таблицы, так как она получится очень большой. Поэтому в Envision можно использовать значения кратные M. Использование кратных значений обеспечивает стабильную работу алгоритма коэффициента заказа. При этом число создаваемых строк ограничено до разумных пределов.

Как правило, мы не рекомендуем пользоваться данной перегрузкой в случаях с коэффициентами заказа. В подобных ситуациях лучше установить значение M на ноль для всех наименований с определенными коэффициентами заказа.

Достижение

Третья перегрузка нужна для таких случаев, когда используется Минимальный объем заказа. В таких случаях таблица должна быть достаточно длинной, чтобы достигнуть определенных значений. Можно использовать следующий синтаксис:
table T = extend.distrib(D, S, M, R)
Аргументы D, S и M были описаны выше. Четвертый аргумент R должен представлять собой неотрицательное целое число. Он представляет собой искомое максимальное значение, которого можно достичь с помощью решетки. Иначе говоря, есть строка, где значение T.Max больше или равно R. Значение по умолчанию для данного аргумента, если он не указан, равно нулю.

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

Как правило, мы не рекомендуем использовать данную перегрузку кроме случаев, когда необходимо достичь MOQ. При использовании данной перегрузки значение R должно быть как можно меньшим. Малое значение R не мешает таблице T достигать более высоких значений, а лишь обеспечивает получение большего количества значений.

extend.billOfMaterials()

Спецификация нужна для случаев, когда основное внимание уделяется не элементам, которые продаются или обслуживаются, а запчастям к ним. В таких случаях желательно преобразовать историю спроса на “наименования” в историю спроса на “детали”. Данное преобразование выполняется с помощью механизма спецификации, который позволяет прописать компоненты для всех наименований.

Функция вызова extend.billOfMaterials() предназначена как раз для таких ситуаций:
table T = extend.billOfMaterials(
  Item: J.Id
  Part: B.PartId
  Quantity: B.Quantity
  DemandId: O.OrderId
  DemandValue: O.Quantity)

T.DemandDate by T.DemandId = same(O.Date) by O.OrderId // показывает, как получить дату

show table "Details" with Id, T.DemandDate, T.Quantity
Вы должны получить три таблицы: J, B и O. Таблица J обычно представляет собой таблицу наименований, однако это необязательное требование. Таблица B — это таблица спецификации, и она должна быть расширением таблицы J, то есть типа (Id, *), если J является таблицей наименований. Таблица O должна отражать историю спроса, обычно она привязана к таблице заказов. Таблица O также должна быть расширением таблицы J.

Итоговая таблица является расширением таблицы J со всеми соответствующими привязками. Таблица T содержит два поля, которые уже заполнены:
  • T.DemandId — позволяет в таблице T найти исходные строки из таблицы O.
  • T.Quantity — значения берутся из таблицы спецификации.

Очень часто полезно указать дату в таблице T. Это можно сделать с помощью оператора left-by, как показано в строке под блоком billOfMaterials() в примере выше.

Функция extend.billOfMaterials() поддерживает рекурсивную сборку, то есть наименование может появляться в таблице B и как компонент, и как набор. Функция выдает ошибку, если в таблице спецификации обнаруживаются циклические зависимости.

Аргумент Quantity представляет собой количество единиц B.PartId, которые участвуют в продаже или обслуживании набора, указанного в J.Id.