Skip to content

ElementManipulation

mikerabat edited this page Sep 25, 2020 · 3 revisions

Matrix Element Manipulation

This section shows how to set individual elementsj, rows or columns in the matrix as well as resizing, reshaping it.

Setting individual elements

A matrix is a 2 diminesional array of elements. The default property setter/getter is actually a direct Pascal matrix syntax:

   property Items[x, y : integer] : double read GetItems write SetItems; default;

An exception is raised in case x or y exceed the selected matrix range. Note that one can select sub matrices in a matrix and that the getter and setter methods take this into account (e.g. shifting index 0 to the selected position).

There is also the possibility to treat a whole matrix as a vector and assigning individual elements by accessing the properties:

   property Vec[idx : integer] : double read GetVecItem write SetVecItem; // matrix as vector
   property VecLen : integer read GetVecLen;  // width*height

The property VecLen is calculated by width x height and elements are set following the row major principle. E.g. if you have a 3x3 matrix x and assign x.Vec[1] := 2 then this maps actually to x.Items[1, 0] := 2 .

Examples

Setting diagonal elements:

   procedure TestDiag;
   var x : IMatrix;
       i : integer;
   begin
        x := TDoubleMatrix.Create(3, 3);
        for i := 0 to x.width - 1 do
            x[i, i] := 2;
   end;

Filling a vector with a sine:

   procedure TestVec;
   var x : IMatrix;
       i : integer;
   begin
        // create a vector
        x := TDoubleMatrix.Create(1, 100);
        for i := 0 to x.VecLen - 1 do
            x.Vec[i] := sin( 2*pi/50*i );
   end;

Setting the inner matrix to ones:

   procedure TestSetItem;
   var x : IMatrix;
       i, j : integer;
   begin
        // create a larger matrix
        x := TDoubleMatrix.Create(50, 50);
		
        // fill matrix x(25:30, 25:30) = 100 

        // select a 5x5 matrix
        x.SetSubMatrix(24, 24, 5, 5); 
        for i := 0 to x.Width - 1 do
            for j := 0 to x.Height - 1 do
            x[i, j] := 1;
				
        // undo the selection
        x.UseFullMatrix;
		
        // the above is the same as:
        for i := 24 to 29 do
            for j := 24 to 29 do
                x[i, j] := 1;
   end;

Manipualting more than one element at once

The library conveniently supports writing full rows/colums, copying matrices to sub elements of the actual matrix. Note that the functions only work on the selected sub matrix on the matrix:

   procedure SetRow(row : integer; const Values : Array of Double); overload;
   procedure SetRow(row : integer; Values : TDoubleMatrix; ValRow : integer = 0); overload;
   procedure SetRow(row : integer; Values : IMatrix; ValRow : integer = 0); overload;
   procedure SetColumn(col : integer; const Values : Array of Double); overload;
   procedure SetColumn(col : integer; Values : TDoubleMatrix; ValCols : integer = 0); overload;
   procedure SetColumn(col : integer; Values : IMatrix; ValCols : integer = 0); overload;

   procedure SetValue(const initVal : double); overload;
   procedure SetValue(const initVal : double; x, y, aWidth, aHeight : TASMNativeInt); overload;  
   
   procedure AssignSubMatrix(Value : TDoubleMatrix; X : integer = 0; Y : integer = 0); overload;
   procedure AssignSubMatrix(Value : IMatrix; X : integer = 0; Y : integer = 0); overload;

The SetRow/SetColumn take either an array of doubles (a vector) or a reference matrix. If it's a reference matrix one can select a row/column from this input matrix (the last ValRow, ValCols parameter) which is copied to the current one.

The procedure SetValue initializes the whole matrix with that value.

One can assign a smaller reference matrix to the current one and also use an offset. Note that an exception is raised in case the matrix does not fit.

A very special function to initialize a whole matrix is:

   procedure MaskedSetValue(const Mask : Array of boolean; const newVal : double);

The mask has to be the same size as the matrix and is treated as row major. Only elements with true are initialized with the given newVal Value.

Examples

Initialize columns/rows

   procedure TestRowCol;
   var x, y : IMatrix;
   begin
        x := TDoubleMatrix.Create(3, 3, 2);
        y := TDoubleMatrix.Create(3, 3, 1);
		
        // set the first column to 0
        x.SetColumn(0, [0, 0, 0]);
        // copy the second row from matrix y
        x.setRow(1, y, 1);  
		
        // copy the second column from y to the 3 column of x
        x.SetColumn(2, y, 1);
   end;

Initialize matrix with a different value:

   procedure TestSetValue;
   var x : IMatrix;
   begin
        x := TDoubleMatrix.Create(3, 3, -1);
        // set the matrix elements (which are currently -1) to 1
        x.SetValue(1);
   end;

