SubTable


The SubTable function displays a subset of another edit table as an editable view. To the user, a subtable looks like a normal edit table; but the values, including any changes you make, are stored in the original edit table. The subtable lets you select a particular row or column, a subset of an index, or a reordered index -- for, example, if you want to view a table with entries ordered alphabetically or numerically.

SubTable(v[i = x])

SubTable lets you treat a slice of table a as an Edit table, where variable «v» is defined as an Edit table, probability table, DetermTable, or another SubTable. It lets a model offer alternative editable views of the same input data.

SubTable must appear as the top-level function in an expression. It must contain a slice or subscript operator. For example, in the simplest form:

SubTable(A[I = J])

where «J» is an index containing a subset of the elements of «I», and «A» is a variable containing an edit table, probability table, or DetermTable.. Many other variations are also useful including:

SubTable(A[I = x])
SubTable(A[I1 = J1, I2 = J2])
SubTable(A[I = B])
SubTable(A[@I = C])

where x is a scalar, B and C are an array indexed by J.

Editable Subtables

A SubTable is said to be live when the following conditions hold:

  • Its parameter is a subscript operator, A[...]
  • The left argument to the operator, «A», is a variable identifier.
  • The variable, «A», is defined as one of the following:
  • All left-hand indexes in the subscript are table-indexes of «A».

The table dimensions of a variable defined as a Table, DetermTable, or ProbTable are those dimensions that are seen on the axes or slicers when the table is edited. Note that these are not necessarily the same as the dimensions of the result. The Table Dimensions can be obtained from the parse of the table without evaluating it.

The table dimensions of a variable defined as Subtable(A[I = B]) are determined by taking the table-dimensions of A and applying the indicated slice operations to those. Determining the table dimensions of A requires the evaluating of B, but not the evaluation of A.

When the subscript reduces the original edit table down to a single cell, the Subtable will have an empty set of table dimensions.

The SubTable Button

When a variable is defined as a Table, a button appears in the definition attribute which opens an edit table view. Likewise, when a variable is defined as a live SubTable, a "SubTable" button appears in the definition in the same fashion. If the SubTable is not live, the definition displays as an expression.

When a subtable has an empty set of table dimensions (i.e., the original table was sliced all the way to a single cell), the table button still appears, but the edit table that is opened by the button will have a single cell with no pivoters or slicers.

Input Node for a SubTable

The input node for a non-live SubTable will display as an expression in a text box.

The input node for a live-subtable having zero input dimensions displays as a text-box, with the corresponding cell definition in «A» displaying in the text box. Changing the value in the text box will change the cell in «A».

The input node for a live subtable having one or more input dimensions displays as a '"SubTable'" button. Pressing the button opens the edit subtable.

Edit Table View of a SubTable

In the Edit Table view of SubTable(A[I = J]), the grid is based on the «J» index, and the «I» index does not show. The subset of data from «A»'s Table definition is displayed to the user in the grid. When the user makes a change, the data is updated in the definition of «A». So even though the SubTable function appears in the definition of B, the changes are written to the definition of «A».

Various complications must be dealt with:

  • What happens with elements in J that are not in I?
  • What if «J» has duplicate elements?
  • Can more than one SubTable or Edit Table view of the same variable, «A», be simultaneously visible / open?
  • What if «J» is a function of «A», so changes to «A» cause «J» to change while editing? This isn't too unreasonable -- the selection of the subset «J» may be based on fields of «A» not shown in the SubTable.
  • Under what conditions can rows be added/removed from «J», or elements renamed, from the SubTable view? When this is done, how are «I» and «J» updated.
  • Which items are modifiable in browse mode? (as a function of input controls)
  • How does the index chooser behave from a SubTable view?
  • What if «Jn» is an index of «A»?
  • What if «A» is defined by a top-level SubTable function?

Multiple Index Case

The two-index use of SubTable(A[I = J]) reindexes «A» from «I» to «J». Multiple subscripts (or slices) can be specified simultaneously, such as:

SubTable(ProjectData[AllProjects = SelectedProjects, AllFields = OnlyAdminFields])

Here SelectedProjects is a subset of AllProjects and OnlyAdminFields is a subset of AllFields.

