English Русский 中文 Español 日本語 Português
preview
Matrix Utils, Erweiterung der Funktionalität der Standardbibliothek für Matrizen und Vektoren

Matrix Utils, Erweiterung der Funktionalität der Standardbibliothek für Matrizen und Vektoren

MetaTrader 5Statistik und Analyse | 14 Februar 2023, 09:28
162 0
Omega J Msigwa
Omega J Msigwa

„Du wirst nie Perfektion erreichen, denn es gibt immer Raum für Verbesserungen.

Doch auf dem Weg zur Perfektion wirst du lernen, besser zu werden.“

Einführung

In Python ist die Klasse Utils eine Art Allzweckwaffe mit Funktionen und Codezeilen, die wir wiederverwenden können, ohne die Instanz einer Klasse zu erstellen.

Die Standardbibliothek für Matrizen bietet uns einige sehr wichtige Funktionen und Methoden, die wir zum Initialisieren, Transformieren, Manipulieren von Matrizen und vielem mehr verwenden können. Aber wie jede andere Bibliothek, die jemals gebaut wurde, kann sie erweitert werden, um zusätzliche Funktionen auszuführen, die in einigen Anwendungen notwendig sein könnten.


Die im Folgenden vorgestellten Funktionen und Methoden sind in keiner bestimmten Reihenfolge aufgeführt;

Inhalt:


Einlesen der Matrix aus einer CSV-Datei

Dies ist eine sehr wichtige Funktion, da sie es uns ermöglicht, Datenbanken aus einer CSV-Datei zu laden und sie mühelos in einer Matrix zu speichern. Ich brauche die Bedeutung dieser Funktion nicht zu betonen, denn bei der Programmierung von Handelssystemen müssen wir oft CSV-Dateien mit Handelssignalen oder Handelsverläufen laden, auf die unser Programm zugreifen soll.

Der erste Schritt in der Funktion (Sie haben es erraten?) ist das Lesen einer CSV-Datei. Wir gehen davon aus, dass die erste Zeile der CSV-Datei nur Zeichenketten enthält, sodass, selbst wenn Werte in der ersten Zeile stehen, sie nicht in der Matrix gehören, da die Matrix keine String-Werte enthalten kann. Die Werte der ersten Zeile werden stattdessen im Array csv_header gespeichert.
      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++;
         //---

Dieses Array dient dazu, die Spaltennamen aus der soeben eingelesenen CSV-Datei zu speichern.

Vollständiger Code:

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_);
  }

Verwendung:

Hier ist die CSV-Datei, die wir in eine Matrix laden wollen:

Laden der CSV-Datei in eine Matrix:

   Print("---> Reading a CSV file to Matrix\n");
   Matrix = matrix_utils.ReadCsv("NASDAQ_DATA.csv"); 
   
   ArrayPrint(matrix_utils.csv_header);
   Print(Matrix);
Ausgabe:
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]

Der Grund, warum ich mich entschlossen habe, diese Funktion zu erstellen, ist, dass ich keine Möglichkeit gefunden habe, eine CSV-Datei aus der Standardbibliothek zu lesen. Ich habe erwartet, dass dies der Weg nach vorne ist, um die Werte aus einer CSV-Datei in eine Matrix mit der Funktion fromfile() zu laden

wurde aber enttäuscht. Wenn jemand weiß, wo in der Dokumentation das beschrieben ist, lassen Sie mich im Diskussionsabschnitt dieses Artikels wissen.

Kodierte Werte aus CSV-Datei lesen

Oft müssen Sie eine CSV-Datei lesen, die Werte in Form von Text enthält. Diese Art von CSV-Dateien, die Textwerte enthalten, sind häufig anzutreffen, wenn es um Klassifizierungsalgorithmen geht. Werfen wir einen Blick auf den beliebtesten Datensatz mit Wetterdaten.

Wetterdatensatz

