English Русский 中文 Español Deutsch 日本語
preview
Matrix Utils, estendendo as matrizes e a funcionalidade da biblioteca padrão de vetores

Matrix Utils, estendendo as matrizes e a funcionalidade da biblioteca padrão de vetores

MetaTrader 5Estatística e análise | 9 março 2023, 09:41
440 0
Omega J Msigwa
Omega J Msigwa

“Você nunca alcançará a perfeição porque sempre há espaço para melhorias.

No entanto, percorrendo o caminho para a perfeição, você aprenderá a melhorar.

Introdução

A classe Utils em python é uma classe utilitária de propósito geral com funções e linhas de código que podemos reutilizar sem criar uma instância de uma classe.

A biblioteca padrão para as matrizes nos fornece alguns recursos e métodos muito importantes que podemos usar para inicializar, transformar, manipular matrizes e muito mais, mas como qualquer outra biblioteca já construída, ela pode ser estendida para executar coisas extras que podem ser necessárias em alguns dos aplicativos.


As funções e métodos apresentados abaixo não estão em nenhuma ordem específica;

Conteúdo:


Lendo uma matriz de um arquivo CSV

Esta é uma função muito importante, pois nos permite carregar bancos de dados de um arquivo CSV e armazená-los em uma matriz sem esforço. Não há necessidade de enfatizar a importância desta função porque, durante a programação dos sistemas de negociação, muitas vezes precisamos carregar arquivos CSV contendo sinais de negociação ou histórico de negociação que queremos que o nosso programa possa acessar.

A primeira etapa na função (você adivinhou certo?) é ler um arquivo CSV. A primeira linha do arquivo CSV é considerada como tendo apenas strings, portanto, mesmo que haja valores na primeira linha, eles não serão incluídos na matriz já que a matriz não pode conter valores de string, em vez disso, os valores da primeira linha serão armazenados no array csv_header.
      while(!FileIsEnding(handle))
        {
         string data = FileReadString(handle);
         //---
         if(rows ==0)
           {
            ArrayResize(csv_header,column+1);
            csv_header[column] = data;
           }
         if(rows>0)  //Avoid the first column which contains the column's header
            mat_[rows-1,column] = (double(data));
         column++;
         //---

Essa matriz ajuda a acompanhar os nomes das colunas do arquivo CSV que acabamos de ler.

Código completo:

matrix CMatrixutils::ReadCsv(string file_name,string delimiter=",")
  {
   matrix mat_ = {};
   int rows_total=0;
   int handle = FileOpen(file_name,FILE_READ|FILE_CSV|FILE_ANSI,delimiter);
   ResetLastError();
   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))
        {
         string data = FileReadString(handle);
         //---
         if(rows ==0)
           {
            ArrayResize(csv_header,column+1);
            csv_header[column] = data;
           }
         if(rows>0)  //Avoid the first column which contains the column's header
            mat_[rows-1,column] = (double(data));
         column++;
         //---
         if(FileIsLineEnding(handle))
           {
            rows++;
            mat_.Resize(rows,column);
            column = 0;
           }
        }
      rows_total = rows;
      FileClose(handle);
     }
   mat_.Resize(rows_total-1,mat_.Cols());
   return(mat_);
  }

Uso:

Aqui está o arquivo CSV que queremos carregar em uma matriz

Carregando o arquivo CSV em uma matriz;

   Print("---> Reading a CSV file to Matrix\n");
   Matrix = matrix_utils.ReadCsv("NASDAQ_DATA.csv"); 
   
   ArrayPrint(matrix_utils.csv_header);
   Print(Matrix);
Saída:
CS      0       06:48:21.228    matrix test (EURUSD,H1) "S&P500" "50SMA"  "13RSI"  "NASDAQ"
CS      0       06:48:21.228    matrix test (EURUSD,H1) [[4173.8,13386.6,34.8,13067.5]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4179.2,13396.7,36.6,13094.8]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4182.7,13406.6,37.5,13108]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4185.8,13416.8,37.1,13104.3]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4180.8,13425.2,34.9,13082.2]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4174.6,13432.2,31.8,13052]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4174.9,13440.4,33.2,13082.2]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4170.8,13447.6,32.2,13070.6]
CS      0       06:48:21.228    matrix test (EURUSD,H1)  [4182.2,13457.8,32.5,13078.8]

A razão pela qual eu decidi construir esta função é que eu não consegui encontrar uma maneira de ler um arquivo CSV da Biblioteca Padrão, eu esperava que este fosse o caminho a seguir para carregar os valores de um arquivo para um CSV Matrix.FromFile()

mas eu fiquei desapontado, se alguém souber como fazer isso nos documentos, informe-me na seção de discussão deste artigo.

Ler valores codificados do arquivo CSV

Muitas vezes, você pode precisar ler um arquivo CSV que contém valores de string, esses tipos de arquivos CSV que contêm strings são frequentemente encontrados quando se está procurando fazer algoritmos de classificação, dê uma olhada no conjunto de dados meteorológicos, que é bem popular;

weather dataset

Para poder codificar este arquivo csv, nós vamos ler todos os seus dados em uma única matriz de valores de string, porque as matrizes não podem carregar os valores de string dentro da função ReadCsvEncode(), a primeira coisa que temos a fazer é ler e conhecer as colunas presentes no arquivo CSV, em segundo lugar, fazemos um loop nessas colunas onde, a cada iteração, um csv é aberto e uma única coluna é lida inteiramente e os valores dessa coluna são obtidos e armazenados na matriz de todos os valores nomeados toArr[].

