Array Functions

The array functions of the SD DSL - sum, product, rank, mean, median, standard deviation, maximum, minimum, size and dot
Keywords

system dynamics, systemdynamics, sd dsl, arrays, bptk, bptk-py, python, business simulation

Array Functions

The examples on this page share one model, set up here:

Array Sum

Calculates the element-wise sum of an array.

\[\begin{equation*} \text{sum} \begin{pmatrix} \text{'value1'}: & 1.0 \\ \text{'value2'}: & 2.0 \\ \text{'value3'}: & 3.0 \end{pmatrix} = 1.0 + 2.0 + 3.0 = 6.0 \end{equation*}\]

6.0

Array Product

Calculates the element-wise product of an array.

\[\begin{equation*} \text{prod} \begin{pmatrix} 2.0 & 3.0 & 4.0 \\ 5.0 & 6.0 & 7.0 \end{pmatrix} = 2.0 \cdot 3.0 \cdot 4.0 \cdot 5.0 \cdot 6.0 \cdot 7.0 = 5040.0 \end{equation*}\]

5040.0

Array Rank

Calculates the \(n\)-th highest element of an array. \(n\) is given by a parameter.

For \(n=-1\) the lowest element of the array will be returned.

\[\begin{equation*} \text{rank} \left( \begin{pmatrix} -2.0\\ -0.1\\ 3.1\\ 5.2\\ 11.1 \end{pmatrix} , 1 \right) = 11.1 \end{equation*}\]

11.1

\[\begin{equation*} \text{rank} \left( \begin{pmatrix} -2.0\\ -0.1\\ 3.1\\ 5.2\\ 11.1 \end{pmatrix} , 4 \right) = -0.1 \end{equation*}\]

-0.1

\[\begin{equation*} \text{rank} \left( \begin{pmatrix} -2.0\\ -0.1\\ 3.1\\ 5.2\\ 11.1 \end{pmatrix} , -1 \right) = -2.0 \end{equation*}\]

-2.0

Array Mean

Calculates the element-wise mean of an array.

\[\begin{equation*} \begin{aligned} &\text{mean} \begin{pmatrix} \text{'value1'}: & \{\text{'value11'}: 2.0,\; \text{'value12'}: 4.0\} \\ \text{'value2'}: & \{\text{'value21'}: 6.0,\; \text{'value22'}: 8.0\} \\ \text{'value3'}: & \{\text{'value31'}: 10.0,\; \text{'value32'}: 12.0\} \end{pmatrix} \\[2pt] &= \frac{2.0 + 4.0 + 6.0 + 8.0 + 10.0 + 12.0}{6} = 7.0 \end{aligned} \end{equation*}\]

7.0

Array Median

Calculates the element-wise median of an array.

\[\begin{equation*} \text{median} \begin{pmatrix} -2.0\\ -0.1\\ 3.1\\ 5.2\\ 11.1 \end{pmatrix} = 3.1 \end{equation*}\]

3.1

\[\begin{equation*} \text{median} \begin{pmatrix} -2.0\\ -0.1\\ 3.1\\ 5.2\\ \end{pmatrix} = \frac{-0.1+3.1}{2}=1.5 \end{equation*}\]

1.5

Array Standard Deviation

Calculates the element-wise standard deviation of an array.

\[\begin{equation*} \sigma \begin{pmatrix} 1.0 & 3.0\\ 3.0 & 1.0 \end{pmatrix} = \sqrt{ \frac{1}{4} \cdot \left( (1-2)^2 + (3-2)^2 + (3-2)^2 + (1-2)^2 \right) } = 1 \end{equation*}\]

1.0

Array Maximum and Minimum

arr_max returns the largest value of an array, arr_min the smallest - over every element of a vector or every cell of a matrix.

Both take no arguments, and both return a single value however arrayed the input is. On an element with no sub-elements they return 0.0, as the other aggregations do.

\[\begin{equation*} \mathrm{arr\_max} \begin{pmatrix} 3.0 & 8.0 \\ 1.0 & 5.0 \end{pmatrix} = 8.0 \qquad \mathrm{arr\_min} \begin{pmatrix} 3.0 & 8.0 \\ 1.0 & 5.0 \end{pmatrix} = 1.0 \end{equation*}\]

arr_max: 8.0
arr_min: 1.0