Um diese CSV-Datei kodieren zu können, werden wir alle Daten in ein einzelnes Array von Textwerten lesen, da Matrizen mit Textwerte nicht umgehen können. Innerhalb der Funktion ReadCsvEncode() ist das erste, was wir tun müssen, die Spalten in der gegebenen CSV-Datei zu lesen, zweitens gehen wir in einer Schleife durch alle Spalten, wobei in jeder Iteration eine CSV-Datei geöffnet wird und eine einzelne Spalte vollständig gelesen wird und die Werte dieser Spalte in das Array aller Werte namens toArr[] gespeichert werden.

//--- 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); 
     }
//---

Sobald dies geschehen ist, sieht toArr[] ausgedruckt wie folgt aus;

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"      

Wenn Sie auf dieses Array achten, werden Sie feststellen, dass die Werte aus einer CSV-Datei in der Reihenfolge stehen, in der sie aus der CSV-Datei entnommen wurden, also von Index 0 bis 13 die erste Spalte, von Index 14 bis 27 die zweite Spalte usw. usw. Von dort aus ist es nun ein Leichtes, die Werte zu extrahieren und zu kodieren, um sie schließlich in einer Matrix zu speichern.

    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);

Die Funktion LabelEncoder tut genau das, was der Name sagt: Sie fügt den Merkmalen, die im Spaltenarray ähnlich sind, die gleiche Bezeichnung zu.

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;
 }

Diese Funktion ist einfach: Sie vergleicht die gegebenen Klassen mit dem gesamten Array, um ähnliche Merkmale zu finden und sie zu kennzeichnen. Der Großteil der Arbeit wird innerhalb der schwarz markierten Funktion Classes() erledigt.

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;
        }
     }
 }

Diese Funktion ermittelt die im angegebenen Array vorhandenen Klassen und übergibt sie an das referenzierte Array classes_arr[]. Diese Funktion hat stattdessen eine Variante für die Suche nach Klassen in einem Vektor. Weitere Einzelheiten zu dieser Funktion finden Sie hier.

vector CMatrixutils::Classes(vector &v)

Im Folgenden wird beschrieben, wie Sie diese Funktion verwenden:

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

Ausgabe:

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]]


Schreiben einer Matrix in eine CSV-Datei

Dies ist eine weitere wichtige Funktion, die dabei helfen kann, die Matrix in eine CSV-Datei zu schreiben, ohne dass jedes Mal, wenn eine neue Spalte zur Matrix hinzugefügt wird, das fest einprogrammiert werden muss. Die Möglichkeit, auf flexible Weise in eine CSV-Datei zu schreiben, war für viele Leute im Forum ein Problem, siehe Post1, Post2, Post3. Hier sehen Sie, wie man das mit Matrizen macht;
Der Trick liegt in der Verkettung von Zeichenketten. Die Daten in einer CSV-Datei werden als eine mit Begrenzungszeichen verkettete Zeichenkette geschrieben, mit Ausnahme des letzten Elements in der Zeile:
         row = Matrix.Row(i);
         for(ulong j=0, cols =1; j<row.Size(); j++, cols++)
           {
            concstring += (string)NormalizeDouble(row[j],digits) + (cols == Matrix.Cols() ? "" : ",");
           }

Dies sieht dann genauso aus wie eine CSV-Datei, die manuell mit verschiedenen Spalten geschrieben wurde. Da die Matrix keine Textwerte enthalten kann, musste ich ein Array von Zeichenketten verwenden, um die Kopfzeile in diese CSV-Datei zu schreiben.

Versuchen wir, diese Matrix, die aus einer CSV-Datei erstellt wurde, wieder in eine neue CSV-Datei zu schreiben.

   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);

Nachfolgend sehen Sie die neu geschriebene CSV-Datei:


Cool.


Matrix in einen Vektor umwandeln

Manch einer mag sich fragen, warum eine Matrix in einen Vektor umwandeln? Dies ist in vielen Bereichen nützlich, da man nicht immer eine Matrix benötigt. Diese Funktion konvertiert die Matrix in einen Vektor, z. B. wenn man die Matrix aus einer CSV-Datei liest, um das Modell einer Linearen Regression zu erstellen, wird die unabhängige Variable eine nx1- oder 1xn-Matrix sein. Wenn man versucht, die Verlustfunktion wie z. B. den MSE zu verwenden, kann man die Matrix nicht verwenden, um das Modell zu bewerten, sondern man verwendet den Vektor dieser unabhängigen Variablen und den Vektor der vom Modell vorhergesagten Werte.

