# List of functions

The primary purpose of functions is to extend the capabilities of Envision whenever the basic language syntax would not be sufficient. Below, we list the functions supported by Envision.

## Function syntax

The general syntax for functions is:

x = fun(arg1, arg2, arg3, ..., argN) by [Group] sort [Order]

fun will be replaced by the name of the function and arg1, arg2, arg3, ..., argN will be replaced by the arguments passed to the function. All functions operate on vectors.

Only certain functions support the extra options by and sort. While the syntax is similar to the syntax of the aggregators, those functions are not aggregators. The by option is optional, and when it is omitted, it is equivalent to by 1. Both Group and Order support tuples of vectors, that is, multiple vectors delimited by commas. For example:

R = rank() by [A, B] sort [1]

## Mathematical functions

• abs(number): similar to the ABS function in Excel.
• ceiling(number): rounds up to the nearest integer, similar to the CEILING.MATH function in Excel.
• floor(number): rounds down to the nearest integer, similar to the FLOOR.MATH function in Excel.
• exp(number): similar to the EXP function in Excel.
• log(number): natural logarithm, similar to the LN function in Excel.
• max(num1, num2, num3, ..., numN): similar to the MAX function in Excel.
• min(num1, num2, num3, ..., numN): similar to the MIN function in Excel.
• percent(N) by [Group]: returns the value N divided by the sum of the all the values within the Group.
• norminv(number): applied to a probability, returns the inverse of the normal cumulative distribution function, with mean at 0 and a standard deviation of 1. Similar to the NORM.S.INV function in Excel.
• pow(number, exponent): similar to the POWER function in Excel. Envision also supports the power operator number exponent, which performs the same calculation.
• round(number, digits): similar to the ROUND function in Excel. Differently from Excel, the second argument is optional, and represents the number of digits that are intended to be kept. If a non-integer or out-of-range value is provided as number of digits, its approximation down to the nearest integer in the range [0,15] is taken instead.
• sqrt(number): similar to the SQRT function in Excel.

## Text functions

• concat(text1, text2, ..., textN): concatenates text values from text1 to textN.
• contains(text, pattern): returns true if the text contains an occurrence of the pattern.
• endswith(text, pattern): returns true if the text ends with an occurrence of the pattern.
• field(text, separator, index): returns the nth field (zero-indexed) in a text value that contains multiple sub-strings separated by a specified separator. Ex: field("a-b-c-d-", "-", 2) == "c". This function is intended to facilitate parsing values that have been concatenated within a single table column.
• field.r(text, separator, index): Like the field() function, but counts the separator occurrences from the right. Ex: field.r("a-b-c-d-", "-", 0) == "".
• indexof(text, pattern): returns the index of the first occurrence of the pattern within the text, or -1, if no such occurrence is found.
• lowercase(text): returns the lowercase variant of the text.
• parsedate(text, format): converts the text into a date using the specified format. The format is optional. When the format is omitted, the date is parsed based on the date format auto-detection behavior of Envision. When the format is provided, the date is parsed against the format expectation. See custom date format string for the detail for the format syntax. If a date cannot be parsed, the date 2001-01-01 is returned instead.
• parsenumber(text): converts the text into a number. The parser leverages the number format auto-detection behavior of Envision. If the number cannot be parsed, zero is returned instead.
• parsetime(text, format): converts a time of the day into a fraction between 0 and 1, representing a fractional day. The format is optional. When the format is not specified, the default value yyyy-MM-dd HH:mm:ss is used. Envision is using the .NET Custom Time Format.
• replace(text, pattern, replacement): replaces in the text all occurrences of the pattern by the replacement. This function is similar to the SUBSTITUTE function of Excel, omitting the instance_num argument.
• startswith(text, pattern): returns true if the text starts with an occurrence of the pattern.
• strlen(text): returns the length of the text argument.
• substr(text, start, count): the start position is defined by start. If negative, it’s an offset from the end of the string, otherwise it’s an offset from the start of the string. The length of the returned substring is defined by count, treated as 0 if count < 0. If the segment start or length places it partially or completely outside the string, e.g. substr("A", 2, 1), then the segment is clipped to fit. Function substr(text, start) is defined as substr(text, start, <infinity>).
• uppercase(text): returns the uppercase variant of the text.