//--- Obtaining the columns
   matrix Matrix={}; 
//--- Loading the entire Matrix to an Array
   int csv_columns=0,  rows_total=0; 
   int handle = CSVOpen(file_name,delimiter);  
   if (handle != INVALID_HANDLE)
      {
       while (!FileIsEnding(handle))
         {
           string data = FileReadString(handle);

           csv_columns++;
//---
           if (FileIsLineEnding(handle)) break;
         } 
      }      
   FileClose(handle);   
   ArrayResize(csv_header,csv_columns);  
//---
   string toArr[];
    int counter=0; 
    for (int i=0; i<csv_columns; i++)
      {                    
        if ((handle = CSVOpen(file_name,delimiter)) != INVALID_HANDLE)
         {  
          int column = 0, rows=0;
          while (!FileIsEnding(handle))
            {
              string data = FileReadString(handle);
              column++;
   //---      
              if (column==i+1)
                 {                      
                     if (rows>=1) //Avoid the first column which contains the column's header
                       {   
                           counter++;                       
                           ArrayResize(toArr,counter); //array size for all the columns 
                           toArr[counter-1]=data;
                       }
                      else csv_header[column-1]  =  data;
                 }
   //---
              if (FileIsLineEnding(handle))
                {                     
                   rows++;
                   column = 0;
                }
            } 
          rows_total += rows-1; 
        }
       FileClose(handle); 
     }
//---

Feito isso, o toArr[] fica assim quando impresso;

CS      0       06:00:15.952    matrix test (US500,D1)  [ 0] "Sunny"    "Sunny"    "Overcast" "Rain"     "Rain"     "Rain"     "Overcast" "Sunny"    "Sunny"    "Rain"     "Sunny"    "Overcast"
CS      0       06:00:15.952    matrix test (US500,D1)  [12] "Overcast" "Rain"     "Hot"      "Hot"      "Hot"      "Mild"     "Cool"     "Cool"     "Cool"     "Mild"     "Cool"     "Mild"    
CS      0       06:00:15.952    matrix test (US500,D1)  [24] "Mild"     "Mild"     "Hot"      "Mild"     "High"     "High"     "High"     "High"     "Normal"   "Normal"   "Normal"   "High"    
CS      0       06:00:15.952    matrix test (US500,D1)  [36] "Normal"   "Normal"   "Normal"   "High"     "Normal"   "High"     "Weak"     "Strong"   "Weak"     "Weak"     "Weak"     "Strong"  
CS      0       06:00:15.952    matrix test (US500,D1)  [48] "Strong"   "Weak"     "Weak"     "Weak"     "Strong"   "Strong"   "Weak"     "Strong"   "No"       "No"       "Yes"      "Yes"     
CS      0       06:00:15.952    matrix test (US500,D1)  [60] "Yes"      "No"       "Yes"      "No"       "Yes"      "Yes"      "Yes"      "Yes"      "Yes"      "No"      

Se você prestar atenção a este array, notará que os valores de um arquivo CSV estão na ordem que foi obtido do arquivo CSV, do índice 0 ao 13 está a primeira coluna, do índice 14 ao 27 está a segunda coluna, etc. e assim por diante. A partir daí é fácil extrair os valores e codificá-los para finalmente armazená-los em uma matriz.

    ulong mat_cols = 0,mat_rows = 0;    
    if (ArraySize(toArr) % csv_columns !=0)
     printf("This CSV file named %s has unequal number of columns = %d and rows %d Its size = %d",file_name,csv_columns,ArraySize(toArr) / csv_columns,ArraySize(toArr));
    else //preparing the number of columns and rows that out matrix needs
     {
        mat_cols = (ulong)csv_columns;       
        mat_rows = (ulong)ArraySize(toArr)/mat_cols;
     }
//--- Encoding the CSV
     Matrix.Resize(mat_rows,mat_cols);          
//---
     string Arr[]; //temporary array to carry the values of a single column      
     int start =0;      
     vector v = {};      
       for (ulong j=0; j<mat_cols; j++)
         {
            ArrayCopy(Arr,toArr,0,start,(int)mat_rows);          
            v = LabelEncoder(Arr);                        
            Matrix.Col(v, j);            
            start += (int)mat_rows;
         }      
   return (Matrix);

A função LabelEncoder() faz exatamente como o seu nome diz, ela coloca o mesmo rótulo para os recursos que são semelhantes na matriz de colunas, aqui está o que está dentro.

vector CMatrixutils::LabelEncoder(string &Arr[])
 {
   string classes[];   
   vector Encoded((ulong)ArraySize(Arr));   
   Classes(Arr,classes);    
    for (ulong A=0; A<classes.Size(); A++)
      for (ulong i=0; i<Encoded.Size(); i++)
        {
           if (classes[A] == Arr[i])
              Encoded[i] = (int)A;
        }    
   return Encoded;
 }

Esta função é fácil, ela faz um loop das classes fornecidas em todo o array para que ela possa encontrar os recursos semelhantes e colocar os rótulos neles, a maior parte do trabalho é feita dentro da função Classes() destacada em preto.