Hier ist das Code dafür:

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

Verwendung:

   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);

Ausgabe:

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]


Vektor in Matrix umwandeln

Dies ist das genaue Gegenteil der Funktion, die wir gerade oben gesehen haben, und doch ist sie wichtiger, als Sie sich vorstellen können. Da die unabhängigen Variablen oft in einem Vektor gespeichert sind, müssen Sie sie möglicherweise in eine nx1-Matrix umwandeln, um die Operationen durchzuführen. Diese beiden entgegengesetzten Funktionen sind wichtig, da sie unseren Code für weniger Variablen machen, indem sie die vorhandenen Variablen optimieren.

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

Verwendung:

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

Ausgabe:

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]]


Array in Vektor umwandeln

Wer braucht schon ein großes Array in einem maschinellen Lernprogrammen? Arrays sind sehr nützlich, aber sie sind sehr empfindlich, wenn es um ihre Größe geht. Sie sehr oft anzupassen und mit ihnen zu spielen, könnte dem Terminal helfen, seine Programme vom Chart mit diesen Arrays außerhalb des Bereichs Art von Fehlern zu löschen, obwohl Vektoren die gleiche Sache erledigen würden, denn sie sind ein bisschen flexibler als Arrays, ganz zu schweigen davon, dass sie objektorientiert sind. Es kann ein Vektor aus einer Funktion zurückgeben werden, aber nicht ein Array, es sei denn, Sie wollen Code-Kompilierungsfehler und Warnungen erzeugen.

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);
  }

Verwendung:

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

Ausgabe:

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]


Vektor zu Array

Wir können jedoch nicht immer auf Arrays verzichten, da es manchmal vorkommen kann, dass der Vektor in ein Array umgewandelt werden muss, das in ein Funktionsargument eingefügt werden muss, das Arrays annimmt, oder wir müssen vielleicht eine Array-Sortierung, ein Set-As-Series, ein Slicing und viele andere Manipulationen durchführen, die wir mit Vektoren nicht machen können.

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);
  }

Verwendung:

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

Ausgabe:

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


Matrix Spalte entfernen

Nur weil wir die CSV-Spalte in eine Matrix geladen haben, heißt das nicht, dass wir alle Spalten daraus benötigen. Bei der Erstellung eines überwachten Modells für maschinelles Lernen müssen wir einige irrelevante Spalten weglassen, ganz zu schweigen davon, dass wir möglicherweise die Antwortvariable aus der Matrix voller unabhängiger Variablen entfernen müssen.

Hier ist der entsprechende Code:

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);
  }

Innerhalb der Funktion gibt es nichts Magisches, wir erstellen die neue Matrix mit der gleichen Anzahl von Zeilen wie die ursprüngliche Matrix, aber mit einer Spalte weniger.

   matrix new_matrix(mat.Rows(),mat.Cols()-1); //Remove the one column
In der neuen Matrix ignorieren wir die nicht mehr benötigte Spalte aus unserer neuen Matrix, alles andere wird gespeichert, und das war's.

Am Ende der Funktion kopieren wir diese neue Matrix in eine alte Matrix.

   mat.Copy(new_matrix);

Löschen wir die Spalte mit dem Index 1 (zweite Spalte) aus der Matrix, die wir gerade aus der CSV-Datei gelesen haben:

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

Ausgabe:

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]


Mehrere Spalten aus der Matrix entfernen

Das war der Fall, als wir versuchten, eine einzelne Spalte zu entfernen, aber es gibt größere Matrizen, die viele Spalten enthalten, die wir alle auf einmal löschen wollen. Um dies zu erreichen, müssen wir etwas Kniffliges tun, um zu vermeiden, dass die benötigten Spalten entfernt werden, und um auch zu vermeiden, dass versucht wird, auf Werte zuzugreifen, die außerhalb des Bereichs liegen, um die Spalten im Grunde sicher zu entfernen.

