Skip to content

Slice Notation

Meinrad Recheis edited this page May 8, 2019 · 4 revisions

NDArray can be sliced using Python slice notation with the slight difference, that the slice definition has to be passed as a string, for syntactical reasons.

Reshape and slice illustration

A slice is constructed either by range notation ["start:stop:step"] or by index notation ["index"].

Range Notation Examples:

a["5:10"]        // return 5 elements starting at index 5 through index 9
a["5:"]          // return the rest of the array starting at index 5
a[":10"]         // return 10 elements from the beginning through index 9 (same as "0:10")

The key point to remember is that the :stop value represents the first value that is not in the selected slice. So, the difference between stop and start is the number of elements selected (if step is 1, the default).

Stepping illustration

There is also the step value, which can be used with any of the above:

a[$"{start}:{stop}:{step}"] // start through not past stop, by step
a[new Slice(start, stop, step)] // same as [$"{start}:{stop}:{step}"]

Note the difference between indexing with integers and strings. Indexing with integers will return a scalar value of type T. Indexing with slice notation strings will return an NDArray.

a[5]           // returns the element at index 5
a["5"]         // returns a NDArray which contains only the element at index 5, Shape is (), which means scalar
a["5:6:1"]     // same as ["5"] except for the Shape which is (1)

The other feature is that start or stop may be a negative number, which means it counts from the end of the array instead of the beginning. So:

a[-1]         // last item in the array
a["-2:"]      // last two items in the array
a[":-2"]      // everything except the last two items

Similarly, step may be a negative number:

a["::-1"]     // all items in the array, reversed
a["1::-1"]    // the first two items, reversed
a[":-3:-1"]   // the last two items, reversed
a["-3::-1"]   // everything except the last two items, reversed

Like NumPy NumSharp is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[":-2"] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen.

Slicing 2-dimensional arrays

Slicing illustration

The above summary of the slicing notation showed only 1D examples. NDArray can represent and slice 2D, 3D or ND shaped data by allowing a slice definition per dimension separated by comma.

a[":"]           // return everything (but without copying)
a["5, :"]        // return the whole 5th row of a 2D matrix (index notation, range notation)
a[Slice.Index(5), Slice.All()]   // same as ["5, :"]
a["5, :100"]     // return the first 100 elements of the 5th row

Note that NDArray per default represents 2D matrices in row major style, meaning that you address an element in a 2D array like this: a[row, column]. By this definition you can access a column by indexing the rows with ":" (= range over all rows) like this:

a[":, 5"]        // return the whole 5th column of a 2D matrix
a[Slice.All(), Slice.Index(5)]   // same as [":, 5"]
a[":100, 5"]     // return the first 100 elements of the 5th column

Slicing N-dimensional arrays

When slicing an N-dimensional slice of an N-dimensional NDArray you can specify N slicing definitions to define how the volume is or is not reduced in every dimension. Consider the following example where we cut a smaller cube out of a cube.

var cube=np.arange(27).reshape(3,3,3);
// slicing a 2x2x2 cube out of a 3x3x3 cube by skipping the first element of each dimension
var cube1=cube["1:,1:,1:"];

If you specify less slicing definitions than dimensions, the missing dimensions will be returned in whole as if you had passed ":".

Reducing dimensions by slicing

When you specify an index instead of a range (i.e. ["5"] instead of ["5:6:1"]) you get a slice of reduced dimensions. Let me explain in more detail:

Let's say you want to get a row or a column out of a 2D matrix as a 1D Vector:

var matrix=np.arange(25).reshape(5,5);
// slicing the 2nd row
var row = matrix["1"]; // Note: this is NOT the same as matrix[1] which gives the first element of that row 
// slicing the 2nd colum
var column = matrix[":,1"];

The result of both slicing operations is a 1D vector of shape (5), so we have effectively reduced the dimensions from two to one. In comparison, if not using index notation for slicing but range notation we get a 2D matrix with only one column or one row:

var matrix=np.arange(25).reshape(5,5);
// slicing the 2nd row with range notation
var row = matrix["1:2"];
// slicing the 2nd colum with range notation
var column = matrix[":,1:2"];

As a result of slicing with range notation above sample gives us row as a 2D matrix of shape (1,5) and column as a matrix of shape (5,1). So you can see, in comparison to the index notation, the range notation preserves the dimensionality of the view.

Further reading: Slicing in NumSharp