//+------------------------------------------------------------------+ //| matrix_utils.mqh | //| Copyright 2022, Omega Joctan . | //| https://www.mql5.com/en/users/omegajoctan | //+------------------------------------------------------------------+ #property copyright "Copyright 2022, Omega Joctan" #property link "https://www.mql5.com/en/users/omegajoctan" #include //+------------------------------------------------------------------+ //| A class containing additional matrix manipulation functions | //+------------------------------------------------------------------+ class MatrixExtend { protected: template static T MathRandom(T mini, T maxi); static string CalcTimeElapsed(double seconds); static void Swap(double &var1, double &var2); static string ConvertTime(double seconds); template static void GetCol(const T &Matrix[], T &Col[], int column, int cols); static bool IsNumber(string text); static vector FixColumn(CLabelEncoder &encoder, string &Arr[], double threshold =0.3); public: MatrixExtend(void); ~MatrixExtend(void); template static int Sign(T var) { if (var<0) return -1; else if (var==0) return 0; else return 1; } //--- File Functions template static bool WriteCsv(string csv_name, matrix &matrix_, string &header[] ,bool common=false, int digits=5); template static bool WriteCsv(string csv_name, matrix &matrix_, string header_string="",bool common=false, int digits=5); static matrix ReadCsv(string file_name, string &headers, string delimiter=",",bool common=false, bool auto_encode=false); static matrix DBtoMatrix(int db_handle, string table_name,string &column_names[],int total=WHOLE_ARRAY); static bool write_bin(vector &v, string file); //--- Manipulations template static bool RemoveCol(matrix &mat, ulong col); static void RemoveMultCols(matrix &mat, int &cols[]); static void RemoveMultCols(matrix &mat, int from, int total=WHOLE_ARRAY); static void RemoveRow(matrix &mat,ulong row); static void VectorRemoveIndex(vector &v, ulong index); //--- Machine Learning template static bool XandYSplitMatrices(const matrix &matrix_, matrix &xmatrix, vector &y_vector,int y_column=-1); template static void TrainTestSplitMatrices(const matrix &matrix_, matrix &x_train, vector &y_train, matrix &x_test, vector &y_test, double train_size=0.7,int random_state=-1); static matrix DesignMatrix(matrix &x_matrix); static matrix OneHotEncoding(const vector &v); //ONe hot encoding static matrix Sign(matrix &x); static vector Sign(vector &x); static matrix eye(uint num_features); //--- Detection static void Unique(const string &Array[], string &classes_arr[]); static vector Unique(const vector &v); //Identifies classes available in a vector static vector Unique_count(vector &v); template static vector Random(T min, T max, int size,int random_state=-1); //Generates a random vector of type T sized = size static matrix Random(double min, double max, ulong rows, ulong cols, int random_state=-1); template static vector Search(const vector &v, T value); //--- Transformations static matrix VectorToMatrix(const vector &v, ulong cols=1); template static vector MatrixToVector(const matrix &mat); template static vector ArrayToVector(const T &Arr[]); template static bool VectorToArray(const vector &v,T &arr[]); //--- Manipulations static vector concatenate(const vector &v1, const vector &v2); //Appends v2 to vector 1 static matrix concatenate(const matrix &mat1, const matrix &mat2, int axis = 0); template static matrix concatenate(const matrix &mat, const vector &v, int axis=1); template static bool Copy(const vector &src, vector &dst, ulong src_start,ulong total=WHOLE_ARRAY); template static void Reverse(vector &v); template static void Reverse(matrix &mat); static matrix HadamardProduct(matrix &a, matrix &b); template static void Randomize(vector &v, int random_state=-1, bool replace=false); template static void Randomize(matrix &matrix_,int random_state=-1, bool replace=false); template static void NormalizeDouble_(vector &v, int digits=3); template static void NormalizeDouble_(matrix &mat, int digits=3); static int CopyBufferVector(int handle, int buff_num, int start_pos,int count, vector &v); static string Stringfy(vector &v, int digits = 2); static matrix Zeros(ulong rows, ulong cols) { matrix ret_mat(rows, cols); return(ret_mat.Fill(0.0)); } static vector Zeros(ulong size) { vector ret_v(size); return( ret_v.Fill(0.0)); } static matrix Get(const matrix &mat, ulong start_index, ulong end_index); static vector Get(const vector &v, ulong start_index, ulong end_index); template static vector Sort(vector &v,ENUM_SORT_MODE sort_mode=SORT_ASCENDING); template static vector ArgSort(vector &v); static matrix Slice(const matrix &mat, ulong start, ulong end, uint axis=0); static vector Slice(const vector &vec, ulong start, ulong end); //--- Others static void PrintShort(matrix &matrix_,ulong rows=5, int digits=5); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ MatrixExtend::MatrixExtend(void) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ MatrixExtend::~MatrixExtend(void) { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ matrix MatrixExtend::VectorToMatrix(const vector &v, ulong cols=1) { ulong rows = 0; matrix mat = {}; if ( v.Size() % cols > 0) //If there is a reminder { printf("Invalid rows %d and cols %d for this vector size ",rows,v.Size()/cols); return mat; } else rows = v.Size()/cols; //--- mat.Resize(rows, cols); for(ulong i=0, index =0; i vector MatrixExtend::MatrixToVector(const matrix &mat) { vector v = {}; matrix temp_mat = mat; if (!temp_mat.Swap(v)) Print(__FUNCTION__," Failed to turn the matrix[",mat.Rows(),"x",mat.Cols(),"] into a vector"); return(v); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool MatrixExtend::RemoveCol(matrix &mat, ulong col) { matrix new_matrix(mat.Rows(),mat.Cols()-1); //Remove the one Column if (col > mat.Cols()) { Print(__FUNCTION__," column out of range"); return false; } for (ulong i=0, new_col=0; i mat.Cols()) { Print(__FUNCTION__," Columns to remove can't be more than the available columns"); return; } vector Zeros(mat.Rows()); Zeros.Fill(0); for(ulong i=0; i= size) for(ulong i=0; i (int)mat.Cols()) { Print(__FUNCTION__," Columns to remove can't be more than the available columns"); return; } vector Zeros(mat.Rows()); Zeros.Fill(0); for (int i=from; i= remain_size && !IsStopped()) { //printf("cols %d total %d",cols,total); for(ulong i=0; i bool MatrixExtend::WriteCsv(string csv_name, matrix &matrix_, string &header[], bool common=false, int digits=5) { string header_str = ""; for (int i=0; i bool MatrixExtend::WriteCsv(string csv_name, matrix &matrix_, string header_string="", bool common=false, int digits=5) { FileDelete(csv_name); int handle = FileOpen(csv_name,FILE_WRITE|FILE_CSV|FILE_ANSI|(common?FILE_COMMON:FILE_IS_WRITABLE),",",CP_UTF8); if (header_string == "" || header_string == NULL) for (ulong i=0; i row = {}; datetime time_start = GetTickCount(), current_time; string header[]; ushort u_sep; u_sep = StringGetCharacter(",",0); StringSplit(header_string,u_sep, header); vector colsinrows = matrix_.Row(0); if (ArraySize(header) != (int)colsinrows.Size()) { printf("headers=%d and columns=%d from the matrix vary is size ",ArraySize(header),colsinrows.Size()); return false; } //--- string header_str = ""; for (int i=0; i=size*threshold); if (is_strings_col) //if a column is detected to be a column full of strings { //Encode it return encoder.encode(Arr);; } //--- string value = ""; int total =0; double mean=0; for (int i=0; i= 48 && char1 <= 57) || char1 == 46) && pointcount < 2) continue; else return false; } // If all characters in the text have been checked without returning false, return true. return true; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ matrix MatrixExtend::ReadCsv(string file_name, string &headers, string delimiter=",",bool common=false, bool auto_encode=false) { CLabelEncoder encoder; string Arr[]; int all_size = 0; int cols_total=0; int handle = FileOpen(file_name,FILE_SHARE_READ|FILE_CSV|FILE_ANSI|(common?FILE_COMMON:FILE_ANSI),delimiter); datetime time_start = GetTickCount(), current_time; string header_arr[]; int header_column = 0; if(handle == INVALID_HANDLE) { printf("Invalid %s handle Error %d ",file_name,GetLastError()); Print(GetLastError()==0?" TIP | File Might be in use Somewhere else or in another Directory":""); } else { int column = 0, rows=0; while(!FileIsEnding(handle) && !IsStopped()) { string data = FileReadString(handle); //--- if(rows ==0) { header_column++; ArrayResize(header_arr, header_column); header_arr[header_column-1] = data; } column++; if(rows>0) //Avoid the first column which contains the column's header { all_size++; ArrayResize(Arr,all_size); Arr[all_size-1] = data; } //--- if(FileIsLineEnding(handle)) { cols_total=column; rows++; column = 0; current_time = GetTickCount(); Comment("Reading ",file_name," record = ",rows," Time taken | ",ConvertTime((current_time - time_start) / 1000.0)); } } FileClose(handle); } //--- Get the headers headers=""; for(uint i=0; i void MatrixExtend::GetCol(const T &Matrix[], T &Col[], int column, int cols) { int rows = ArraySize(Matrix)/cols; ArrayResize(Col,rows); int start = 0; for (int i=0; i vector MatrixExtend::ArrayToVector(const T &Arr[]) { vector v(ArraySize(Arr)); for (int i=0; i bool MatrixExtend::VectorToArray(const vector &v, T &arr[]) { vector temp = v; if (!temp.Swap(arr)) { Print("Failed to Convert vector to Array Err=",GetLastError()); return false; } return(true); } //+------------------------------------------------------------------+ //| | //| | //+------------------------------------------------------------------+ template bool MatrixExtend::XandYSplitMatrices(const matrix &matrix_, matrix &xmatrix, vector &y_vector,int y_column=-1) { y_column = int( y_column==-1 ? matrix_.Cols()-1 : y_column); if (matrix_.Rows() == 0 || matrix_.Cols()==0) { #ifdef DEBUG_MODE printf("%s Line %d Cannot split the matrix of size[%dx%d]",__FUNCTION__,__LINE__,matrix_.Rows(),matrix_.Cols()); #endif return false; } y_vector = matrix_.Col(y_column); xmatrix.Copy(matrix_); return RemoveCol(xmatrix, y_column); //Remove the y column } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void MatrixExtend::Randomize(vector &v, int random_state=-1, bool replace=false) { MathSrand(random_state!=-1?random_state:GetTickCount()); int swap_index; double temp; int SIZE = (int)v.Size(); vector temp_v = v; for (int i=0; i void MatrixExtend::Randomize(matrix &matrix_,int random_state=-1, bool replace=false) { MathSrand(random_state!=-1?random_state:GetTickCount()); int ROWS=(int)matrix_.Rows(), COL=(int)matrix_.Cols(); int swap_index; vector temp(COL); matrix temp_m = matrix_; int random = 0; for (int i=0; i void MatrixExtend::TrainTestSplitMatrices(const matrix &matrix_, matrix &x_train, vector &y_train, matrix &x_test, vector &y_test, double train_size=0.7,int random_state=-1) { ulong total = matrix_.Rows(), cols = matrix_.Cols(); ulong last_col = cols-1; //--- Random pseudo matrix matrix ret_matrix = matrix_; Randomize(ret_matrix,random_state); //--- int train = (int)MathFloor(total*train_size); int test = (int)total-train; x_train.Resize(train,cols-1); x_test.Resize(test,cols-1); y_train.Resize(train); y_test.Resize(test); int train_count = 0, test_count = 0; Copy(ret_matrix.Col(last_col),y_train,0,train); Copy(ret_matrix.Col(last_col),y_test,train); for(ulong i=0; i T MatrixExtend:: MathRandom(T mini, T maxi) { double f = (MathRand() / 32767.0); return (mini + (T)(f * (maxi - mini))); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template vector MatrixExtend::Random(T min, T max,int size,int random_state=-1) { MathSrand(random_state!=-1?random_state:GetTickCount()); vector v(size); for (ulong i=0; i(min,max); return (v); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ matrix MatrixExtend::Random(double min,double max,ulong rows,ulong cols,int random_state=-1) { MathSrand(random_state!=-1?random_state:GetTickCount()); matrix mat(rows,cols); for (ulong r=0; r(min,max); return (mat); } //+------------------------------------------------------------------+ //| Appends vector v1 to the end of vector v2 | //+------------------------------------------------------------------+ vector MatrixExtend::concatenate(const vector &v1, const vector &v2) { vector v_out = v1; v_out.Resize(v1.Size()+v2.Size()); for (ulong i=0; i0) || (axis == 1 && mat1.Rows() != mat2.Rows() && mat1.Rows()>0)) { Print(__FUNCTION__, "Err | Dimensions mismatch for concatenation"); return m_out; } if (axis == 0) { m_out.Resize(mat1.Rows() + mat2.Rows(), MathMax(mat1.Cols(), mat2.Cols())); for (ulong row = 0; row < mat1.Rows(); row++) { for (ulong col = 0; col < m_out.Cols(); col++) { m_out[row][col] = mat1[row][col]; } } for (ulong row = 0; row < mat2.Rows(); row++) { for (ulong col = 0; col < m_out.Cols(); col++) { m_out[row + mat1.Rows()][col] = mat2[row][col]; } } } else if (axis == 1) { m_out.Resize(MathMax(mat1.Rows(), mat2.Rows()), mat1.Cols() + mat2.Cols()); for (ulong row = 0; row < m_out.Rows(); row++) { for (ulong col = 0; col < mat1.Cols(); col++) { m_out[row][col] = mat1[row][col]; } for (ulong col = 0; col < mat2.Cols(); col++) { m_out[row][col + mat1.Cols()] = mat2[row][col]; } } } return m_out; } //+------------------------------------------------------------------+ //| Concatenates the vector to a matrix, axis =0 along the rows | //| while axis =1 along the colums concatenation //+------------------------------------------------------------------+ template matrix MatrixExtend::concatenate(const matrix &mat, const vector &v, int axis=1) { matrix ret= mat; ulong new_rows, new_cols; if (axis == 0) //place it along the rows { if (mat.Cols() == 0) ret.Resize(mat.Rows(), v.Size()); new_rows = ret.Rows()+1; new_cols = ret.Cols(); if (v.Size() != new_cols) { Print(__FUNCTION__," Dimensions don't match the vector v needs to have the same size as the number of columns in the original matrix"); return ret; } ret.Resize(new_rows, new_cols); ret.Row(v, new_rows-1); } else if (axis == 1) { if (mat.Rows() == 0) ret.Resize(v.Size(), ret.Cols()); new_rows = ret.Rows(); new_cols = ret.Cols()+1; if (v.Size() != new_rows) { Print(__FUNCTION__," Dimensions don't match the vector v needs to have the same size as the number of rows in the original matrix"); return ret; } ret.Resize(new_rows, new_cols); ret.Col(v, new_cols-1); } else { Print(__FUNCTION__," Axis value Can either be 0 or 1"); return ret; } //--- return ret; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template bool MatrixExtend::Copy(const vector &src, vector &dst,ulong src_start,ulong total=WHOLE_ARRAY) { if (total == WHOLE_ARRAY) total = src.Size()-src_start; if ( total <= 0 || src.Size() == 0) { printf("%s Can't copy a vector | Size %d total %d src_start %d ",__FUNCTION__,src.Size(),total,src_start); return (false); } dst.Resize(total); dst.Fill(0); for (ulong i=src_start, index =0; i vector MatrixExtend::Search(const vector &v, T value) { vector v_out ={}; for (ulong i=0, count =0; i vector MatrixExtend::Sort(vector &v,ENUM_SORT_MODE sort_mode=SORT_ASCENDING) { T arr[]; vector temp = v; temp.Swap(arr); if (!ArraySort(arr)) printf("%s Failed to sort this vector Err=%d",__FUNCTION__,GetLastError()); switch(sort_mode) { case SORT_ASCENDING: temp = MatrixExtend::ArrayToVector(arr); break; case SORT_DESCENDING: temp = MatrixExtend::ArrayToVector(arr); MatrixExtend::Reverse(temp); break; default: printf("%s Unknown sort mode"); break; } return temp; } //+------------------------------------------------------------------+ //| Returns the Sorted Argsuments in either ascending order or | //| descending order | //+------------------------------------------------------------------+ template vector MatrixExtend::ArgSort(vector &v) { //--- ulong size = v.Size(); vector args(size); // Initialize args array with sequential values for (ulong i = 0; i < size; i++) args[i] = (int)i; // Perform selection sort on args based on array values for (ulong i = 0; i < size - 1; i++) { ulong minIndex = i; for (ulong j = i + 1; j < size; j++) { if (v[(int)args[j]] < v[(int)args[minIndex]]) minIndex = j; } // Swap args int temp = (int)args[i]; args[i] = args[minIndex]; args[minIndex] = temp; } return args; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void MatrixExtend::Reverse(vector &v) { vector v_temp = v; for (ulong i=0, j=v.Size()-1; i void MatrixExtend::Reverse(matrix &mat) { matrix temp_mat = mat; for (ulong i=0, j=mat.Rows()-1; i is a binary operation that takes two | //| matrices of the same dimensions and produces another matrix | //| of the same dimension as the operands. | This operation is | //| widely known as element wise multiplication | //+------------------------------------------------------------------+ matrix MatrixExtend::HadamardProduct(matrix &a,matrix &b) { matrix c = {}; if (a.Rows() != b.Rows() || a.Cols() != b.Cols()) { Print("Cannot calculate Hadamard product | matrix a and b are not having the same size "); return c; } //--- return a*b; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string MatrixExtend::CalcTimeElapsed(double seconds) { string time_str = ""; uint minutes=0, hours=0; if(seconds >= 60) time_str = StringFormat("%d Minutes and %.3f Seconds ",minutes=(int)round(seconds/60.0), ((int)seconds % 60)); if(minutes >= 60) time_str = StringFormat("%d Hours %d Minutes and %.3f Seconds ",hours=(int)round(minutes/60.0), minutes, ((int)seconds % 60)); else time_str = StringFormat("%.3f Seconds ",seconds); return time_str; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ matrix MatrixExtend::DBtoMatrix(int db_handle, string table_name,string &column_names[],int total=WHOLE_ARRAY) { matrix matrix_ = {}; #ifdef DEBUG_MODE Print("---> loading database "); #endif if(db_handle == INVALID_HANDLE) { printf("db handle failed, Err = %d",GetLastError()); DatabaseClose(db_handle); return matrix_; } //--- string sql = "SELECT * FROM "+table_name; int request = DatabasePrepare(db_handle,sql); ulong cols = DatabaseColumnsCount(request), rows =0; ArrayResize(column_names,(int)cols); //--- matrix_.Resize(cols,0); double time_start = GetMicrosecondCount()/(double)1e6, time_stop=0; //Elapsed time double row_start = 0, row_stop =0; for (int j=0; DatabaseRead(request) && !IsStopped(); j++) { row_start = GetMicrosecondCount()/(double)1e6; rows = (ulong)j+1; matrix_.Resize(cols,rows); for (ulong k=0; k= total) break; #ifdef DEBUG_MODE row_stop =GetMicrosecondCount()/(double)1e6; printf("Row ----> %d | Elapsed %s",j,CalcTimeElapsed(row_stop-row_start)); #endif } //--- DatabaseFinalize(request); DatabaseClose(db_handle); matrix_ = matrix_.Transpose(); //very crucial step #ifdef DEBUG_MODE time_stop = GetMicrosecondCount()/(double)1e6; printf("---> finished reading DB size=(%dx%d) | Time Elapsed %s",rows,cols,CalcTimeElapsed(time_stop-time_start)); ArrayPrint(column_names); for (ulong i=0; i<5; i++) Print(matrix_.Row(i)); #endif return matrix_; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ template void MatrixExtend::NormalizeDouble_(vector &v,int digits=3) { for (ulong i=0; i void MatrixExtend::NormalizeDouble_(matrix &mat,int digits=3) { for (ulong i=0; i= 60) { minutes = (uint)(seconds / 60.0) ; seconds = fmod(seconds, 1.0) * 60; time_str = StringFormat("%d Minutes and %.3f Seconds", minutes, seconds); } if (minutes >= 60) { hours = (uint)(minutes / 60.0); minutes = minutes % 60; time_str = StringFormat("%d Hours and %d Minutes", hours, minutes); } if (time_str == "") { time_str = StringFormat("%.3f Seconds", seconds); } return time_str; } //+------------------------------------------------------------------+ //| Obtains a part of the matrix starting from a start_index row to | //| end_index row Inclusive | //+------------------------------------------------------------------+ matrix MatrixExtend::Get(const matrix &mat, ulong start_index, ulong end_index) { matrix ret_mat(MathAbs(end_index-start_index+1), mat.Cols()); if (start_index >= mat.Rows()) { Print(__FUNCTION__," Error | start_index (",start_index,") is greater than or Equal to matrix Rows (",mat.Rows(),")"); return ret_mat; } if (end_index > mat.Rows()) { Print(__FUNCTION__," Error | end_index (",end_index,") is greater than (",mat.Rows(),")"); return ret_mat; } if (start_index > end_index) { Print(__FUNCTION__," Error | start_index shouldn't be greater than end_index ???"); return ret_mat; } for (ulong i=start_index, count =0; i<=end_index; i++, count++) for (ulong col=0; col= v.Size()) { Print(__FUNCTION__,"Error | start_index (",start_index,") is greater than or Equal to matrix Rows (",v.Size(),")"); return ret_vec; } if (end_index > v.Size()) { Print(__FUNCTION__,"Error | end_index (",start_index,") is greater than (",v.Size(),")"); return ret_vec; } if (start_index > end_index) { Print(__FUNCTION__,"Error | start_index shouldn't be greater than end_index ???"); return ret_vec; } for (ulong i=start_index, count=0; i<=end_index; i++, count++) ret_vec[count] = v[i]; return ret_vec; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ matrix MatrixExtend::Sign(matrix &x) { matrix ret_matrix = x; for (ulong i=0; i=end) { printf("%s failed, end must be >= start",__FUNCTION__); return sliced; } for (ulong i=start, count=0; imat.Rows()) { printf("%s failed, Index out of range line %d",__FUNCTION__,__LINE__); return sliced; } sliced.Resize(end-start, mat.Cols()); sliced.Row(mat.Row(i) , count); break; case 1: //slice along columns if (end-start>mat.Cols()) { printf("%s failed, Index out of range line %d",__FUNCTION__,__LINE__); return sliced; } sliced.Resize(mat.Rows(), end-start); sliced.Col(mat.Col(i) , count); break; default: printf("%s failed, Unknown axis value %d",__FUNCTION__, axis); return sliced; break; } } return sliced; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ vector MatrixExtend::Slice(const vector &vec, ulong start, ulong end) { vector sliced = {}; if (start>=end) { printf("%s failed, end must be >= start",__FUNCTION__); return sliced; } sliced.Resize(end-start); for (ulong i=start, count=0; ivec.Size()) { printf("%s failed, Index out of range line %d",__FUNCTION__,__LINE__); return sliced; } sliced[count] = vec[i]; } return sliced; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+