Die Anzahl/Größe der Spalten der neuen Matrix muss der Gesamtzahl der Spalten abzüglich der zu entfernenden Spalten entsprechen. Die Anzahl der Zeilen für jede Spalte wird konstant gehalten (wie bei der alten Matrix).

Als erstes müssen wir alle ausgewählten Spalten, die wir entfernen wollen, mit allen verfügbaren Spalten in der alten Matrix vergleichen. Dies ist ein kluger Vorgang, um einen sauberen Vorgang zu erhalten.
   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);
        }
Als Letztes müssen wir die Matrix noch zweimal in einer Schleife durchlaufen, um alle Spalten mit Nullwerten zu finden und diese Spalten nacheinander zu entfernen.
   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);
        }

Sehen Sie die beide Schleifen? Sie sind sehr wichtig, da die Größe der Matrix nach dem Entfernen jeder Spalte angepasst wird, sodass es sehr wichtig ist, wieder eine Schleife zu machen, um zu prüfen, ob wir eine Spalte übersprungen haben.

Nachstehend finden Sie den vollständigen Code für die Funktion;

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);
        }
  }

Schauen wir uns diese Funktion in Aktion an. Versuchen wir, die Spalte bei Index 0 und bei Index 2 zu entfernen;

   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);

Ausgabe:

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]


Eine Zeile aus der Matrix entfernen

Es kann auch notwendig sein, eine einzelne Zeile aus dem Datensatz zu entfernen, weil sie irrelevant ist oder weil man die Anzahl der Spalten reduzieren möchte, um zu sehen, was funktioniert. Dies ist nicht so wichtig wie das Entfernen der Spalte aus der Matrix, daher haben wir diese Funktion nur zum Entfernen einer einzelnen Zeile, wir werden die Funktion für mehrere Zeilen nicht sehen, Sie können sie selbst programmieren, wenn Sie das möchten.

Die gleiche Idee wurde beim Versuch, die Zeile aus der Matrix zu entfernen, angewandt. Wir ignorieren einfach die unerwünschte Zeile in der neuen Matrix, die wir erstellen:

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);
  }

Verwendung:

   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); 

Ausgabe:

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]


Indizierten Einzelwert aus einem Vektor entfernen

Diese Funktion hilft dabei, ein einzelnes Element mit einem bestimmten Index aus einem Vektor zu entfernen. Sie erreicht das Ziel, indem nur die Zahl am angegebenen Index aus dem Vektor ignoriert wird, der Rest der Werte wird in den neuen Vektor gespeichert, der schließlich in den primären/referenzierten Vektor aus dem Funktionsargument kopiert wird.

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);
  }

Nachfolgend wird beschrieben, wie sie zu verwenden ist:

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

Ausgabe:

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]


Aufteilung der Matrix in Trainings- und Testmatrizen

Ich kann gar nicht genug betonen, wie wichtig diese Funktion bei der Erstellung eines überwachten maschinellen Lernmodells ist. Oft müssen wir den Datensatz in einen Trainings- und einen Testdatensatz aufteilen, damit wir den Datensatz auf einem Datensatz trainieren und ihn auf einem anderen Datensatz testen können, den das Modell noch nie gesehen hat.

Standardmäßig teilt diese Funktion 70 % des Datensatzes in die Trainingsstichprobe und die restlichen 30 % in die Teststichprobe auf, es gibt keinen Zufallsstatus. Die Daten werden in der chronologischen Reihenfolge der Matrix ausgewählt. Die ersten 70% der Daten werden in der Trainingsmatrix gespeichert, während die restlichen 30% in der Testmatrix gespeichert werden.

   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);

Ausgabe:

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]


X- und Y-Splitmatrizen

Wenn wir versuchen, überwachte Lernmodelle zu erstellen, nachdem wir den Datensatz in eine Matrix geladen haben, müssen wir als Nächstes die Zielvariablen aus der Matrix extrahieren und sie unabhängig speichern, so wie wir die unabhängigen Variablen in ihrer Wertematrix speichern wollen:
Wenn keine Spalte ausgewählt wird, wird die letzte Spalte der Matrix als die der unabhängigen Variablen angenommen.
   if(y_index == -1)
      value = matrix_.Cols()-1;  //Last column in the matrix