Reshaping, Resizing and appending a matrix

The library supports to resize, append and reshape a matrix with the following methods:

   procedure Resize(aNewWidth, aNewHeight : Integer);           

   function Reshape(newWidth, newHeight : integer; RowMajor : boolean = False) : TDoubleMatrix;
   procedure ReshapeInPlace(newWidth, newHeight : integer; RowMajor : boolean = False);

   procedure RepeatMatrixInPlace(numX, numY : integer);
   function RepeatMatrix(numX, numY : integer) : TDoubleMatrix;

   procedure Append(Value : IMatrix; appendColumns : boolean); overload;
   procedure Append(Value : TDoubleMatrix; appendColumns : boolean); overload;

Resize allows to grow or shrink a matrix and preserves data (like realloc memory). New elements are initalized with 0. If only a subset of the matrix is selected this selection is lost but actually the submatrix is preserved.

_Reshape_s a matrix e.g. a vector to a matrix and vice versa. RowMajor determines how e.g. a vector fills up a new matrix. Note that an exception is raised if the dimesions do not fit.

RepeatMatrix extends the current matrix numX*numY times. The content of the first "field" (the initial matrix) is repeated over the new blocks.

Append allows you to merge two matrices either on the right (apendColumns = True) or on the bottom (appendColumns = False). If the width or height params do not fit then an exception is raised.

Examples

Load a matrix from the file and extend the content. This is part of the unit test ;)

   procedure TestLoadExtend;
   var x : IMatrix;
   begin
        x := ReadObjFromFile('mahalonobis.dat') as TDoubleMatrix;
        x.Resize(x.Width, x.Height + 1);

        // add outlier ;)
        x.SetRow(x.Height - 1, [ 1000, 1000 ]);
   end;

Testcase for append:

   procedure TestAppend;
   var x : TDoubleMatrix;
       i: Integer;
       j: Integer;
   begin
        x := TDoubleMatrix.CreateRand(4, 4);
        x.Append(x.Clone as IMatrix, False);

        for i := 0 to x.Width - 1 do
            for j := 0 to x.Height div 2 - 1 do
                Assert( x[i, j] = x[i, j + 4], 'Appending rows failed' );



        x.Free;  
   end;

Extracting selected rows and columns

In lending the idea of the matlab selectors one can now use the following functions to extract selected rows, columns or even items from a matrix:

   function SubColMtx( colIdx : TIntegerDynArray ) : TDoubleMatrix; overload;
   function SubRowMtx( rowIdx : TIntegerDynArray ) : TDoubleMatrix; overload;
   function SubMtx( colIdx : TIntegerDynArray; rowIdx : TIntegerDynArray ) : TDoubleMatrix; overload;
   function SubColMtx( fromIdx, toIdx : integer ) : TDoubleMatrix; overload;
   function SubRowMtx( fromIdx, toIdx : integer ) : TDoubleMatrix; overload;
   function SubMtx( fromColIdx, ToColIdx, fromRowIdx, ToRowIdx : integer ) : TDoubleMatrix; overload;

In effect all functions boil down to SubMtx . One can there use a column and row vector indices array to select certain values in a new resulting matrix.

Examples

Invert columns:

   procedure Invertcolumn;
   var mtx, mtx1 : TDoubleMatrix;
       x, y : integer;
       rows, cols : TIntegerDynArray;
   begin
        mtx := TDoubleMatrix.CreateRand(20, 20);

        // invert the columns
        mtx1 := mtx.SubColMtx(19, 0);

        for x := 0 to mtx.Width - 1 do
        begin
             for y := 0 to mtx.Height - 1 do
                 Check( mtx[19 - x, y] = mtx1[x, y], 'Bad selection');
        end;

        mtx1.Free;
        mtx.Free;
   end;

Select subsections from the matrix

   procedure TestSelect;
   var mtx, mtx1 : TDoubleMatrix;
       x, y : integer;
       rows, cols : TIntegerDynArray;
   begin
        mtx := TDoubleMatrix.CreateRand(20, 20);
        // sub selection
        rows := Arr([0, 2, 4, 5, 6, 7, 14, 19]);
        cols :=Arr([0, 1, 3, 4, 5, 6, 7, 12, 18]);
     
        mtx1 := mtx.SubMtx(cols, rows);

        for x := 0 to Length(cols) - 1 do
        begin
             for y := 0 to Length(rows) - 1 do
                 Check( mtx1[x, y] = mtx[ cols[x], rows[y] ], 'Bad selection');
        end;

        mtx1.Free;
        mtx.Free;
   end;
Clone this wiki locally