void CMatrixutils::Classes(const string &Array[],string &classes_arr[])
 {
   string temp_arr[];
   ArrayResize(classes_arr,1);
   ArrayCopy(temp_arr,Array);   
   classes_arr[0] = Array[0];   
   for(int i=0, count =1; i<ArraySize(Array); i++)  //counting the different neighbors
     {
      for(int j=0; j<ArraySize(Array); j++)
        {
         if(Array[i] == temp_arr[j] && temp_arr[j] != "nan")
           {
            bool count_ready = false;

            for(int n=0; n<ArraySize(classes_arr); n++)
               if(Array[i] == classes_arr[n])
                    count_ready = true;

            if(!count_ready)
              {
               count++;
               ArrayResize(classes_arr,count);

               classes_arr[count-1] = Array[i]; 

               temp_arr[j] = "nan"; //modify so that it can no more be counted
              }
            else
               break;
            //Print("t vectors vector ",v);
           }
         else
            continue;
        }
     }
 }

Esta função obtém as classes presentes no array fornecido e passa para o array referenciado classes_arr[]. Esta função tem uma variante para localizar as classes em um vetor. Mais detalhes sobre esta função são encontrados aqui.

vector CMatrixutils::Classes(vector &v)

Abaixo está como usar esta função;

   Matrix = matrix_utils.ReadCsvEncode("weather dataset.csv");
   ArrayPrint(matrix_utils.csv_header);
   Print(Matrix);

Saída:

CS      0       06:35:10.798    matrix test (US500,D1)  "Outlook"     "Temperature" "Humidity"    "Wind"        "Play"       
CS      0       06:35:10.798    matrix test (US500,D1)  [[0,0,0,0,0]
CS      0       06:35:10.798    matrix test (US500,D1)   [0,0,0,1,0]
CS      0       06:35:10.798    matrix test (US500,D1)   [1,0,0,0,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [2,1,0,0,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [2,2,1,0,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [2,2,1,1,0]
CS      0       06:35:10.798    matrix test (US500,D1)   [1,2,1,1,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [0,1,0,0,0]
CS      0       06:35:10.798    matrix test (US500,D1)   [0,2,1,0,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [2,1,1,0,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [0,1,1,1,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [1,1,0,1,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [1,0,1,0,1]
CS      0       06:35:10.798    matrix test (US500,D1)   [2,1,0,1,0]]


Escrevendo uma Matrix em um arquivo CSV

Esta é outra função importante que pode ajudar a gravar a matriz em um arquivo CSV sem a necessidade de codificar as coisas sempre que uma nova coluna é adicionada à matriz. Ser capaz de escrever de forma flexível em um arquivo CSV tem sido uma luta para muitas pessoas no fórum > Post1, Post2, Post3. Aqui está como fazer isso usando matrizes;
O truque está na concatenação de strings. Os dados em um arquivo CSV são gravados como uma string concatenada com delimitadores, exceto o último elemento da linha
         row = Matrix.Row(i);
         for(ulong j=0, cols =1; j<row.Size(); j++, cols++)
           {
            concstring += (string)NormalizeDouble(row[j],digits) + (cols == Matrix.Cols() ? "" : ",");
           }

No final, isso se parecerá com um arquivo CSV escrito com diferentes colunas manualmente. Como a matriz não pode carregar os valores de string, eu tive que usar uma matriz de strings para nos permitir escrever o cabeçalho neste arquivo csv.

Vamos tentar escrever esta matriz que foi retirada de um arquivo CSV de volta para um novo arquivo CSV.

   string csv_name = "matrix CSV.csv";
   
   Print("\n---> Writing a Matrix to a CSV File > ",csv_name);
   string header[4] = {"S&P500","SMA","RSI","NASDAQ"};
   
   matrix_utils.WriteCsv(csv_name,Matrix,header);

Abaixo está o arquivo CSV recém-escrito;


Legal.


Transformando uma matriz em vetor

Alguém pode se perguntar por que transformar uma matriz em um vetor? Isso é útil em muitas áreas, pois você pode não precisar de uma matriz o tempo todo, então o que esta função faz é converter a Matrix em um vetor, por exemplo, quando você lê a matriz de um arquivo CSV quando deseja fazer um modelo de regressão linear, a variável independente será uma matriz nx1 ou 1xn, ao tentar usar a função de perda como a MSE, você não pode usar a matriz para avaliar o modelo, em vez disso, você usará o vetor dessa variável independente e o vetor dos valores previstos pelo modelo.

Abaixo está o código para isso;

vector CMatrixutils::MatrixToVector(const matrix &mat)
  {
    vector v = {};
    
    if (!v.Assign(mat))
      Print("Failed to turn the matrix to a vector");
    
    return(v);
  }

Uso:

   matrix mat = {
      {1,2,3},
      {4,5,6},
      {7,8,9}
   };

   Print("\nMatrix to play with\n",mat,"\n");
   
//---

   Print("---> Matrix to Vector");
   vector vec = matrix_utils.MatrixToVector(mat);
   Print(vec);

Saída:

2022.12.20 05:39:00.286 matrix test (US500,D1)  ---> Matrix to Vector
2022.12.20 05:39:00.286 matrix test (US500,D1)  [1,2,3,4,5,6,7,8,9]


Transformando um vetor em matriz

Isso é exatamente o oposto da função que nós acabamos de ver acima, mas isso é mais importante do que você pode imaginar. Na maioria das vezes, em modelos de aprendizado de máquina, você precisa realizar operações de multiplicação entre diferentes matrizes. Como as variáveis independentes geralmente são armazenadas em um vetor, pode ser necessário transformá-las em uma matriz nx1 para fazer as operações, essas duas funções opostas são importantes pois elas fazem o nosso código para poucas variáveis ajustando as existentes.

matrix CMatrixutils::VectorToMatrix(const vector &v)
  {   
   matrix mat = {};
   
   if (!mat.Assign(v))
      Print("Failed to turn the vector to a 1xn matrix");
   
   return(mat);
  }

Uso:

   Print("\n---> Vector ",vec," to Matrix");
   mat = matrix_utils.VectorToMatrix(vec); 
   Print(mat);

Saída:

2022.12.20 05:50:05.680 matrix test (US500,D1)  ---> Vector [1,2,3,4,5,6,7,8,9] to Matrix
2022.12.20 05:50:05.680 matrix test (US500,D1)  [[1,2,3,4,5,6,7,8,9]]


Transformando um array em vetor

Quem precisa de muitos arrays dentro de programas de aprendizado de máquina? Arrays são muito úteis, mas são muito sensíveis ao tamanho. Ajustá-los com muita frequência e brincar com eles pode ajudar a plataforma a lançar seus programas fora do gráfico com esses tipos de erro de "array out of range", mesmo que os vetores façam a mesma coisa, mas são um pouco mais flexíveis que os arrays, sem mencionar que eles são orientados a objetos, você pode retornar um vetor de uma função, mas não uma matriz, a menos que queira esses erros e avisos de compilação de código.

vector CMatrixutils::ArrayToVector(const double &Arr[])
  {
   vector v((ulong)ArraySize(Arr));

   for(ulong i=0; i<v.Size(); i++)
      v[i] = Arr[i];

   return (v);
  }

Uso:

   Print("---> Array to vector");
   double Arr[3] = {1,2,3};
   vec = matrix_utils.ArrayToVector(Arr);
   Print(vec);

Saída:

CS      0       05:51:58.853    matrix test (US500,D1)  ---> Array to vector
CS      0       05:51:58.853    matrix test (US500,D1)  [1,2,3]


Vetor para Array

Ainda não podemos abandonar as matrizes, pois pode haver momentos em que o vetor precisa ser convertido em uma matriz que precisa ser conectada a um argumento de função que aceita matrizes, ou podemos precisar fazer alguma classificação de matriz, definir como série, corte e muito mais manipulações que não poderíamos fazer com vetores.

bool CMatrixutils::VectorToArray(const vector &v,double &arr[])
  {
   ArrayResize(arr,(int)v.Size());

   if(ArraySize(arr) == 0)
      return(false);

   for(ulong i=0; i<v.Size(); i++)
      arr[i] = v[i];

   return(true);
  }

Uso:

   Print("---> Vector to Array");
   double new_array[];
   matrix_utils.VectorToArray(vec,new_array);
   ArrayPrint(new_array);

Saída:

CS      0       06:19:14.647    matrix test (US500,D1)  ---> Vector to Array
CS      0       06:19:14.647    matrix test (US500,D1)  1.0 2.0 3.0


Remover a coluna de uma matriz

Só porque carregamos a coluna CSV em uma matriz, isso não significa que precisamos de todas as colunas dela. Ao criar o modelo de aprendizado de máquina supervisionado, precisamos descartar algumas colunas irrelevantes e sem mencionar que podemos precisar descartar a variável de resposta da matriz cheia de variáveis independentes.

Aqui está o código para fazê-lo:

void CMatrixutils::MatrixRemoveCol(matrix &mat, ulong col)
  {
   matrix new_matrix(mat.Rows(),mat.Cols()-1); //Remove the one Column

   for (ulong i=0, new_col=0; i<mat.Cols(); i++) 
     {
        if (i == col)
          continue;
        else
          {
           new_matrix.Col(mat.Col(i),new_col);
           new_col++;
          }    
     }

   mat.Copy(new_matrix);
  }

Não há nada de mágico dentro da função, criamos a nova matriz dimensionada com o mesmo número de linhas da matriz original, mas com uma coluna a menos.

   matrix new_matrix(mat.Rows(),mat.Cols()-1); //Remove the one column
Na nova matriz, nós ignoramos a coluna que não é mais necessária da nossa nova matriz, todo o resto será armazenado e pronto.

No final da função, copiamos esta nova matriz para uma antiga.

   mat.Copy(new_matrix);

Vamos descartar a coluna no índice 1 (segunda coluna) da matriz que acabamos de ler do arquivo CSV

   Print("Col 1 ","Removed from Matrix");
   matrix_utils.MatrixRemoveCol(Matrix,1);
   Print("New Matrix\n",Matrix);

Saída:

CS      0       07:23:59.612    matrix test (EURUSD,H1) Column of index 1 removed new Matrix
CS      0       07:23:59.612    matrix test (EURUSD,H1) [[4173.8,34.8,13067.5]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4179.2,36.6,13094.8]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4182.7,37.5,13108]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4185.8,37.1,13104.3]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4180.8,34.9,13082.2]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4174.6,31.8,13052]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4174.9,33.2,13082.2]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4170.8,32.2,13070.6]
CS      0       07:23:59.612    matrix test (EURUSD,H1)  [4182.2,32.5,13078.8]


Removendo várias colunas da Matrix

Esse foi o caso ao tentar remover uma única coluna, mas existem matrizes maiores que contêm muitas colunas que queremos descartar de uma vez, para conseguir isso, nós precisamos realizar algo complicado para evitar remover as colunas necessárias e também evitar tentando acessar valores que estão fora do intervalo, basicamente para remover as colunas com segurança.

O número/tamanho das colunas da nova matriz precisa ser igual ao número total de colunas subtraídas das colunas que queremos remover. O número de linhas para cada coluna é mantido constante (igual à matriz antiga).

A primeira coisa que temos que fazer é percorrer todas as colunas selecionadas que queremos remover versus todas as colunas disponíveis na matriz antiga, se as colunas devem ser removidas, definiremos todos os valores nessas colunas como zero. Esta é uma operação sábia a ser executada para finalizarmos com uma operação limpa.
   vector zeros(mat.Rows());
   zeros.Fill(0);

   for(ulong i=0; i<size; i++)
      for(ulong j=0; j<mat.Cols(); j++)
        {
         if(cols[i] == j)
            mat.Col(zeros,j);
        }
A última coisa que nós temos a fazer é percorrer a matriz novamente por duas vezes e verificar todas as colunas preenchidas com os valores iguais a zero e remover essas colunas uma a uma.
   vector column_vector;
   for(ulong A=0; A<mat.Cols(); A++)
      for(ulong i=0; i<mat.Cols(); i++)
        {
         column_vector = mat.Col(i);
         if(column_vector.Sum()==0)
            MatrixRemoveCol(mat,i);
        }

Observou os loops duplos? eles são muito importantes porque esta matriz é ajustada em seu tamanho depois que cada coluna é removida, por isso é muito importante voltar novamente para verificar se nós pulamos alguma coluna.

Abaixo está o código completo da função;

void CMatrixutils::MatrixRemoveMultCols(matrix &mat,int &cols[])
  {
   ulong size = (int)ArraySize(cols);

   if(size > 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; i++)
      for(ulong j=0; j<mat.Cols(); j++)
        {
         if(cols[i] == j)
            mat.Col(zeros,j);
        }
//---

   vector column_vector;
   for(ulong A=0; A<mat.Cols(); A++)
      for(ulong i=0; i<mat.Cols(); i++)
        {
         column_vector = mat.Col(i);
         if(column_vector.Sum()==0)
            MatrixRemoveCol(mat,i);
        }
  }

Vamos ver esta função em ação, vamos tentar remover a coluna no índice 0 e no índice 2;

   Print("\nRemoving multiple columns");
   int cols[2] = {0,2};
   
   matrix_utils.MatrixRemoveMultCols(Matrix,cols);
   Print("Columns at 0,2 index removed New Matrix\n",Matrix);

Saída:

CS      0       07:32:10.923    matrix test (EURUSD,H1) Columns at 0,2 index removed New Matrix
CS      0       07:32:10.923    matrix test (EURUSD,H1) [[13386.6,13067.5]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13396.7,13094.8]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13406.6,13108]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13416.8,13104.3]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13425.2,13082.2]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13432.2,13052]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13440.4,13082.2]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13447.6,13070.6]
CS      0       07:32:10.923    matrix test (EURUSD,H1)  [13457.8,13078.8]