Verwendung:

   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);

Ausgabe:

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,...]

Innerhalb der X- und Y-Splitfunktion wird die ausgewählte Spalte als Y-Variable in der Y-Matrix gespeichert, während die übrigen Spalten in der X-Matrix gespeichert werden.

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
  }


Erstellen einer Design-Matrix

Die Design-Matrix ist für den Algorithmus der kleinsten Quadrate von entscheidender Bedeutung. Es handelt sich dabei um die x-Matrix(Matrix der unabhängigen Variablen), deren erste Spalte mit den Werten von eins gefüllt ist. Anhand dieser Gestaltungsmatrix lassen sich die Koeffizienten für das lineare Regressionsmodell ermitteln. In der Funktion DesignMatrix() wird der Vektor mit den Werten von eins erstellt und in die erste Spalte der Matrix eingefügt, während die übrigen Spalten in der vorgegebenen Reihenfolge angeordnet werden;
Vollständiger Code:

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);
  }

Verwendung:

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

Ausgabe:

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]


Eine Hot Encoding Matrix

Wenden wir uns dem ‚Hot Encoding‘ zu. Dies ist eine sehr wichtige Funktion, die Sie benötigen, wenn Sie versuchen, neuronale Netze zur Klassifizierung zu erstellen. Diese Funktion erweist sich als sehr wichtig, da sie es dem MLP ermöglicht, die Ausgaben des Neurons in der letzten Schicht mit den tatsächlichen Werten in Beziehung zu setzen.

In einer Hot Encoding Matrix besteht der Vektor für jede Zeile aus Nullwerten mit Ausnahme eines Wertes, der für den richtigen Wert steht, den wir treffen wollen und der den Wert 1 hat.

Um dies besser zu verstehen, sehen wir uns das folgende Beispiel an:

Nehmen wir an, dies ist der Datensatz, mit dem wir das neuronale Netz trainieren wollen, damit es in der Lage ist, zwischen den drei Klassen Hund, Katze und Maus auf der Grundlage der Höhe ihrer Beine und ihres Körperdurchmessers zu unterscheiden.

Das mehrschichtige Perzeptron hat 2 Eingabeknoten/Neuronen, einen für die Höhe der Beine und den anderen für den Körperdurchmesser auf der Eingabeschicht, während die Ausgabeschicht 3 Knoten hat, die die 3 Ergebnisse Hund, Katze und Mäuse darstellen.

Angenommen, wir füttern dieses MLP mit den Werten 12 und 20 für die Höhe bzw. den Durchmesser, dann erwarten wir, dass das neuronale Netz dies als einen Hund klassifiziert, richtig? Die One-Hot-Kodierung setzt den Wert 1 in den Knoten, der den korrekten Wert für den gegebenen Trainingsdatensatz hat, in diesem Fall in den Knoten für einen Hund, der den Wert 1 bekommt, und der Rest erhält den Wert 0. 

Da der Rest der Werte Nullen sind, können wir die Kostenfunktion berechnen, indem wir die Werte eines Hot Encoding Vektors mit jeder der Wahrscheinlichkeiten, die uns das Modell geliefert hat, ersetzen.

Ok, jetzt wollen wir One-Hot-Encoding in Aktion sehen.

Dies sei unsere Matrix in MetaEditor;

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

Dies ist derselbe Datensatz wie der, den wir in einer CSV-Datei gesehen haben, mit der Ausnahme, dass das Ziel in ganzen Zahlen gekennzeichnet ist, da wir keine Zeichenketten in einer Matrix haben können, übrigens, wann und wo kann man Zeichenketten in Berechnungen verwenden? 

   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));   

Nachfolgend die Ausgabe:

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]]
                


Ableitung der Klassen aus einem Vektor

Diese Funktion gibt den Vektor zurück, der die im gegebenen Vektor vorhandenen Klassen enthält, zum Beispiel: Ein Vektor mit [1,2,3] hat 3 verschiedene Klassen, ein Vektor mit [1,0,1,0] hat zwei verschiedene Klassen. Die Funktion hat die Aufgabe, die verfügbaren unterschiedlichen Klassen zurückzugeben.

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