## Calendar functions

• "\{myDate:yyyy-MM-dd}": custom date formatting through string interpolation. The date format is specified by the token found after the semi-colon. More details about date format strings.
• chineseYear(date): returns the current year in the Chinese calendar.
• chineseYearEnd(date): returns the last day of the current Chinese Year.
• chineseYearStart(date): returns the first day of the current Chinese year.
• date(y, m, d): returns a date built from the year, month and day passed as an arguments. The arguments y, m and d are expected to be numbers.
• monday(date): returns the first Monday that precedes the date (inclusive).
• month(date): returns the index of the month associated to the date, counting the number of months since January 1st 2001.
• monthnum(date): returns the applicable month (1-12) for the date passed as an argument.
• daynum(date): returns the applicable day of the month (1-31) for the date passed as an argument.
• today(timezone): returns the current wall-time date with the time-zone passed as an argument and expressed as the difference in hours to UTC.
• year(date): returns the applicable year for the date passed as an argument. Similar to the YEAR function in Excel.
• isoYear(date): returns the applicable year for the week which the date in the argument belongs to. All days of a given week belong to the same isoYear and weeks begin on Mondays.
• yearStart(date): returns the first day of the year for the current year.
• yearEnd(date): returns the last day of the year for the current year.
• weeknum(date): similar to the WEEKNUM (System 2) function in Excel. Weeks start on Mondays.

## Ranking functions

• argfirst() by [Group] sort [Order]: returns true for one first value of the group according to the ordered values (the ordering). The group is optional. When the group is provided, the function returns true once per group. An overload argfirst() by [Group] sort [Order] where condition is also provided for convenience. When the where option is used, it may result in groups where no true value is present, because the condition was false for the entire group.
• arglast() by [Group] sort [Order]: same as argfirst(), but returns true for the one last value.
• cumsum(N) by [Group] sort [Order]: returns the cumulative sum of the numbers N according to the increasing ranks. The group is optional. When the group is specified, it is used to perform a local cumulative sum for each group.
• fifo(V, T.D, T.Q): helper for FIFO inventory analysis. Returns the unsold inventory quantity as a vector of T. The vector V contains the total stock. The table T contains the purchase orders. T.D contains the dates, and T.Q contains the purchase quantities. The function computes the unsold quantities by playing the purchase orders backwards in time. See also FIFO inventory method.
• rank() by [Group] sort [Order]: returns the ranks of the numbers with no tie. Similar to the RANK.EQ function in Excel, except that all numbers get a distinct rank (tie-breaks are arbitrary). The group is optional. When the group is provided, it is used to perform local ranks for each group.
• rank(N, Group, S): a more advanced ranking function that is quite different from the other rank() overloads. The purpose of this overload is to support the generation of a prioritized purchase list. In particular, rank(N, Group, S) cannot be re-expressed as a simple expression of sorting and grouping. This is a two-stage imperative algorithm. In the first stage, values are grouped by Group into stacks, with each stack ordered by ascending S. In the second stage, the algorithm selects the highest value of N among the top elements of all stacks, pops that element, assigns it a rank (starting at 1), and repeats until all stacks are empty.
• rankd(N) by [Group]: returns the ranks of the numbers N; identical numbers get identical ranks. Similar to the RANK.EQ function in Excel.

## Graph functions