Removendo uma linha da matriz

Também pode ser necessário remover uma única linha do conjunto de dados porque ele é irrelevante ou porque se deseja reduzir o número de colunas apenas para ver o que funciona. Isso não é tão importante quanto remover a coluna da matriz, então só temos esta função para remover uma única linha, não veremos aquela para várias linhas, você pode programar por conta própria se quiser.

A mesma ideia foi aplicada ao tentar remover a linha da matriz., nós simplesmente ignoramos a linha indesejada na nova matriz que estamos fazendo.

void CMatrixutils::MatrixRemoveRow(matrix &mat, ulong row)
  {
   matrix new_matrix(mat.Rows()-1,mat.Cols()); //Remove the one Row
      for(ulong i=0, new_rows=0; i<mat.Rows(); i++)
        {
         if(i == row)
            continue;
         else
           {
            new_matrix.Row(mat.Row(i),new_rows);
            new_rows++;
           }
        }
   mat.Copy(new_matrix);
  }

Uso:

   Print("Removing row 1 from matrix");
    
   matrix_utils.MatrixRemoveRow(Matrix,1);
   
   printf("Row %d Removed New Matrix[%d][%d]",0,Matrix.Rows(),Matrix.Cols());
   Print(Matrix); 

Saída:

CS      0       06:36:36.773    matrix test (US500,D1)  Removing row 1 from matrix
CS      0       06:36:36.773    matrix test (US500,D1)  Row 1 Removed New Matrix[743][4]
CS      0       06:36:36.773    matrix test (US500,D1)  [[4173.8,13386.6,34.8,13067.5]
CS      0       06:36:36.773    matrix test (US500,D1)   [4182.7,13406.6,37.5,13108]
CS      0       06:36:36.773    matrix test (US500,D1)   [4185.8,13416.8,37.1,13104.3]
CS      0       06:36:36.773    matrix test (US500,D1)   [4180.8,13425.2,34.9,13082.2]
CS      0       06:36:36.773    matrix test (US500,D1)   [4174.6,13432.2,31.8,13052]
CS      0       06:36:36.773    matrix test (US500,D1)   [4174.9,13440.4,33.2,13082.2]
CS      0       06:36:36.773    matrix test (US500,D1)   [4170.8,13447.6,32.2,13070.6]


Remover o índice do vetor

Este é curto, esta função ajuda a remover um único item em um índice específico de um vetor. Ele é capaz de alcançar tal resultado apenas ignorando o número localizado no índice dado do vetor, o restante dos valores é armazenado no novo vetor que é finalmente copiado para o vetor primário/referenciado do argumento da função.

void CMatrixutils::VectorRemoveIndex(vector &v, ulong index)
  {
   vector new_v(v.Size()-1);

   for(ulong i=0, count = 0; i<v.Size(); i++)
      if(i != index)
        {
         new_v[count] = v[i];
         count++;
        }
    v.Copy(new_v);
  }

Abaixo está como usá-lo:

   vector v= {0,1,2,3,4};
   Print("Vector remove index 3");
   matrix_utils.VectorRemoveIndex(v,3);
   Print(v);

Saída:

2022.12.20 06:40:30.928 matrix test (US500,D1)  Vector remove index 3
2022.12.20 06:40:30.928 matrix test (US500,D1)  [0,1,2,4]


Divisão da matriz em matrizes de treinamento e teste

Eu não posso enfatizar a importância dessa função ao criar um modelo de aprendizado de máquina supervisionado. Muitas vezes, precisamos dividir o conjunto de dados em conjuntos de dados de treinamento e teste para que possamos treinar o conjunto de dados em algum conjunto de dados e testá-lo em outro conjunto de dados que o modelo nunca viu antes.

Por padrão, essa função divide 70% do conjunto de dados na amostra de treinamento e os 30% restantes na amostra de teste. Não há estado aleatório. Os dados são selecionados na ordem cronológica da matriz, os primeiros 70% dos dados são armazenados na matriz de treinamento, enquanto os 30% restantes são armazenados na matriz de teste.

   Print("---> Train / Test Split");
   matrix TrainMatrix, TestMatrix;
   matrix_utils.TrainTestSplitMatrices(Matrix,TrainMatrix,TestMatrix);

   Print("\nTrain Matrix(",TrainMatrix.Rows(),",",TrainMatrix.Cols(),")\n",TrainMatrix);
   Print("\nTestMatrix(",TestMatrix.Rows(),",",TestMatrix.Cols(),")\n",TestMatrix);

Saída:

CS      0       07:38:46.011    matrix test (EURUSD,H1) Train Matrix(521,4)
CS      0       07:38:46.011    matrix test (EURUSD,H1) [[4173.8,13386.6,34.8,13067.5]
CS      0       07:38:46.011    matrix test (EURUSD,H1)  [4179.2,13396.7,36.6,13094.8]
CS      0       07:38:46.011    matrix test (EURUSD,H1)  [4182.7,13406.6,37.5,13108]
CS      0       07:38:46.011    matrix test (EURUSD,H1)  [4185.8,13416.8,37.1,13104.3]
CS      0       07:38:46.011    matrix test (EURUSD,H1)  [4180.8,13425.2,34.9,13082.2]
....
....

CS      0       07:38:46.012    matrix test (EURUSD,H1) TestMatrix(223,4)
CS      0       07:38:46.012    matrix test (EURUSD,H1) [[4578.1,14797.9,65.90000000000001,15021.1]
CS      0       07:38:46.012    matrix test (EURUSD,H1)  [4574.9,14789.2,63.9,15006.2]
CS      0       07:38:46.012    matrix test (EURUSD,H1)  [4573.6,14781.4,63,14999]
CS      0       07:38:46.012    matrix test (EURUSD,H1)  [4571.4,14773.9,62.1,14992.6]
CS      0       07:38:46.012    matrix test (EURUSD,H1)  [4572.8,14766.3,65.2,15007.1]


Divisão das matrizes em X e Y

Ao tentar criar modelos de aprendizado supervisionado depois de carregarmos o conjunto de dados em uma matriz, a próxima coisa que precisamos é extrair as variáveis de destino da matriz e armazená-las independentemente da mesma forma que queremos que as variáveis independentes sejam armazenadas em sua matriz de valores, esta é a função para fazer isso:
Se nenhuma coluna for selecionada, a última coluna da matriz será considerada a das variáveis independentes.
   if(y_index == -1)
      value = matrix_.Cols()-1;  //Last column in the matrix

Uso:

   Print("---> X and Y split matrices");
   
   matrix x;
   vector y;
   
   matrix_utils.XandYSplitMatrices(Matrix,x,y);
   
   Print("independent vars\n",x);
   Print("Target variables\n",y);

Saída:

CS      0       05:05:03.467    matrix test (US500,D1)  ---> X and Y split matrices
CS      0       05:05:03.467    matrix test (US500,D1)  independent vars
CS      0       05:05:03.468    matrix test (US500,D1)  [[4173.8,13386.6,34.8]
CS      0       05:05:03.468    matrix test (US500,D1)   [4179.2,13396.7,36.6]
CS      0       05:05:03.468    matrix test (US500,D1)   [4182.7,13406.6,37.5]
CS      0       05:05:03.468    matrix test (US500,D1)   [4185.8,13416.8,37.1]
CS      0       05:05:03.468    matrix test (US500,D1)   [4180.8,13425.2,34.9]
CS      0       05:05:03.468    matrix test (US500,D1)   [4174.6,13432.2,31.8]
CS      0       05:05:03.468    matrix test (US500,D1)   [4174.9,13440.4,33.2]
CS      0       05:05:03.468    matrix test (US500,D1)   [4170.8,13447.6,32.2]
CS      0       05:05:03.468    matrix test (US500,D1)   [4182.2,13457.8,32.5]
....
....
2022.12.20 05:05:03.470 matrix test (US500,D1)  Target variables
2022.12.20 05:05:03.470 matrix test (US500,D1)  [13067.5,13094.8,13108,13104.3,13082.2,13052,13082.2,13070.6,13078.8,...]

Dentro da função de divisão em X e Y, a coluna selecionada como variáveis y é armazenada na matriz Y enquanto o restante das colunas restantes é armazenada na matriz X.

void CMatrixutils::XandYSplitMatrices(const matrix &matrix_,matrix &xmatrix,vector &y_vector,int y_index=-1)
  {
   ulong value = y_index;
   if(y_index == -1)
      value = matrix_.Cols()-1;  //Last column in the matrix
//---
   y_vector = matrix_.Col(value);
   xmatrix.Copy(matrix_);

   MatrixRemoveCol(xmatrix, value); //Remove the y column
  }


Fazendo um modelo da matriz

A matriz modelo é essencial para o algoritmo dos mínimos quadrados, ele é apenas a matriz x (matriz de variáveis independentes) com a primeira coluna preenchida com os valores iguais a um. Esta matriz modelo nos ajuda a obter os coeficientes para o modelo de regressão linear. Dentro da função DesignMatrix() o vetor preenchido com valores iguais a um é criado, ele é inserido na primeira coluna da matriz, enquanto o restante das colunas é definido para seguir a ordem;
Código completo:

matrix CMatrixutils::DesignMatrix(matrix &x_matrix)
  {
   matrix out_matrix(x_matrix.Rows(),x_matrix.Cols()+1);
   vector ones(x_matrix.Rows());
   ones.Fill(1);
   out_matrix.Col(ones,0);
   vector new_vector;
   for(ulong i=1; i<out_matrix.Cols(); i++)
     {
      new_vector = x_matrix.Col(i-1);
      out_matrix.Col(new_vector,i);
     }
   return (out_matrix);
  }

Uso:

   Print("---> Design Matrix\n");
   matrix design = matrix_utils.DesignMatrix(x);
   Print(design);

Saída:

CS      0       05:28:51.786    matrix test (US500,D1)  ---> Design Matrix
CS      0       05:28:51.786    matrix test (US500,D1)  
CS      0       05:28:51.786    matrix test (US500,D1)  [[1,4173.8,13386.6,34.8]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4179.2,13396.7,36.6]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4182.7,13406.6,37.5]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4185.8,13416.8,37.1]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4180.8,13425.2,34.9]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4174.6,13432.2,31.8]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4174.9,13440.4,33.2]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4170.8,13447.6,32.2]
CS      0       05:28:51.786    matrix test (US500,D1)   [1,4182.2,13457.8,32.5]