Ausgabe:

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


Erstellen eines Vektors mit Zufallswerten.

Es gibt mehrere Fälle, in denen wir einen Vektor voller Zufallsvariablen erzeugen müssen. Diese Funktion kann dabei helfen. Die häufigste Anwendung dieser Funktion ist die Erzeugung von Gewichten und Bias-Vektoren für NN. Sie können es auch verwenden, um den Datensatz einer Stichprobe mit zufälligen Werte zu machen.

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

Verwendung:

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

Ausgabe:

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]


Anhängen eines Vektors an einen anderen.

Dies ist so etwas wie Verkettung, aber wir verketten stattdessen Vektoren. Ich habe mich dabei ertappt, dass ich diese Funktion in vielen Fällen verwenden möchte. Nachfolgend sehen Sie, woraus sie besteht,

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); 
 }

Verwendung:

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

Ausgabe:

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


Kopieren eines Vektors in einen anderen

Zu guter Letzt können wir einen Vektor in einen anderen Vektor kopieren, indem wir den Vektor genau wie bei Arraycopy manipulieren. Oftmals müssen wir nicht den gesamten Vektor in einen anderen kopieren, sondern nur einen Teil davon.

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);
 }

Verwendung:

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

Ausgabe:

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


Abschließende Überlegungen

Es gibt eine Menge, was wir tun können, und noch viel mehr Funktionen, die wir der utils-Datei hinzufügen können, aber die Funktionen und Methoden, die in diesem Artikel vorgestellt werden, sind diejenigen, von denen ich denke, dass Sie sie oft brauchen werden, wenn Sie Matrizen für eine Weile verwendet haben und wenn Sie versuchen, komplexe Handelssysteme zu erstellen, wie die, die vom maschinellen Lernen inspiriert sind.

Verfolgen Sie die Entwicklung dieser Utility Matrix Klasse auf meinem GitHub Repo > https://github.com/MegaJoctan/MALE5


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/11858

Beigefügte Dateien |
MQL5.zip (13.63 KB)
Algorithmen zur Optimierung mit Populationen Firefly-Algorithmus (FA) Algorithmen zur Optimierung mit Populationen Firefly-Algorithmus (FA)
In diesem Artikel werde ich die Optimierungsmethode des Firefly-Algorithmus (FA) betrachten. Dank der Änderung hat sich der Algorithmus von einem Außenseiter zu einem echten Tabellenführer entwickelt.
Algorithmen zur Optimierung mit Populationen Fish School Search (FSS) Algorithmen zur Optimierung mit Populationen Fish School Search (FSS)
Fish School Search (FSS, Suche mittels Fischschulen) ist ein neuer Optimierungsalgorithmus, der durch das Verhalten von Fischen in einem Schwarm inspiriert wurde, von denen die meisten (bis zu 80 %) in einer organisierten Gemeinschaft von Verwandten schwimmen. Es ist erwiesen, dass Fischansammlungen eine wichtige Rolle für die Effizienz der Nahrungssuche und den Schutz vor Räubern spielen.
Lernen Sie, wie man ein Handelssystem mit Gator Oscillator entwickelt Lernen Sie, wie man ein Handelssystem mit Gator Oscillator entwickelt
Ein neuer Artikel in unserer Serie über die Entwicklung eines Handelssystems auf der Grundlage beliebter technischer Indikatoren wird sich mit dem technischen Indikator Gator Oscillator und der Erstellung eines Handelssystems durch einfache Strategien befassen.
DoEasy. Steuerung (Teil 30): Animieren des ScrollBar-Steuerelements DoEasy. Steuerung (Teil 30): Animieren des ScrollBar-Steuerelements
In diesem Artikel werde ich die Entwicklung des ScrollBar-Steuerelements fortsetzen und mit der Implementierung der Interaktionsfunktionen der Maus beginnen. Außerdem werde ich die Listen der Status-Flags der Maus und der Ereignisse erweitern.