• canonical(A, B): returns the canonical representative for each A value. From a practical perspective, this function is used to deal with code replacement (ex: SKU code replacement). For example, canonical(OldSku, NewSku) would return the latest SKUs available for each item, recursively performing the replacements. See also nonCanonical().
• nonCanonical(A, B): returns true whenever a canonical representative cannot be computed for the A. This happens when circular paths or branching paths get detected.
• connected(A, B) by [Group]: considers the undirected graph described by all edges (A,B), then returns for each node A the name of the smallest node in A’s connected component. Here, “smallest” means having the smallest name, in terms of string comparison. The group is optional. When the group is specified, the dataset is first partitioned against the specified groups.

## Algebra of ranvars

In the following, given an integer-valued random variable $X: \mathbb{\Omega} \to \mathbb{Z}$, its associated probability distribution $\mathbf{P}(X = k)$ will be referred to as a ranvar. Additional information on the algebra of ranvars is provided in the algebra of ranvars section.

### Parametric ranvars

Parametric ranvars can be generated through functions which take a number as an argument - the parameter.

• dirac(n): returns a ranvar zero valued everywhere except for n where the function is valued 1.
• poisson$(\lambda)$: returns the Poisson distribution of parameter $(\lambda)$.
• exponential$(\lambda)$: returns the exponential distribution of parameter $(\lambda)$.
• negativebinomial$(\mu,\sigma)$: returns the negative binomial distribution of mean $\mu$ and standard deviation $\sigma$. If the standard deviation is smaller than the square root of the mean, then a Poisson distribution of mean $\mu$ is returned instead.
• ranvar.uniform(n): returns the ranvar represented by the function $k \mapsto \frac{1}{|n| + 1}$ on the segment [0;n] (if $n \geq 0$ or [n;0] (if $n < 0$) and 0 elsewhere.
• ranvar.uniform(m, n): returns the ranvar represented by the function $k \mapsto \frac{1}{n + 1 - m}$ on the segment [m;n] and 0 elsewhere. We assume that $m < n$ an error is thrown if $m > n$.

### Non-parametric ranvars

• distrib(Id, G.Probability, G.Min, G.Max): function that returns, for each Id, the ranvar defined by a list of buckets, where each bucket has left and right inclusive boundaries and a value for the bucket.
• ranvar(T.X): aggregator that returns the empirical ranvar resulting from the observations stored in the vector T.X.
• ranvar.segment(...): advanced function that generates an empirical ranvar resulting from a sliding time-window (more details in the following).
• ranvar.periodicr(...): more general version of ranvar.segment(...) (more details in the following).

### Indicators on ranvars

Numeric indicators about ranvars can also be obtained in Envision. If X and Y are independent random variables, with ranvars (probability distributions) R and S respectively, and a and b are integers,

• crps(R, a): returns the Continuous Ranked Probability Score (CRPS).
• crps(R, S): returns the half of the energy distance, which can be understood as a generalization of the CRPS to a pair of ranvars.
• mean(R): returns the statistical mean of the random variable X.
• variance(R): returns the statistical variance of the random variable X.
• int(R, a, b): returns the integral of R over the inclusive segment [a;b].
• quantile(R, tau): returns the quantile of the ranvar; the smallest $k$ such as $\mathbf{P}[X \leq k] \geq \tau$.
• spark(R): returns a text value that contains compact ascii-art representation of the ranvar, that can be read by a text editor.
• support.max(R): returns the higher bound of the ranvar support.
• support.min(R): returns the lower bound of the ranvar support.

### Transformations of ranvars

A ranvar can also be transformed into another ranvar (the total mass of 1 being conserved by the transformation). If X and Y are independent random variables, with ranvars R and S respectively, and a and b are integers,

• reflect(R): returns the reflected ranvar $k \mapsto R(-k)$.
• transform(R, a): returns a ranvar that approximates through interpolation $k \mapsto R(k / a)$.
• fillrate(R): returns the marginal fill rate.
• truncate(R, a, b): returns the truncated ranvar that approximates $k \mapsto R(k) \text{ if } k \in [a; b] \text{ else } 0$. The boundaries A and B are inclusive.
• maxr(R, a) or maxr(a, R): returns the ranvar that approximates $k \mapsto R(k) \text{ if } k \in [a + 1; \infty[ \text{ or } \sum_{i=-\infty}^a R(i) \text{ if } k = a \text{ else } 0$.
• minr(R, a) or minr(a, R): returns the ranvar that approximates $k \mapsto R(k) \text{ if } k \in ]-\infty; a - 1] \text{ or } \sum_{i=a}^\infty R(i) \text{ if } k = a \text{ else } 0$.
• maxr(R, S): returns the ranvar described by $\mathbf{P}[max(X, Y) \leq k] = \mathbf{P}[X \leq k]\mathbf{P}[Y \leq k]$.
• minr(R, S): returns the ranvar described by $\mathbf{P}[min(X, Y) \geq k] = \mathbf{P}[X \geq k]\mathbf{P}[Y \geq k]$.
• mixture(R1, p, R2): returns the mixture of two ranvars and a weight: $k \to p \times R_1(k) + (1 - p) \times R_2(k)$.
• mixture(R1, p1, R2, p2, R3): returns the mixture of three ranvars and two weights: $k \to p_1 \times R_1(k) + p_2 \times R_2(k) + (1 - p_1 - p_2) \times R_3(k)$.
• mixture(R1, p1, R2, p2, R3, p3, R4): returns the mixture of four ranvars and three weights: $k \to p_1 \times R_1(k) + p_2 \times R_2(k) + p_3 \times R_3(k) + (1 - p_1 - p_2 - p_3) \times R_4(k)$.

## Algebra of zedfuncs

In Envision, a zedfunc is defined as a function $Z: \mathbb{Z} \to \mathbb{R}$. As an example, stockrwd.m() and pricebrk() return a zedfunc.

### Parametric zedfuncs

Zedfuncs can be generated through functions which take a parameter as an argument.

• identity(n): returns the zedfunc $\text{id}: k \mapsto k$ but limited to the segment [0;n] and 0 elsewhere.
• uniform(n): returns the zedfunc $\text{unif}: k \mapsto 1$ but limited to the segment [0;n] and 0 elsewhere.
• uniform(m, n): returns the zedfunc $\text{unif}: k \mapsto 1$ but limited to the segment [m;n] and 0 elsewhere. If $m - 1 = n$, the uniform() returns a zero zedfunc.
• uniform(D): returns the zedfunc $\text{unif}: k \mapsto 1$ but limited to the support of the ranvar R and 0 elsewhere.

### Transformations of zedfuncs

If Z is a zedfunc,

• zoz(Z) (zero on zero): returns the zedfunc where $k \mapsto f(k) \text{ if } k \neq 0 \text{ else } 0$.

## Table creation functions

New tables can be created from existing tables or ranvars:

• extend.range(T.N): creates N lines for each line from the table T where N is expected to be an integer. For example:
table T = extend.range(Orders.42)
T.Quantity = Orders.Quantity // implicit extension
show table "T" with T.N, T.Quantity
• extend.distrib(ranvar, gap, multiplier, reach): extends a ranvar into a table. See also extend.distrib().
• extend.billOfMaterials(...): translates a demand history for items into the demand history for the parts. See also extend.billOfMaterials().

### cumsub(G.Item, G.Stock, G.Quantity, G.Rank)

Takes 4 vectors belonging to a grid table with:

• G.Item the item identifier, all lines that share the same value belong to the same item;
• G.Stock the initial stock for the item, all lines that belong to the same item must have the same G.Stock value;
• G.Quantity the quantity of the item required for the purchase of the grid line;
• G.Rank a bundle identifier, all lines that share the same bundle identifier belong to the same bundle, it is forbidden to have two lines with the same (G.Item, G.Rank) pair, and all bundles are ordered by increasing rank.

The function cumsub() explores all bundles by increasing rank, keeping track of the remaining stock for each item. Initially, this stock is defined by the G.Stock vector. For each bundle, the function determines whether there is enough remaining stock to purchase all grid lines in that bundle, based on whether the stock exceeds G.Quantity. If that is the case, then the function decrements the stock for each item, and writes to each grid line the remaining stock for that item. If there is not enough stock to serve the entire bundle - usually because one of the items has run out - then the function does not update the remain stocks and stores for each grid line the value -(S+1) (where S is the remaining stock for that item at that point), to indicate both that the grid line is not purchased (test if G.S < 0) and whether it was that specific line that caused the bundle not to be purchased (test if G.Quantity + G.S + 1 > 0) and by how much (G.Missing = G.Quantity + G.S + 1).

### forex(value, Origin, Destination, date)

Returns the amount expressed in the currency Origin into the equivalent amount in the currency Destination according to the historical rates at the specified date. The currencies should be encoded with their canonical three-letter codes. Lokad supports about 30 currencies leveraging the data provided by the European Central Bank. Rates are updated on a daily basis. See also isCurrency() to test the validity of your currency code.

### hash(value)

Returns a pseudo-injective hash value between 0 and 2^24-1. This function is typically used to randomly shuffle a dataset by hashing the content of a column, and then sorting against the hashed values.

### isCurrency(currencyCode)

Returns true if the text entry passed as an argument is a currency code recognized by the forex() function.

### mkuid(X, offset)

Returns a unique number, with unicity maintained across Envision runs. This function is intended to uniquely identify results calculated by Lokad. For example, it can be used to generate a unique purchase order number to be incremented whenever the Envision script is re-executed.

The vector X is ignored, but the UID (unique identifier) is generated as a scalar in the table associated to X. The offset is an optional scalar that represents the starting suffix for for the UID. The generated strings are numbers in the format PPPPPPPAAA, with P as a page number (does not start with 0) that is always strictly increasing, and A as an incremented counter that starts at offset (or 0 if no offset parameter is provided). P has at least 7 digits, A has at least 3.

The UIDs offer three properties.

1. All UIDs can be parsed as numbers, and those numbers will be different. Keep in mind, however, that UIDs have at least 10 digits, and likely more if each call needs to generate more than 1000.
2. A UID generated at time T is strictly inferior (in alphabetical order) to a UID generated at time T’ > T.
3. If all calls generate similar numbers of UIDs (less than 999, or between 1000 and 9999, etc.) then the previous property is also true for the numeric order between UIDs.

### solve.moq(…)

An advanced numeric solver for the general MOQ problem (minimal order quantities).

### pricebrk(D, P, Prices.MinQ, Prices.P, Stock, StockP)

Returns the zedfunc (i.e. a function $Z: \mathbb{Z} \to \mathbb{R}$) vector of the marginal purchase unit price. See Supplier Price Breaks.

### priopack(V, MaxV, JT, B) by [Group] sort [Order]

A simple variant of the bin packing algorithm intended to be used with a purchase prioritization list. Unlike the classic bin packing algorithm, not only do we seek to optimize the bin capacities, but the ordering of the units will also be preserved as much as possible.

• V is the volume of each line.
• MaxV is the max volume capacity, its value is homogeneous to V, and it is assumed to be a constant value across the equivalent class Group.
• JT is the jumping threshold, its value is homogeneous to V, it is typically expected to be a small multiple of the Group value.
• B is an optional argument interpreted as the barrier, when this value is provided, the bin-packing process is not allowed to reorder lines that belong to the same equivalence class as defined by B.
• Group is the equivalence class of the suppliers, with bin packing computed per supplier.
• Order contains the ranks of the lines to be packed.

### ranvar.segment(…)

This call-function is intended to convert time-series into probability distributions (ranvar) by collecting observations over moving windows.

D = ranvar.segment(
start: Items.Start // first date (inclusive) for each item
end: Items.End // end date (inclusive) for each item
step: Items.Step // number, increments in day in-between observation
horizon: Items.Horizon // number, the length in day of period for each item
date: Orders.Date  // date for each event
quantity: Orders.Quantity) // quantity for each event

This function computes, for each item, the ranvar of the sum of event quantities over periods of horizon length, that are entirely between the first and last date for that item. For example, for a start date on Jan 1st, end date on Jan 7th, a horizon of 3 days, and a single event of quantity 5 on Jan 2nd, the observed periods are:

• Jan 1st - Jan 3rd: Q = 5
• Jan 2nd - Jan 4th: Q = 5
• Jan 3rd - Jan 5th: Q = 0
• Jan 4th - Jan 6th: Q = 0
• Jan 5th - Jan 7th: Q = 0

And so the resulting ranvar is 60% Q = 0, 40% Q = 5.

### ranvar.periodicr(…)

This is a more general version of ranvar.segment, where the horizon is a ranvar of lengths (e.g. the probability distribution of lead times) and events at the extrema or at the middle of the chosen time interval are equally taken into account:

D = ranvar.periodicr(
start: Items.Start // first date (inclusive) for each item
end: Items.End // end date (inclusive) for each item
horizon: Items.LeadTime // ranvar of lengths in day
date: Orders.Date  // date for each event
quantity: Orders.Quantity) // quantity for each event

Indeed, ranvar.periodicr considers an infinite repetition of the input data and sums the event quantities over periods of all possible lengths contained in the horizon ranvar. As a consequence, if we consider the example above and we imagine an additional event of quantity 2 on Jan 7th, ranvar.periodicr with dirac(3) as the horizon would observe the following:

• Jan 1st - Jan 3rd: Q = 5
• Jan 2nd - Jan 4th: Q = 5
• Jan 3rd - Jan 5th: Q = 0
• Jan 4th - Jan 6th: Q = 0
• Jan 5th - Jan 7th: Q = 2
• Jan 6th - Jan 1st: Q = 2
• Jan 7th - Jan 2nd: Q = 7

and return the ranvar ~28% Q = 0, ~28% Q = 2, ~28% Q = 5, ~14% Q = 7. For comparison, ranvar.segment with horizon 3 and step 1 would ignore the last two lines of the above list and return 40% Q = 0, 20% Q = 2, 40% Q = 5.

### forest.regress(…)

Gives access to a random forest regression algorithm. A training dataset (Example) and an evaluation dataset (Sample) must be provided. These two tables contain the same series of column attributes, for a different set of Id. In addition, the Example table contains a further column called Label, which represents the desired output of the regression function. The Example table shall thus be conceived as a Sample with a Label.

The function syntax is the following:

Sample.Label = forest.regress(
training: Example.A, Example.B, Example.C // number/Boolean/text
trainingBow : Example.D // plain text, optional
label: Example.Label // number
evaluation: Sample.A, Sample.B, Sample.C // number/Boolean/text
evaluationBow: Sample.D) // plain text, optional

This call-function returns for each Id a ranvar (probability distribution) representing the quantity Label. Up to 16 different attributes are supported as training and evaluation entries. Of these, 8 at most can be number vectors and 8 at most can be Boolean or text vectors representing categories. Optionally, a text vector of words can be provided as trainingBow and evaluationBow: here the text string is treated as a bag-of-words, and analysed in terms of words occurrences.

### smudge(values, present) by [Group] sort [Order]

Takes an incomplete vector of values and a Boolean vector that determines where the valid values are present. It returns a full vector of valid values, which has been completed by spreading valid values into the non-valid ones. More precisely, the output vector is built by looking at every line, group by group (if there is a Group argument) and following the ascending Order, and replacing any non-valid value by the last value that has been seen, or by a default value if no valid value has yet been seen in the group.

### stockrwd.m(D, AM), stockrwd.s(D), stockrwd.c(D, AC)

The stock reward functions. These functions are used to build prioritized ordering policy out of the probabilistic forecasts produced by Lokad.