Array Size

Calculates the size of an array.

For a vector, the length will be returned. For a matrix, the size of the highest level will be returned (for example 2 for a \(2 \times 3\) matrix).

\[\begin{equation*} \text{len} \begin{pmatrix} 1.0\\ 1.0\\ 1.0\\ 1.0\\ 1.0\\ 1.0\\ \end{pmatrix} = 6 \end{equation*}\]

6

\[\begin{equation*} \text{len} \begin{pmatrix} 1.0 & 1.0 & 1.0\\ 1.0 & 1.0 & 1.0 \end{pmatrix} = 2 \end{equation*}\]

2

\[\begin{equation*} \text{len} \begin{pmatrix} 1.0 & 1.0 & 1.0\\ 1.0 & 1.0 & 1.0\\ 1.0 & 1.0 & 1.0\\ 1.0 & 1.0 & 1.0 \end{pmatrix} = 4 \end{equation*}\]

4

Array Dot

The Dot function provides the classical vector/matrix-multiplication logic. That means, the following can be calculated:

Factor 1 Factor 2 Result
Vector of size \(m\) Constant Vector of size \(m\)
Constant Vector of size \(m\) Vector of size \(m\)
Matrix of size \(m \times n\) Constant Matrix of size \(m \times n\)
Constant Matrix of size \(m \times n\) Matrix of size \(m \times n\)
Vector of size \(m\) Vector of size \(m\) Value (Scalar Product)
Vector of size \(m\) Matrix of size \(m \times n\) Vector of size \(n\)
Matrix of size \(m \times n\) Vector of size \(n\) Vector of size \(m\)
Matrix of size \(m \times n\) Matrix of size \(n \times p\) Matrix of size \(m \times p\)

❗ Using the Dot function for an array and a constant yields the same result as using the \(*\)-Operator for the array and the value of the constant.

If the dimensions of the arrays to which the dot function is applied do not allow for a valid array multiplication, an exception is thrown.

Named arrays follow the same table, with labels in place of sizes. The axis that is summed over has to carry the same labels on both sides - the labels of a vector against the rows of a matrix, the columns of the left matrix against the rows of the right one. The axes that survive keep their own labels: rows come from the left operand, columns from the right. A vector times a matrix is therefore labelled by the matrix’s columns, because its rows are exactly what the sum consumed.

Three things follow from that:

  • The operands are paired by label, not by position, so the two may list their labels in a different order.
  • A named matrix has to carry the same column labels in every row. A matrix whose rows carry different labels is legal everywhere else, but here there would be no single axis to sum over.
  • A named array cannot be multiplied with an unnamed one - there is nothing for the labels to line up against.

The examples below show each shape once. Where a product has two directions, one of them is named and the other is not.

\[\begin{equation*} \begin{pmatrix} 1.0 \\ 2.0 \\ 3.0 \end{pmatrix} \cdot 2.0 = \begin{pmatrix} 2.0 \\ 4.0 \\ 6.0 \end{pmatrix}\end{equation*}\]

[2.0 , 4.0 , 6.0]

The other direction, and with a named vector. A constant multiplies every cell, so there is no axis to line up and the labels come through untouched:

\[\begin{equation*} 2.0 \cdot \begin{pmatrix} \text{'a'}: & 4.0 \\ \text{'b'}: & 5.0 \\ \text{'c'}: & 6.0 \end{pmatrix} = \begin{pmatrix} \text{'a'}: & 8.0 \\ \text{'b'}: & 10.0 \\ \text{'c'}: & 12.0 \end{pmatrix}\end{equation*}\]

{a: 8.0 , b: 10.0 , c: 12.0}

\[\begin{equation*} \begin{pmatrix} 1.0 & 2.0 \\ 3.0 & 4.0 \\ 5.0 & 6.0 \end{pmatrix} \cdot 2.0 = \begin{pmatrix} 2.0 & 4.0 \\ 6.0 & 8.0 \\ 10.0 & 12.0 \end{pmatrix}\end{equation*}\]

[ [2.0 , 4.0]
  [6.0 , 8.0]
  [10.0 , 12.0] ]

A named matrix this time, with rows north and south and columns a, b and c. As with the vector, a constant leaves the labels alone:

\[\begin{equation*} \begin{aligned} &2.0 \cdot \begin{pmatrix} \text{'north'}: & \{\text{'a'}: -1.0,\; \text{'b'}: -2.0,\; \text{'c'}: -3.0\} \\ \text{'south'}: & \{\text{'a'}: -4.0,\; \text{'b'}: -5.0,\; \text{'c'}: -6.0\} \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'north'}: & \{\text{'a'}: -2.0,\; \text{'b'}: -4.0,\; \text{'c'}: -6.0\} \\ \text{'south'}: & \{\text{'a'}: -8.0,\; \text{'b'}: -10.0,\; \text{'c'}: -12.0\} \end{pmatrix} \end{aligned} \end{equation*}\]

north: {a: -2.0 , b: -4.0 , c: -6.0}
south: {a: -8.0 , b: -10.0 , c: -12.0}

\[\begin{equation*} \left\langle \begin{pmatrix} 1.0 \\ 2.0 \\ 3.0 \end{pmatrix}, \begin{pmatrix} 4.0 \\ 5.0 \\ 6.0 \end{pmatrix} \right\rangle = 1.0 \cdot 4.0 + 2.0 \cdot 5.0 + 3.0 \cdot 6.0 = 32.0 \end{equation*}\]

32.0

The same scalar product with named vectors. The right operand lists its labels in the opposite order, which changes nothing: a meets a, b meets b, c meets c.

\[\begin{equation*} \begin{aligned} \left\langle \begin{pmatrix} \text{'a'}: & 4.0 \\ \text{'b'}: & 5.0 \\ \text{'c'}: & 6.0 \end{pmatrix}, \begin{pmatrix} \text{'c'}: & 3.0 \\ \text{'b'}: & 2.0 \\ \text{'a'}: & 1.0 \end{pmatrix} \right\rangle &= 4.0 \cdot 1.0 + 5.0 \cdot 2.0 + 6.0 \cdot 3.0 \\[2pt] &= 32.0 \end{aligned} \end{equation*}\]

32.0

\[\begin{equation*} \begin{pmatrix} 1.0 & 2.0 & 3.0 \\ \end{pmatrix} \cdot \begin{pmatrix} 1.0 & 2.0 \\ 3.0 & 4.0 \\ 5.0 & 6.0 \\ \end{pmatrix} = \begin{pmatrix} 1.0 \cdot 1.0 + 2.0 \cdot 3.0 + 3.0 \cdot 5.0 \\ 1.0 \cdot 2.0 + 2.0 \cdot 4.0 + 3.0 \cdot 6.0 \\ \end{pmatrix} = \begin{pmatrix} 22.0 & 28.0 \end{pmatrix} \end{equation*}\]

[22.0 , 28.0]

The same product with labels. The vector is labelled by product and so are the rows of the matrix - that is the axis the sum runs over, and it disappears. What is left are the matrix’s columns, so the result is labelled online and retail:

\[\begin{equation*} \begin{aligned} &\begin{pmatrix} \text{'a'}: & 1.0 \\ \text{'b'}: & 2.0 \\ \text{'c'}: & 3.0 \end{pmatrix} \cdot \begin{pmatrix} \text{'a'}: & \{\text{'online'}: 1.0,\; \text{'retail'}: 2.0\} \\ \text{'b'}: & \{\text{'online'}: 3.0,\; \text{'retail'}: 4.0\} \\ \text{'c'}: & \{\text{'online'}: 5.0,\; \text{'retail'}: 6.0\} \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'online'}: & 1.0 \cdot 1.0 + 2.0 \cdot 3.0 + 3.0 \cdot 5.0 \\ \text{'retail'}: & 1.0 \cdot 2.0 + 2.0 \cdot 4.0 + 3.0 \cdot 6.0 \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'online'}: & 22.0 \\ \text{'retail'}: & 28.0 \end{pmatrix} \end{aligned} \end{equation*}\]

{online: 22.0 , retail: 28.0}