Unrecognized Elements

During evaluating, if «J» contains an element not in «I», then the evaluation of SubTable(A[I = J]) would match the behavior that A[I = J] would exhibit in the same situation -- warning and returning of null.

In an edit view, if «J» contains elements not in «I», then the corresponding cells are left blank and are uneditable.

Duplicate Elements

Consider a SubTable(A[I = B]) definition. When B contains the same element of I multiple times, then the subTable will contain multiple cells that alias the same cell in the original table. It is possible for B to introduce dimensions without this happening (in fact, this provides an interesting way to turn a flat input table into a multi-D input table).

When a user edits a cell in the sub-table edit view, all other cells referring to the same cell in the original need to synchronize when the value is entered. However, it is acceptable for the synchronization to occur only when the green button is pressed to accept changes, with the most-recently entered cell sticking.

When a block of data is copy/pasted into a sub-table and spans two or more cells that refer to a common cell in the original, only the value for one of those cells will "stick" -- after the copy/paste, all cells referring to the same original cell will once again have the same definition.

Simultaneous Table Views

At some point in the future, it should become possible to embed tables directly on diagrams. At that point, the ability to simultaneously view multiple tables and subtables of the same table will become important. At that point, the same mechanism might as well work when multiple table windows are open.

When an edit table or edit SubTable detects a change in «A», it should resynchronize. This synchronization would only occur when the green-check button is pressed.

In order to avoid the problem in which changes made in one view clobber changes made in another view, only one open view of «A» at a time shall allow tentative changes to be pending, even if the cells visible in each view do not overlap. When you attempt to make the first change to a cell, the view will ensure no other open view has pending changes before continuing. If another view does have pending changes, the user will be informed that he must accept or cancel the changes in another window before changing the values here.

Quasi-Cyclical Dependencies

Consider this example:

Fields := ['Priority', 'Cost', 'Duration']
ProjectData := Table(Project, Fields)
SelectedProject := Subset(ProjectData[Fields = 'Priority'] > 10)
SelectedProjectData := SubTable(A[Project = SelectedProject])

In this case, a change to a priority in SelectedProjectData can cause a change in the SelectedProject index.

The change will not take effect until the user presses the accept button (the green check). At that time, the edit table will resynchronize to the new SelectedProject index, so that the rows appearing in the edit table view change (in this case, it is likely that some rows will disappear).

Changes to Indexes

When you are editing a standard edit table, you can make changes (add or remove elements or change element labels), to the indexes under some circumstances, namely:

  • The index must be defined as a literal list.
  • You are in edit mode, or the index has an input node.

The same conditions could apply when editing a view of SubTable(A[I = J]). The J index can be edited from the edit table view under exactly these same conditions, i.e., if it is defined by a literal list, etc.

When you insert a new element into J, and assuming that element is found in I, the existing data from A's definition is imported to fill the cells of this new row.

If an element of J is deleted, any pending changes are updated in A's definition before the row is removed.

If you rename an element of J, the corresponding element of I is not named.

Note: This feature would not be essential in an initial implementation of SubTable. If not implemented, then the indexes simply would be non-changeable.

Input Controls and Browse Mode

Suppose A is an edit table and B is defined by SubTable(A[I = J]). From browse mode, the edit table of A is read-only unless A has an input node. It does not matter whether B has an input node, if the edit table for A is opened, only the present or absence of A's input node determines whether it is editable.

In browse mode, the view of a SubTable node is editable either if it, or its parent, has an input node. Note that if we required B to have the input node, the user would still be able to change the data if A had an input node, by simply editing it from A, hence it makes sense for either condition to suffice.

Index Chooser

The index chooser for a live Subtable would need to be disabled, or would need to bring up an alternative dialog tailored to SubTable. It isn't just a matter of choosing indexes, as it is with a table.

Re-indexes to Indexes in A

What happens if a sub-index is already in A? Two cases to consider:

SubTable(A[I = J]) where A is indexed by I and J
SubTable(A[I1 = J, I2 = I1]) where A is indexed by I1 and I2

