Creating ranged subarrays

All objects of type ILBaseArray<> (and it's derived type ILArray<>) own the public member Subarray(). The following overloads exist:
  • Subarray(params ILBaseArray[] range)
  • Subarray(params string[] range)
  • Subarray(int shift, params ILBaseArray[] range)
  • Subarray(int shift, params string[] range)
All of these functions create a new array from all or parts of the original array and optionally shift the dimensions of the result.

The shift parameter specifies the number of dimensions the result will be shifted after creation. If it is zero or ommited (by calling one of the first two overloads), the result will have the same orientation as the original.

The 2nd parameter defines the "part" used to derive the subarray from. This definition is called a "range" definition. Just like in other mathematical frameworks it is possible to specify the needed range in a very convenient way:

  • The definition may be given as an array of strings. Each string corresponds to one dimension in the original array. Alternatively there is a short definition for supplying only one string and seperating the dimensions with the semicolon character ';'.
  • by specifying an array of ILBaseArray objects. Each ILBaseArray is expected to hold numeric elements in a vector of arbitrary orientation. There will be one ILBaseArray for each dimension of the original array. Alternatively one may specify only one ILBaseArray. This corresponds to a special case called 'sequential index access'.
When defining a range, each parameter in the range parameter list (or each substring in the case of single string definition) chooses indices of the original array for one corresponding dimension. For matrices the first range definition will choose the rows, the second will choose the columns. For N-dimensional arrays the 3-rd string will choose the elements from the 3-rd dimension etc.
Lets take a closer look now, on how to exactly specify those ranges.

Defining ranges by strings

Each string parameter for one dimension may consist out of a comma seperated list of any of

  • integer numbers explicitely counting the indices needed,
  • a colon ':' as a special character substituting for the whole corresponding dimension,
  • the 'end' keyword as a placeholder for the last element in the corresponding dimension,
  • an automated counter definition in the format
    [start]:[end],
    which will expand to
    [start],[start+1],[start+2],...[end-1],[end]
    start and end must be integer numbers or the keyword 'end'. Of course the numbers must fit inside the dimension length.
  • an automated stepped counter definition in the format
    [start]:[step]:[end],
    which will expand to
    [start],[start+step],[start+2*step],...[start+?*step] until the number exeeds the end limit. start, step and end must be integer numbers. start and/or end may be the keyword 'end'. Otherwise they (of course) must fit inside the dimension length.
Each of the items above may appear arbitrary times and in arbitrary order. Each string must start with such an item. Each item must be followed by a comma, a colon, whitespace or nothing.

Example 1 (C#):
In the example we will construct a matrix of size 3 x 4. Than we create a subarray of the full matrix - including all rows and all columns. Keep in mind, indices of array elements are 0-based in C#! Therefore the first index starts at 0. The last element has the index [length-1].

ILArray<double> A = ILMath.counter(3,4);
ILArray<double> B = A.Subarray("0,1,2","0,1,2,3");
/* A.ToString() is now:
A
    {<double>
41149443 [3x4] Phys.
(:,:)
1,00000 4,00000 7,00000 10,00000
2,00000 5,00000 8,00000 11,00000
3,00000 6,00000 9,00000 12,00000

and B.ToString() brings:
    B
    {<double> 41142329 [3x4] Ref(2)
(:,:)
1,00000 4,00000 7,00000 10,00000
2,00000 5,00000 8,00000 11,00000
3,00000 6,00000 9,00000 12,00000
The same result we might as well produce by one of the following statements:
B = A.Subarray(":",":");
B = A.Subarray("0:end",":");
B = A.Subarray("0:2","0:1:end");
B = A.Subarray(":;:");

Now lets create another subarray. This time we create a partial subarray of the same matrix - including only some rows and some columns:

Example 2 (C#)
B = A.Subarray("0,2","0,3");
/* B.ToString() is now:

B
{<double> 42332320 [3x4] Ref(3)
(:,:) 
1,00000 10,00000
3,00000 12,00000 
The same result we could produce with one of the following statements:
B = A.Subarray("0,2","0,end");
B = A.Subarray("0,end","0,end");
B = A.Subarray("0,end;0,3");

Example 3 (C#)
Now we will demonstrate some more sophisticated range definitions. Since we use C# in the examples, let's use the indexing features of that language! This shortens the syntax a lot and makes it much more readable and fun. For indexers the same arguments as for the Subarray method are used directly after the array within brackets:
... = A[params string[] range]
B = A[":,2,1,0","1,3,0:3"];
/* B.ToString() is now:
B
{<double> 4878312 [3x4] Ref(3)
(:,:) 
4,00000  10,00000  1,00000  4,00000  7,00000  10,00000
5,00000  11,00000  2,00000  5,00000  8,00000  11,00000    
6,00000  12,00000  3,00000  6,00000  9,00000  12,00000    
6,00000  12,00000  3,00000  6,00000  9,00000  12,00000
5,00000  11,00000  2,00000  5,00000  8,00000  11,00000
4,00000  10,00000  1,00000  4,00000  7,00000  10,00000
The rows count from the first to the last row and than back from the last to the first row. For columns the second (index 1), the last index (3) and after that all columns together again are selected. This will result in the matrix above.
The same result we would have produced with one of the following statements:
B = A[":,2:-1:0","1,3,0:end"];
B = A[":,2:-1:0","1,3,:"];
B = A[":,end,1,0;1,end,:"];

Defining ranges by arrays

Just like string definitions, the range definition for each dimension can be selected from vectors (ILBaseArray's) directly. This is often more convenient, if some of the indices are to be choosen from variables themself - like iteration variables in a for (...) statement f.e.
To be used as dimension specifier, each ILBaseArray must fullfill the following condition.
If exactly one ILBaseArray is used for each dimension in the params parameter list, each ILBaseArray must either be
  • a row vector or a column vector of arbitrary length, or
  • a scalar, or
  • empty or null, which will evaluate to the whole corresponding dimension (!)

All numeric types may be used as inner type. All elements of the ILBaseArrays must - of course - evaluate to some integer inside the dimension length.

Example 4 (C#)
Use an array of ILBaseArray's to create the same matrix as in Example 2:
ILArray<short> D1 = new ILArray<short>(0,2);
ILArray<float> D2 = new ILArray<float>(0.0f,3.0f);
B = A[D1,D2];
/* B.ToString() is now:
B
{<double> 42332320 [3x4] Ref(4)
(:,:)
1,00000  10,00000
3,00000  12,00000
Notice the ILBaseArrays do not have to have the same type! They might as well be of floating point types, evaluating to integers.
Also the params keyword in C# prevents you from having to create real ILBaseArray[] arrays. You just provide single ILBaseArray objects in a comma separated parameter list.
The next example demonstrates a more realistic situation. Here we will access a matrix by an iteration variable and by scalar constants.

Example 5 (C#)
Consider again the matrix A from the examples above. We will iterate through the columns and transfer them into another matrix. Columns having an even index will get copied into the destination matrix on corresponding position. For columns having an odd index the last column will be inserted.
ILArray<double> A = ILMath.counter(3,4);
ILArray<double> Dest = ILMath.zeros(3,4);
for (int i = 0; i < A.Dimensions[1]; i++) {
    if (i%2 == 0) {
        Dest[null,i] = A[null,i]; 
    } else {
        Dest[null,i] = A[null,0]; 
    }  
}
/* Dest.ToString() is now:
Dest
{<double> 45523402 [3x4] Phys.
(:,:)
1,00000 1,00000 7,00000 1,00000
2,00000 2,00000 8,00000 2,00000
3,00000 3,00000 9,00000 3,00000 */
Here null specify's the whole dimension. In this case this means 'all the rows'. The integer variable i will implicitly cast to ILArray<int> which is a derived type from ILBaseArray and therefore absolutely valid to be used here. The same is true for the scalar constant in the second assignement. '0' will implicitly cast to ILArray<int>.
Of course there are much easier ways to create the same result:
ILArray<double> Dest = (ILArray<double>)A.Clone();
ILArray<int> ind = new ILArray<int>(1,3);
Dest[null,ind] = ILMath.repmat(A[null,0],1,ind.Length);
// or: 
ILArray<double> Dest = (ILArray<double>)A.Clone();
Dest[":;1,3"] = ILMath.repmat(A[null,0],1,2);
// or even more simple: 
ILArray<double> Dest = A[":;0,0,2,0"];  

For more details of the set methods read altering arrays - tutorial.

Valid CSS! Valid XHTML 1.0 Transitional