\[\begin{equation*} \begin{aligned} &\begin{pmatrix} -1.0 & -2.0 & -3.0 \\ -4.0 & -5.0 & -6.0 \\ \end{pmatrix} \cdot \begin{pmatrix} 4.0 \\ 5.0 \\ 6.0 \\ \end{pmatrix} \\[2pt] &= \begin{pmatrix} -1.0 \cdot 4.0 + (-2.0) \cdot 5.0 + (-3.0) \cdot 6.0 \\ -4.0 \cdot 4.0 + (-5.0) \cdot 5.0 + (-6.0) \cdot 6.0 \\ \end{pmatrix} \\[2pt] &= \begin{pmatrix} -32.0 \\ -77.0 \end{pmatrix} \end{aligned} \end{equation*}\]

[-32.0 , -77.0]

And with labels - the same numbers, reusing the named matrix and the named vector from further up. Here the sum runs over the columns of the matrix, which carry the product labels, so what survives are its rows:

\[\begin{equation*} \begin{aligned} &\begin{pmatrix} \text{'north'}: & \{\text{'a'}: -1.0,\; \text{'b'}: -2.0,\; \text{'c'}: -3.0\} \\ \text{'south'}: & \{\text{'a'}: -4.0,\; \text{'b'}: -5.0,\; \text{'c'}: -6.0\} \end{pmatrix} \cdot \begin{pmatrix} \text{'a'}: & 4.0 \\ \text{'b'}: & 5.0 \\ \text{'c'}: & 6.0 \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'north'}: & -1.0 \cdot 4.0 + (-2.0) \cdot 5.0 + (-3.0) \cdot 6.0 \\ \text{'south'}: & -4.0 \cdot 4.0 + (-5.0) \cdot 5.0 + (-6.0) \cdot 6.0 \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'north'}: & -32.0 \\ \text{'south'}: & -77.0 \end{pmatrix} \end{aligned} \end{equation*}\]

{north: -32.0 , south: -77.0}

\[\begin{equation*} \begin{aligned} &\begin{pmatrix} 1.0 & 2.0 \\ 3.0 & 4.0 \\ \end{pmatrix} \cdot \begin{pmatrix} -1.0 & -2.0 \\ -4.0 & -5.0 \\ \end{pmatrix} \\[2pt] &= \begin{pmatrix} 1.0 \cdot (-1.0) + 2.0 \cdot (-4.0) & 1.0 \cdot (-2.0) + 2.0 \cdot (-5.0) \\ 3.0 \cdot (-1.0) + 4.0 \cdot (-4.0) & 3.0 \cdot (-2.0) + 4.0 \cdot (-5.0) \\ \end{pmatrix} \\[2pt] &= \begin{pmatrix} -9 & -12 \\ -19 & -26\\ \end{pmatrix} \end{aligned} \end{equation*}\]

[ [-9.0 , -12.0]
  [-19.0 , -26.0] ]

The same matrix product with labels. The left matrix runs from regions to products, the right one from products to channels - the products are what the two have in common and what the sum consumes. The result runs from regions to channels: its rows come from the left operand, its columns from the right.

\[\begin{equation*} \begin{aligned} &\begin{pmatrix} \text{'north'}: & \{\text{'a'}: 1.0,\; \text{'b'}: 2.0\} \\ \text{'south'}: & \{\text{'a'}: 3.0,\; \text{'b'}: 4.0\} \end{pmatrix} \cdot \begin{pmatrix} \text{'a'}: & \{\text{'online'}: -1.0,\; \text{'retail'}: -2.0\} \\ \text{'b'}: & \{\text{'online'}: -4.0,\; \text{'retail'}: -5.0\} \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'north'}: & \{\text{'online'}: 1.0 \cdot (-1.0) + 2.0 \cdot (-4.0), \\ & \phantom{\{} \text{'retail'}: 1.0 \cdot (-2.0) + 2.0 \cdot (-5.0)\} \\[2pt] \text{'south'}: & \{\text{'online'}: 3.0 \cdot (-1.0) + 4.0 \cdot (-4.0), \\ & \phantom{\{} \text{'retail'}: 3.0 \cdot (-2.0) + 4.0 \cdot (-5.0)\} \end{pmatrix} \\[2pt] &= \begin{pmatrix} \text{'north'}: & \{\text{'online'}: -9.0,\; \text{'retail'}: -12.0\} \\ \text{'south'}: & \{\text{'online'}: -19.0,\; \text{'retail'}: -26.0\} \end{pmatrix} \end{aligned} \end{equation*}\]

north: {online: -9.0 , retail: -12.0}
south: {online: -19.0 , retail: -26.0}