Matriz One hot encoding

Vamos fazer uma One hot encoding. Esta é uma função muito importante que você precisará ao tentar fazer as redes neurais de classificação. Esta função se mostra de grande importância, pois ela deixa claro para a MLP para relacionar as saídas do neurônio na última camada com os valores reais.

Em uma matriz codificada a quente, o vetor para cada linha consiste em valores iguais a zero, exceto por um valor que é o valor correto que queremos atingir, que terá o valor igual a 1.

Para entender bem vamos ver o exemplo abaixo:

suponha que este seja o conjunto de dados que queremos treinar a rede neural, para poder classificar entre as três classes, Cachorro, Gato e Rato, com base na altura das pernas e no diâmetro do corpo.

O perceptron multicamadas terá 2 nós/neurônios de entrada, um para a altura das pernas e outro para o diâmetro do corpo na camada de entrada, enquanto a camada de saída terá 3 nós representando os 3 resultados Cachorro, Gato e Camundongos.

Agora digamos que alimentamos esta MLP com o valor de 12 e 20 para altura e diâmetro respectivamente, nós esperamos que a rede neural classifique isso como um cachorro, certo? o que a One hot encoding faz é colocar o valor igual a um em um nó que tem o valor correto para o conjunto de dados de treinamento fornecido, neste caso, no nó de um cachorro, o valor de 1 será colocado e o restante será carregado com os valores iguais a zero. 