When evaluated, the first case would select the "diagonal" of the array A. In the second case, a rotation occurs, the J dimension in the subtable view corresponds to the I1 dimension in the original, but the I1 dimension in the SubTable is actually the I2 dimension in the original.

These cases are noted because they need to be considered during implementation, testing, not because they are particularly important. If difficult to implement, it would be acceptable to not enable to edit table view if any J index is already in A.

Recursive SubTables

There is a potential for recursive SubTable functions, for example:

A := Table(I)
B := SubTable(A[I = J])
C := SubTable(B[J = K])

In this case, editing C would cause A's table definition to change. The indexes end up being mapped through two levels, not just one.

In an initial implementation, it would be okay to not allow editing of C when it is nested. Evaluation would still be okay. In the longer term, it would save on end-user confusion and just seem more consistent if it handled the recursive case.

Evaluation of SubTable

In most cases, SubTable just evaluates and returns its parameter.

The exception is when obtaining SubTables of DetermTables and ProbTables, where SubTable makes it possible to access rows of the original DetermTable that aren't selected in the original DetermTable's result.

SubTables of DetermTables

Suppose D is a DetermTable with discrete selector variables Dv1 and Dv2. Using SubTable, you can access the rows of D that aren't selected by Dv1 and Dv2.

To demonstrate these ideas, consider the following discrete variables and DetermTable:

Decision Dv1 := Choice(Self, 3)
Domain of Dv1 : [1, 2, 3, 4]
Decision Dv2 := Choice(Self, 4)
Domain of Dv2 : [1, 2, 3, 4]
Determ D :=
Dv1 ▶
Dv2 ▼ 1 2 3 4
1 11 21 31 41
2 12 22 32 42
3 13 23 33 43
4 14 24 34 44

The evaluation of D results in a single number, 34, the cell in yellow, since it is selected out of the DetermTable by its discrete parent variables. However, SubTable allows you to access the other cells excluded by the selectors.

Slice or Subscript operations inside SubTable are applied first. If a discrete variable dimension remains, then it is sliced according to the value of the discrete variable selector:

SubTable(D[Dv1 = 2,Dv2 = 2]) → 22
SubTable(D[Dv1 = 2]) → 24
SubTable(D[Dv2 = 2]) → 32
SubTable(D) → 34

An optional parameter to SubTable, «fullDeterm», can be set to true to disable selection by discrete variables.

SubTable(D[Dv2 = 2], fullDeterm: true) →
Dv1 ▶
1 2 3 4
21 22 23 24
SubTable(D[Dv1 = 2], fullDeterm: true) →
Dv2 ▶
1 2 3 4
12 22 32 42
SubTable(D, fullDeterm: true) →
Dv1 ▶
Dv2 ▼ 1 2 3 4
1 11 21 31 41
2 12 22 32 42
3 13 23 33 43
4 14 24 34 44

SubTables of ProbTables

Since ProbTables are specializations of DetermTables, you can use SubTable to select rows from the original ProbTable as shown in the previous section.

An optional parameter to SubTable, «rawProbs», controls whether the result is the Mid-value or Sample-Value (depending on Evaluation Context), or whether it is the original probabilities from the table. By default, «rawProbs» is false, so that the distribution is returned. Samples are generated independently (but with the same marginal distribution, of course) as the result of the original probability table.

SubTable(PT, rawProbs: true, fullDeterm: true) → { returns the full table of probabilities }

Troubleshooting

Displays as text, not as SubTable button

When the argument to SubTable is not in the correct form, the definition displays as an expression. The parameter may be a totally valid expression, and may evaluate just fine, so that the definition is not invalid as an expression, it is simply not of a form that can be used to display a table.

If you are experiencing this and cannot figure out why it is not displaying as a button, here are some things you might want to look for:

  • Is the expression inside SubTable either a Slice (i.e., A[@I = expr]) or Subscript (i.e., A[I = expr]) expression?
  • Is the source table, «A», a Table, SubTable, DetermTable, IntraTable, ProbTable or MultiTable that successfully displays in table form?
  • Is the index of the subscript or slice one of the source table's indexes?
  • Does the «expr» part evaluate without an error?
  • If the source, «A», is an expression, does the expression recursively satisfy all of the above?

See Also

Comments


You are not allowed to post comments.