Creating ranged subarrays
All objects of typeILBaseArray<> (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)
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'.
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]
startandendmust 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 theendlimit.start,stepandendmust be integer numbers.startand/orendmay be the keyword 'end'. Otherwise they (of course) must fit inside the dimension length.
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 afor (...) 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.