Como o restante dos valores tem zeros, podemos calcular a função de custo substituindo os valores de um vetor one hot encoding por cada uma das probabilidades que o modelo nos forneceu, esse erro será propagado de volta à rede em seus respectivos nós anteriores de uma camada anterior.

Ok, agora vamos ver o One hot Encoding em ação.

Esta é a nossa matriz no MetaEditor;

   matrix dataset = {
                     {12,28, 1},
                     {11,14, 1},
                     {7,8, 2},
                     {2,4, 3}
                    };

Este é o mesmo conjunto de dados que você viu em um CSV, exceto que o destino é rotulado em números inteiros, pois não podemos ter strings em uma matriz, a propósito, quando e onde você pode usar strings em cálculos? 

   matrix dataset = {
                     {12,28, 1},
                     {11,14, 1},
                     {7,8, 2},
                     {2,4, 3}
                    };
 
   vector y_vector = dataset.Col(2); //obtain the column to encode
   uint classes = 3; //number of classes in the column
   Print("One Hot encoded matrix \n",matrix_utils.OneHotEncoding(y_vector,classes));   

Abaixo está a saída:

CS      0       08:54:04.744    matrix test (EURUSD,H1) One Hot encoded matrix 
CS      0       08:54:04.744    matrix test (EURUSD,H1) [[1,0,0]
CS      0       08:54:04.744    matrix test (EURUSD,H1)  [1,0,0]
CS      0       08:54:04.744    matrix test (EURUSD,H1)  [0,1,0]
CS      0       08:54:04.744    matrix test (EURUSD,H1)  [0,0,1]]
                


