Tutorial: Arrays
Release: |
4.6 • 5.0 • 5.1 • 5.2 • 5.3 • 5.4 • 6.0 • 6.1 • 6.2 • 6.3 • 6.4 • 6.5 |
---|
This section (page) introduces you to Intelligent Arrays, which is source of much of Analytica's power and flexibility. An array is a table of values, with one, two, or more dimensions. Even if you often skip tutorials, you may find it valuable to go through this section, because Analytica arrays have some unique aspects different from other modeling tools and computer languages. And it's essential to understand the basics of arrays before you can use Analytica effectively.
This chapter shows you how to:
- Create and define an Index
- Define an array variable as a Table
- Understand principles of array abstraction when combining arrays in an expression
- Use conditional expressions and logical values when defining an array
- Define variables using Expression syntax
- Use local values to simplify an expression
- Analyze a multi-dimensional result by pivoting indexes
- Reduce an array using subscripts
- Use array reducing functions such as Sum() and NPV()
In the previous chapter you defined a single variable, Miles per gallon, as a list of values. This is an example of a simple one-dimensional array. When you defined Fuel cost using the Miles per gallon array variable as an input, it also became an array. This demonstrates the concept of array abstraction. Array variables can be used just like ordinary variables in expressions. Whenever you expand an index of the array to include more values, or even add an entirely new index along a new dimension, all dependent variables downstream will be extended automatically! Intelligent arrays allow you to scale your model without making any changes to the design.
This section covers lots of ground. The example model is simple, but demonstrates many important array concepts. The approach is streamlined so you can learn the key ideas with-out getting bogged down with procedural details. You should already be familiar with the basic mechanics of Analytica‘s user interface, and know how to:
- Create a new model; Open an existing model; Save; Save As... (See Tutorial: Open a model to browse)
- Create and define new variables; Enter attributes in Attribute or Object windows; Draw influence arrows between nodes. (See Tutorial: Create a model)
Summarize variables using Expression syntax
You will need to know how to enter definitions directly as Expressions. Every variable type and every functional expression can be represented this way. In addition to its usefulness in building models, expression syntax is a convenient and efficient way to summarize examples in the documentation. For example, this summarizes the Car cost model from the previous section:
Variable MPY := 12K
Variable MPG := Sequence(20, 50, 5)
Variable Fuel_price := 3
Variable Fuel_cost := MPY*Fuel_price/MPG
We summarize new variables in this form in this section. It is also widely used throughout the Docs:
- The first term indicates the Class of the object being defined. Classes include Decision, Variable, Chance, Objective, Module, Index, Constant and Function.
- The second term is the Identifier for the object. (Identifiers may not contain spaces.)
:=
sometimes known as the assignment operator.- The definition is an expression, which could be a number, function call, probability distribution, table, or other expression. It may refer to other variables (the "Inputs" of this variable. You could even copy the expression directly into the Definition of a variable or function. The expressions are marked as
<code>
.
Fast Tony’s Generic Wheels
In this chapter you extend the Car cost model to address an important decision scenario: the purchase of a new car. There are three cars to choose from: Standard, SUV and Hybrid.
There are also three finance options: Cash purchase, Lease, or Loan. Fast Tony has presented you with an array of decision possibilities, a 3 x 3 array to be exact. Your objective is to determine the option with the lowest total cash outflow over a 24-month period, including fuel. (Maintenance costs are covered under warranty as long as Fast Tony’s cousin, Greasy Tony, is on his feet that day.) Ownership equity will be represented as a positive cash flow on the last period.
Continue with model
In this chapter, you will continue with the model you developed in Tutorial: Create a model. The model from the end of Tutorial: Create a model can be found in the Tutorial Models directory. Select Open on the File menu.
Create an index variable
Index variables define the categories along a dimension of an array. They are represented by index nodes (parallelogram shapes) in the influence diagram. In this example, your decision will be arrayed by Car type and Finance Option so you will need to define an index for each of these dimensions. Index variables are generally defined as a list of labels or a list of values representing categories in the array.
(For the finance options you will leave out "Loan" for now. It will be added later.)
Index Car_type := ['Standard', 'SUV', 'Hybrid']
Index Finance_option := ['Purchase', 'Lease']
[ ]
. Note that all text values in Expression syntax must be enclosed in quotes (single or double).Defining a variable as a table
Each of the cars has a unique purchase price. You will define Car price as a Table indexed by Car type. In this context, a "table" does not necessarily have two dimensions. It can have one (as in this case), two, three or more.
Variable Car_price := Table(Car_type)(22K, 40K, 24K)
After you close the Indexes dialog an Edit table will appear.
Enter the following values for Car price:
- Standard: '22K'
- SUV: 40K'
- Hybrid: '24K'
You can draw influence arrows from index nodes to table variables to pre-populate the Indexes dialog with the chosen indexes. Influence arrows from index nodes are invisible by default but can be made visible by choosing the Diagram menu and selecting Set diagram style.
Next you will create a variable for lease payments. Fast Tony lists the following monthly payments for the two-year lease option:
- Standard: '$400'
- SUV: $700'
- Hybrid: '$500'
Lease Payment is similar to the Car price variable. But this time you will enter the definition directly using expression syntax instead of using the edit table.
Variable Lease_payment := Table(Car_type)(400, 700, 500)
As you can see, Table definitions follow a specific syntax. The Table declaration is followed by two lists enclosed in parentheses. The first list contains the indexes by which the table will be arrayed. (In this simple example there is only one index but there can be many.) The second list contains the values.
It is important for the values to be in the same order as the index categories. Recall that the Car type index was defined as:
Index Car_type := ['Standard', 'SUV', 'Hybrid']
which matches the respective order of values in the Lease payment variable. The same principle applies when there is more than one index. For example, a two dimensional array might look like:
- Table (Index_abc, Index_123)
- ('a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3')
It is generally easier to use an edit table when multiple dimensions are involved.
Finally, re-define the existing Miles per gallon variable as a table indexed by Car type. The MPG values for Standard, SUV and Hybrid are 28, 23, and 45 respectively.
Variable Mpg := Table(Car_type)(28, 23, 45)
Select the Miles per gallon variable.
Open the Definition field and select from the Expression popup menu. Enter the new definition as shown above.
Combine arrays
Now that you have established purchase and lease payments, you can combine them in one variable. This time the index will be Finance Option.
Variable Payments := Table (Finance_option)(Car_price, Lease_payment)
Create a new variable titled Payments.
Open the definition field and select Table from the Expression popup menu.
The Indexes dialog appears. Select the Finance option index and transfer it to selected indexes list. Click OK. An edit table will appear.
You may have noticed an important difference between this table and the previous tables. In the previous tables you entered number values into the table cells, but this time you are entering expressions! Expressions are valid entries for table cells. With very few exceptions they can be as complex as anything you might enter in a standard definition line, including formulas, logical expressions and conditional expressions. You will see more examples of this later in the chapter.
Now look at the result of this new variable.
Select the Payments variable and click the Results button ()
Something interesting has happened! Notice that the array contains two dimensions even though only one index is called out in the definition of the array:
Variable Payments := Table (Finance_option)(Car_price, Lease_payment)
Why is Car type included in the result when Finance_option is the only index included in the definition of the table?
The Car type index is included because it is an index of the input variables Car price and Lease payment.
You have just encountered one of the most powerful features of Analytica’s array abstraction: The dimensions of arrayed input variables will automatically be incorporated into the resulting output of an expression. The general principle of array abstraction can be summarized as follows:
In Analytica, the result of an expression is arrayed by the union set of dimensions from all input variables, and the definition of the expression itself.
Add a dimension to an array
So far, the Payments variable is not very useful for comparing the purchase and lease options. Lease payments are paid every month instead of just once, and there is still ownership equity to consider. In order to represent payments as a stream of cash flows over time, you will need to add a new dimension to the Payments array:
Create and define a new index titled Period. Define it as a sequence of months from 0 to 24.
Index Period := Sequence(0, 24)
There is a special shortcut syntax for sequences that have a step value of one: Type the starting value, followed by two periods (..), followed by the final value. For example, the Period index can be defined as: Index Period := 0..24
Next, you will edit the Payments definition to include the new index. Try using the edit table for this:
There is a button in the top left corner of the edit table, labeled with a parallelogram. Click this button to open the Indexes window. Then add Period to the list of selected indexes.
The edit table should now look like this:
The subset of an array corresponding to a singe index value is referred to as a slice. When you expanded the Payments array to include a new index, the original values (expressions in this case) were copied into all new slices along the Period index.
The Payments array is not yet accurate. Under the Purchase column you should have zero values for all periods except Period 0. Lease payments should be included in periods 1 through 24 but not Period 0. It would not take too much effort to edit all the cells to be the way you want them to be. The final definition of the array would look something like this:
Although this would work, it would not be a very efficient approach. In general, whenever you find yourself editing tables in the same way you would edit a spreadsheet, you are not benefiting from the advantages of array abstraction.
Use conditional expressions
Expressions can include conditional statements using If...then...else syntax. The condition can be based on a variable or an index value. This is a very useful way of defining array variables. For example, the cash flow for purchasers can be represented as:
Likewise, the cash flow for lease holders can be represented as:
Specifying a condition based on Period will automatically add the Period index to the array. This would have been a better way to add a new dimension to the Payments variable, compared to the example above.
Let’s revert the Payments variable to the way it was before adding the Period index:
Variable Payments := Table(Finance_option)(Car_price, Lease_payment)
As you did in the section above, Select the Payments variable, open the edit table, and click the Index button in the top left corner.
Select the Period index in the Selected Indexes list and transfer it back to left hand box. Click OK. Answer Yes when the warning appears:
This time you will add the Period index by including it in a conditional expression. Edit the cells to include conditional expressions:
Variable Payments := Table(Finance_option)
Let’s take a step back to consider how many indexes are now included in the Payments array:
- Finance_option is called out specifically as a table index
- Period is the basis for a condition statement within an expression
- Car_type is an index of the Car price and Lease payment input variables. Therefore, you can expect that Payments will be a three-dimensional array.
Conditional expressions can have nested If-Then-Else statements. In fact, it would be possible to define the Payments array using only one conditional expression:
Variable Payments :=
This would be equivalent to the table definition above.
Logical statements can also be used to specify a condition. They are enclosed in parentheses and include an equal sign. When used in a mathematical expression, a logical statement assumes a value of one if true, or zero if false. For example:
24*(2 + 2 = 4) → 24
24*(2 + 2 = 5) → 0
Hence another equivalent to the previous table definition is
View the results of a multi-dimensional array
Representing a multi-dimensional array on a two-dimensional screen presents a challenge. When viewing results in tabular form, Analytica will display only a two-dimensional slice of the data. You can change the orientation and selection of data by pivoting the table. To do this, select the desired row and column indexes from the row and column pop-up menus. Any leftover indexes become slicer indexes for which only slice of data (corresponding to a single index value) can be displayed at a time.
Select the Payments variable and click the Results button ().
Select the table view button ().
You can experiment by selecting different row and column indexes, and by changing the value of the slicer index.
Now click on the graph view button ()
The vertical axis of the graph represents data values. The graph indexes can be pivoted to select the desired horizontal axis, key, and slicer index. You can experiment with various graph configurations and slicer values.
You can explore various chart types and options by selecting Graph Setup... from the Result menu. For more details about graphs reference Tutorial: Reviewing a model.
Add a new value to an index
Fast Tony’s loan financing terms require 10% down and 2% of the purchase price per month for 60 months. His financing manager, Big Anthony, explains that at a 12% nominal interest rate (1% per month compounded monthly), the load will be paid off in 60 months. There are severe penal- ties for delinquency but none of them are expressed in dollar values.
One of the advantages of intelligent arrays is that they are easily expandable. So far, you have set up cash flows for purchasers and lease holders. Adding a third financing option is a simple matter of adding a new value to the Finance option index.
Index Finance_option := ['Purchase','Lease','Loan']
Select the Finance option index and open the Definition field. Select List of Textfrom the Expression popup menu. You will see a list of current values in the definition field. Select the bottom value (Lease) and press the down-arrow key. A new box appears at the bottom of the list. By default the previous label is copied down. Change the new value to Loan.
Now select the Payments variable and click the Results button ().
A new column appears with zero values by default.
Now you will need to add an expression for loan payments in the edit table:
Car_price*(If Period = 0 then 10% else 2%)
Select the Payments variable. Open the Definition field, then click the Edit table button.
Recall that the input variable Car price is indexed by Car type. Therefore, your new expression will be evaluated separately for the Standard car, the SUV and the Hybrid. The new expression also references the Period index, so it will be evaluated separately for each period as well.
Using local values and indexes in an expression
Now that you have established all the cash outflows, you can start thinking about how much ownership equity you will have at the end of the 24-month period. Unavoidably you will need to calculate the remaining loan balance at that time. The financial dynamics of a typical loan include compound interest offset by fixed payment amounts. Although the formula for loan balances can be derived fairly easily, this section will focus on how to apply the formula rather than explaining the formula itself. Readers are invited to verify on their own if they wish:
Assuming that a constant amount is paid and nominal interest is compounded every month:
B0
= Starting balance of the loan12%
= Nominal interest rate60
= The total number of periods required to pay off the loanPeriod
= A number from 0 to 24 representing the current periodB(Period)
= The remaining loan balance for the current period- Let
A = (1 + 12% / 12)
B(Period) = B0 * (A^60 - A^Period) / (A^60 - 1)
It is easy to verify that B(0) = B0
and B(60)=0
as required.
The quantity A
appears several times in this formula so it makes sense to define it as an intermediate variable. To do the same thing in Analytica, you could define a new variable called A
and use it as an input to the loan balance variable. But this would clutter your diagram with an unnecessary node.
Analytica allows local values and indexes to be used temporarily within a definition. In this case you will define a local identifier.
Create a new variable titled Loan balance and enter the following definition:
Variable Loan_balance :=
Local A := (1 + 12%/12);
Car_price * (1 - 10%) * (A^60 - A^Period)/(A^60 - 1)
Syntax rules for local objects are as follows:
- The instruction line starts with
Local
orLocalIndex
to declare a local value or index. - The declaration is followed by an identifier. This can be anything you choose but it must not be identical to any other input contained in the expression.
- The assignment operator
:=
comes next (colon followed by an equal sign), followed by an expression defining the object. - A semicolon (
;
) is required to end the instruction line. You may choose to have a visual break (Enter key) after the semicolon for appearance purposes but this is optional. Analytica only looks at semicolons to mark line breaks in code. Each local identifier declaration must be on a separate code line. - The final line of instruction contains the definition as usual. All declared local values and indexes will be available to use in expressions.
Local variables are used temporarily and then forgotten after the expression is evaluated. Therefore, the same local identifier (i.e. A
) can be used in any number of nodes without conflict.
A final note about the expression for Loan Balance:
Although Period is an index, you have used it here as a variable in a mathematical expression. This is possible because Period is a list of numerical values. It would not have been possible to use Car type or Finance option in a mathematical formula since they are lists of labels (although they could be used in logical or conditional statements). In general, you can think of an index as a one-dimensional array that uses its own values as an index.
By the principles of array abstraction, you should expect Loan Balance to be a two-dimensional array indexed by Car type (an index of Car price) and Period.
The Future Value function Fv is a convenient way to calculate loan balances and annuity values without using formulas. Arguments in order are: Interest rate per period, Future period, Payments (negative for payouts), Initial value. For example, Loan Balance could be defined as: Fv(1%, Period, -Car_price*2%, Car_price*90%)
See Financial functions in the Analytica User Guide.
Reduce an array using subscripts
Sometimes you may want to reference part of an array variable without using all of its available indexes. Subscripts allow you to reference a slice of an array, effectively eliminating one or more dimensions. The subscript specifies indexes and their desired values for the slice. It is enclosed in square brackets placed immediately after the identifier for the array. For example:
The subscript has reduced a two-dimensional array to a one-dimensional array
Variable Final_period_balance := Loan_balance[Period = 24]
This refers only to the Loan Balance values that correspond to Period 24. Recall that Loan Balance is a two-dimensional array indexed by Period and Car Type. The subscripted reference above will eliminate the Period dimension and return a one-dimensional array indexed only by Car_type.
It is possible to eliminate multiple dimensions by including multiple indexes in the subscript, separated by commas. For example:
Variable Final_period_balance := Loan_balance[Period = 24, Car_type = 'SUV']
The result of this expression will be a single value corresponding to the loan balance at Period 24 for the SUV: 24.11K
An Index value can also be selected according to its position in the index rather than the actual value. To make a positional reference, place the "at" symbol (cat) in front of the index identifier. For example, 'SUV' is second in the order of values for the Car type index.
The following subscript references are equivalent:
Loan_balance[Car_type = 'SUV']
Loan_balance[@Car_type = 2]
The verbal terminology for this reference would be: "Loan_balance sliced at position of Car_type equal to 2".
Complete cash flows
Now you can add a variable to represent the amount of ownership equity you will have after 24 months. Of course this will depend on the finance option.
Assume that the value of every car depreciates by 35% over two years.
- Cash purchasers’ equity will be: Car_price (1 - 35%)
- Lease holders’ equity will be zero
- Loaners’ equity will be the difference between car value and the loan balance at Period 24: Car_price (1 - 35%) - Loan_balance[Period = 24]
The Equity variable has only two dimensions: Car type and Finance option.
It refers to a single slice of the Period index.
You can define the Equity variable as a table indexed by Finance option. Each of the expressions above can be entered into the appropriate cell in the edit table:
Variable Equity := Table(Finance_option)
(Car_price*(1 - 35%), 0, (Car_price*(1 - 35%) - Loan_balance[Period = 24]))
Alternatively, you can use conditional and logical statements as follows:
Variable Equity := If Finance_option = 'Lease' then 0 else
Car_price*(1 - 35%) - (Finance_option = 'Loan')*Loan_balance[Period = 24]
Once again, consider how many dimensions your new variable will have. At first you might be tempted to list the indexes as follows:
- Finance option (Specified as a table index or used in a conditional expression)
- Car type (Index of the input variables Car price and Loan balance)
- Period (Index of the input variable Loan balance)
But Loan balance is not an input to this expression; only a slice of it is used. The slice is arrayed only by Car type. Therefore, the Equity variable will be indexed by Finance option and Car type but not by Period.
It refers to a single slice of the Period index.
Combine dissimilar arrays
Now that you have established payments and ownership equity, you can combine them into a single cash flow variable. Payments will be represented as negative cash flows. Equity will be represented as a positive cash flow on the last period.
Create a new variable titled Cash flow and enter the following definition:
Variable Cash_flow := If Period = 24 then Equity - Payments else -Payments
In this expression you have combined arrays with different numbers of dimensions:
- Payments is indexed by Car type, Finance option and Period.
- Equity is indexed only by Car type and Finance option.
There is no ambiguity here since (Equity - Payments) applies to a slice along the Period index. The slice is called out in the conditional statement.
Notice that Equity and Payments[Period = 24] have the same dimensions.
There are other situations where you may want to use a scalar variable (not an array) as an input to an array. Similarly, you may want use an array as an input to another array that has more dimensions. In this situation Analytica will repeat the values of the smaller array for each slice along the new dimensions.
For example, suppose you want to incorporate monthly fuel cost into the Cash flow variable.
Here again you are combining two dissimilar arrays:
- Cash flow is indexed by Car type, Finance option and Period
- Fuel cost is indexed only by Car type
Create a new variable titled Cash flow with fuel and define it as follows:
Variable Cash_flow_with_fuel :=
If Period = 0 then Cash_flow else Cash_flow - Fuel_cost/12
In this example, Fuel cost is applied to multiple slices along new dimensions:
- All Period values except Period 0
- All three Finance option categories.
Select Cash_flow_with_fuel and click the Results button ()
You will notice "Totals" check boxes next to the row and column index pop-up menus. Checking the box next to Period will allow you to see totals along that index.
Array-Reducing Functions
A function that reduces one or more indexes of an array is referred to as an array reducing function. The output of an array reducing function will have fewer dimensions than the input array. For example, the following expression will determine the sum of cash flows over all periods:
Variable Total_cash_flow := Sum(Cash_flow_with_fuel, Period)
The input array is indexed by Car type, Finance option and Period. The output will be a two- dimensional array indexed by Car type and Finance option. In this example the Period dimension has been reduced.
- The first argument of the Sum() function is the identifier of the variable to be operated on.
- This is followed by a list of indexes separated by commas. These are the indexes over which the function will operate.
In this example you have taken a sum over a single index.
Similar examples of array-reducing functions include Max(), Min(), Product() and Average().
Net Present Value (NPV) analysis
If someone offered you a choice of receiving $100 today or receiving $100 two years from now, which would you choose? If you choose to receive it immediately, you can invest in a low risk bond with an annual return of, say, 6%. In two years the investment will be worth about $112. Clearly these two alternatives do not have equal value. Conversely, you would probably prefer to pay an obligation later rather than sooner.
Net present value (NPV) analysis is a way to compare various scenarios that involve inflows and outflows of money at different time periods. It converts a stream of cash flows to an equivalent scenario where all money is exchanged in a single transaction at the present time. The interest rate that can be achieved in a low risk investment is referred to as the discount rate. For example, given a discount rate of 6%, the NPV of $112 paid two years in the future would be $100.
By taking a simple sum of cash flows as in the example above, you have ignored the time value of money. A Net Present Value analysis is more useful in this case. The NPV() function in Analytica makes this easy. It is another example of an array reducing function.
Assume the discount rate is 0.5% per month. Create an objective node titled Cash flow NPV.
(Objective nodes are represented by the hexagon shape on the node palette.)
Apply the following definition:
Objective Cash_flow_NPV := NPV(0.5%, Cash_flow_with_fuel, Period)
- The first argument of the NPV function is the discount rate per period.
- The second argument is the identifier for the variable containing cash flows.
- The third argument is the index containing period values.
The analysis is complete!
Summary
In this chapter you have:
- Learned Expression syntax
- Created and defined index variables
- Created array variables as tables dimensioned along established indexes
- Expanded arrays by adding new indexes
- Expanded indexes by adding new index values
- Created examples that demonstrate the basic principle of array abstraction: The result of an expression is arrayed by the union set of dimensions from all input variables, and the definition of the expression itself.
- Used conditional and logical statements within an expression
- Established local values in the definition field
- Analyzed a multi-dimensional result by pivoting indexes
- Reduced array variables using subscripts
- Applied array-reducing functions to arrays
See Also
- Array
- Table
- Creating Arrays (Tables)
- Intelligent Arrays
- Arrays and Indexes
- Indexes and arrays: An introduction
- Creating an index
- Functions that create indexes
- Array-reducing functions
- Array functions
- Array Manipulation Examples and Challenge Problems
- Array Function Example Variables
- Multidimensional arrays
- Expression Syntax
- Expressions
- The Expression popup menu
- Class
- Classes of variables and other objects
- Local Values
- Subscript
- Subscript and slice of a subarray
- Identifier
- Modeling Depreciation
- NPV
- Choice menus and Checkboxes in an edit table
- Dynamic Simulation
- Tutorial videos
- Intelligent Array Abstraction (explanatory video on YouTube)
- Intro-to-arrays (Part 1).wmv (an explanatory video about Analytica arrays and indexes (part 1); requires Windows Media player)
- Intro-to-arrays (Part 2).wmv (an explanatory video about Analytica arrays and indexes (part 2); requires Windows Media player)
Enable comment auto-refresher