Obtendo as classes de um vetor

Esta função retorna o vetor contendo as classes disponíveis no vetor dado, por exemplo; um vetor com [1,2,3] tem 3 classes diferentes. Um vetor de [1,0,1,0] tem duas classes diferentes. O papel da função é retornar as classes distintas disponíveis.

   vector v = {1,0,0,0,1,0,1,0};
   
   Print("classes ",matrix_utils.Classes(v));

Saída:

2022.12.27 06:41:54.758 matrix test (US500,D1)  classes [1,0]


Criando um vetor de valores aleatórios.

Existem diversas vezes que precisamos gerar um vetor cheio de variáveis aleatórias, essa função pode ajudar. O uso mais comum dessa função é gerar pesos e vetores de pesos e viés para a NN. Você também pode usá-lo para criar amostras de conjuntos de dados aleatórios.

   vector            Random(int min, int max, int size);
   vector            Random(double min, double max, int size);

Uso:

   v = matrix_utils.Random(1.0,2.0,10);
   Print("v ",v);

Saída:

2022.12.27 06:50:03.055 matrix test (US500,D1)  v [1.717642750328074,1.276955473494675,1.346263008514664,1.32959990234077,1.006469924008911,1.992980742820521,1.788445692312387,1.218909268471328,1.732139042329173,1.512405774101993]


Anexando um vetor a outro.

Isso é algo como a concatenação de strings, mas, em vez disso, concatenamos vetores. Eu me vi querendo usar essa função em muitos casos. Abaixo está o que foi feito,

vector CMatrixutils::Append(vector &v1, vector &v2)
 {
   vector v_out = v1;   
   v_out.Resize(v1.Size()+v2.Size());   
   for (ulong i=v2.Size(),i2=0; i<v_out.Size(); i++, i2++)
      v_out[i] = v2[i2];   
   return (v_out); 
 }

Uso:

   vector v1 = {1,2,3}, v2 = {4,5,6};
   
   Print("Vector 1 & 2 ",matrix_utils.Append(v1,v2));

Saída:

2022.12.27 06:59:25.252 matrix test (US500,D1)  Vector 1 & 2 [1,2,3,4,5,6]


Copiando um vetor para outro

Por último, mas não menos importante, vamos copiar um vetor para outro vetor, manipulando o vetor como a Arraycopy faz, geralmente não precisamos copiar o vetor inteiro para o outro, muitas vezes nós precisamos de apenas um pedaço dele, o método Copy fornecido pela biblioteca padrão falha em uma situação como esta.

bool CMatrixutils::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("Can't copy a vector | Size %d total %d src_start %d ",src.Size(),total,src_start);
       return (false);
    }  
   dst.Resize(total);
   dst.Fill(0);  
   for (ulong i=src_start, index =0; i<total+src_start; i++)
      {   
          dst[index] = src[i];         
          index++;
      }
   return (true);
 }

Uso:

   vector all = {1,2,3,4,5,6};   
   matrix_utils.Copy(all,v,3);
   Print("copied vector ",v);   

Saída:

2022.12.27 07:15:41.420 matrix test (US500,D1)  copied vector [4,5,6]


Pensamentos finais

Há muito que nós podemos fazer e muito mais funções que podemos adicionar ao arquivo utils, mas as funções e métodos apresentados neste artigo são os que eu acho que serão usados com maior frequência se estiver você usando matrizes há algum tempo e ao tentar criar sistemas de negociação complexos, como aquele inspirado no aprendizado de máquina.

Acompanhe o desenvolvimento desta da classe Utility Matrix no meu repositório do GitHub > https://github.com/MegaJoctan/MALE5


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/11858

Arquivos anexados |
MQL5.zip (13.63 KB)
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 02): Primeiros experimentos (II) Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 02): Primeiros experimentos (II)
Vamos experimentar uma outra abordagem, desta vez tentando alcançar o objetivo de 1 minuto. Mas isto não é uma tarefa tão simples, como muitos pensam.
Indicadores não-lineares Indicadores não-lineares
Neste artigo, vamos considerar algumas formas de construir indicadores não-lineares e seu uso na negociação. Existem alguns indicadores disponíveis na plataforma de negociação MetaTrader que utilizam abordagens não-lineares.
Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 03):  Ajustando as coisas (I) Desenvolvendo um sistema de Replay — Simulação de mercado (Parte 03): Ajustando as coisas (I)
Vamos dar uma ajeitada nas coisas, pois este começo não está sendo um dos melhores. Se não fizermos isto agora, vamos ter problemas logo, logo.
Teoria das Categorias em MQL5 (Parte 1) Teoria das Categorias em MQL5 (Parte 1)
A Teoria das Categorias é um ramo diverso da Matemática e em expansão, sendo uma área relativamente recente na comunidade MQL. Esta série de artigos visa introduzir e examinar alguns de seus conceitos com o objetivo geral de estabelecer uma biblioteca aberta que atraia comentários e discussões enquanto esperamos promover o uso deste campo notável no desenvolvimento da estratégia dos traders.