English Русский 中文 Español Deutsch 日本語
preview
Seleção de características e redução de dimensionalidade com Análise de Componentes Principais (PCA)

Seleção de características e redução de dimensionalidade com Análise de Componentes Principais (PCA)

MetaTrader 5Estatística e análise |
175 1
Francis Dube
Francis Dube

Introdução

A previsão de séries temporais financeiras frequentemente envolve a análise de inúmeras características, muitas das quais podem ser altamente correlacionadas. Métodos de redução de dimensionalidade, como a Análise de Componentes Principais (Principal Component Analysis, PCA), podem ajudar a criar uma representação mais compacta desses objetos. No entanto, o PCA tem suas limitações, especialmente na presença de variáveis fortemente correlacionadas. Nesses casos, o PCA tende a exibir um efeito de agrupamento, no qual um conjunto de variáveis altamente correlacionadas, em conjunto, contribui para um determinado componente principal. Em vez de destacar uma variável específica, o PCA distribui a influência de forma relativamente uniforme por todas as variáveis dentro do grupo correlacionado.

Essa distribuição uniforme pode ser útil para a supressão de ruído, já que os componentes principais ressaltam padrões comuns, em vez de flutuações aleatórias características apenas de variáveis individuais. No entanto, essa supressão de ruído tem um custo: frequentemente reduz a contribuição das variáveis individuais em cada componente principal. Variáveis que poderiam ser relevantes por si só podem parecer menos importantes no espaço transformado, já que sua influência é absorvida por uma estrutura mais ampla, coberta pelo grupo. Isso pode ser uma limitação significativa em tarefas como seleção de variáveis, onde o objetivo é identificar as características mais importantes, ou em análise de causas fundamentais, onde a compreensão da influência direta de variáveis específicas é crucial.

Além disso, essa característica dificulta a interpretação do modelo. Como cada componente principal representa uma combinação de todas as variáveis brutas, transformar a contribuição desses componentes de volta para o contexto das variáveis originais pode ser uma tarefa complexa. Para os profissionais, pode ser difícil obter uma visão clara sobre os principais componentes, pois se torna complicado determinar quais variáveis originais estão de fato definindo os padrões observados. Para lidar com esse problema, apresentamos a implementação da Análise de Componentes de Seleção Direta (FSCA), um método inspirado no trabalho de Luca Puggini e Sean McLoone, que busca evitar as falhas do PCA ao lidar com características fortemente correlacionadas.


Análise de Componentes de Seleção Direta

A Análise de Componentes de Seleção Direta (FSCA) é um método de redução de dimensionalidade que combina tanto a redução de dimensionalidade quanto a seleção de características, determinando as variáveis mais informativas para explicar os dados brutos. O FSCA utiliza uma abordagem gulosa, escolhendo as variáveis uma a uma com base em sua capacidade de capturar a variância residual nos dados. A seguir está uma descrição resumida das principais etapas do procedimento FSCA:

  1. Inicialização:
    • Começamos com um conjunto vazio de variáveis selecionadas e o conjunto completo de variáveis candidatas.
    • Calculamos a variância total do conjunto de dados.
    • Iniciamos o processo iterativo escolhendo a variável que melhor prevê os valores de todas as outras variáveis, garantindo que essa escolha reflita a maior parcela de variância explicada.
  2. Seleção iterativa:
    • A cada etapa, avaliamos a contribuição de cada variável candidata remanescente para a variância explicada quando adicionada ao conjunto atual de variáveis selecionadas.
    • Escolhemos a variável que proporciona o maior aumento da variância explicada ao ser adicionada ao subconjunto.
  3. Atualização:
    • Adicionamos a variável selecionada ao subconjunto de variáveis escolhidas.
    • Removemos a variável selecionada do conjunto de variáveis candidatas.
    • Recalculamos a variância residual ou a variância não explicada após considerar a contribuição das variáveis selecionadas.
  4. Critérios de parada:
    • Continuamos o processo até que seja atingido um critério de parada pré-definido. Esse critério pode ser um número específico de variáveis selecionadas, uma fração alvo da variância total explicada ou um valor limite para a variância adicional explicada pela inclusão de uma nova variável.


      FSCA outline

Dado um conjunto de variáveis brutas, organizamos essas variáveis em forma de matriz, onde cada coluna representa uma característica distinta e cada linha representa uma amostra. Os valores brutos são inicialmente transformados por padronização. A partir desse ponto, qualquer referência às variáveis originais corresponde ao conjunto de variáveis padronizadas, que chamaremos de matriz X. Essa matriz X possui v colunas (variáveis). O algoritmo FSCA gera pelo menos três novos conjuntos de variáveis:

  • Matriz Z: Essa matriz é composta por um subconjunto k (onde k<v) de colunas de X, ordenadas de acordo com o quanto contribuem para a reconstrução de X. Essas colunas são conhecidas como Variáveis de Seleção Direta (FSV).
  • Matriz M: As colunas dessa matriz são chamadas de Componentes de Seleção Direta (FSC). Cada componente é uma função da respectiva coluna em Z, bem como das que a precedem, caso existam.
  • Matriz U: Essa matriz contém os coeficientes ou cargas, que em combinação com as FSV geram os FSC.

O objetivo do algoritmo é obter um subconjunto ideal de variáveis que melhor representem a variação única de X. No entanto, trata-se de um problema complexo de otimização. O FSCA nem sempre garante a identificação do subconjunto ótimo de variáveis de acordo com critérios de otimização pré-estabelecidos. Quando se trabalha com grandes conjuntos de dados, a busca por todos os possíveis subconjuntos pode se tornar impraticável, e por isso o FSCA propõe uma abordagem mais pragmática. Mesmo assim, em alguns casos não é possível encontrar o subconjunto ótimo, já que variáveis escolhidas nas primeiras iterações podem se tornar redundantes à medida que novas variáveis são adicionadas. Para superar essa limitação, os autores da pesquisa mencionada no início deste artigo sugerem incluir uma etapa de refinamento reverso no procedimento FSCA.


FSCA com refinamento reverso

O refinamento reverso é um procedimento que permite remover e substituir variáveis previamente selecionadas. Esse processo envolve a reavaliação da contribuição de cada variável escolhida para a variância total explicada e a consideração de variáveis alternativas que possam oferecer um melhor ajuste. Embora isso possa melhorar a qualidade do conjunto final de variáveis, acaba quebrando a ordem estrita de relevância mantida pela seleção puramente direta.

O trabalho de pesquisa descreve duas abordagens para a inclusão do refinamento reverso. Na primeira abordagem, o refinamento reverso é aplicado após a conclusão de todas as etapas do FSCA, funcionando como uma etapa de pós-processamento. A segunda abordagem, chamada de refinamento reverso recursivo, aplica o refinamento sempre que uma nova variável é adicionada a Z ao final do passo 3 do algoritmo FSCA. Essa é a abordagem implementada no código que será apresentado na próxima seção deste texto.

Existem também duas variantes do próprio estágio de refinamento. A primeira, chamada Refinamento Reverso Simples (SPBR), avalia sequencialmente a relevância de cada variável, indo da mais antiga para a mais recente. Na segunda, Refinamento Reverso Multietapas (MPBR), considera-se que variáveis inicialmente vistas como relevantes podem perder relevância à medida que ajustes são feitos em variáveis em etapas posteriores da sequência. No MPBR, o processo se repete até que seja concluído um ciclo completo sem necessidade de mais refinamentos. Vale observar que o código apresentado mais adiante neste artigo implementa apenas o SPBR.

Ilustração do Refinamento Reverso

Nas seções seguintes, descreveremos as etapas do algoritmo FSCA em maior detalhe, utilizando alguma matemática e código. Todos os trechos de código mencionados foram retirados do arquivo fsca.mqh, que pode ser encontrado no anexo ao final deste artigo.


Componentes principais

O algoritmo FSCA é baseado no uso de componentes principais para a determinação inicial das fontes únicas de variação no conjunto de dados X. Nesse estágio, também se define o número máximo de variáveis componentes para a matriz Z, que determina o valor de k (conforme definido anteriormente). No trabalho de pesquisa, o algoritmo utiliza um valor de k especificado pelo usuário. Entretanto, em nossa implementação, k é sempre definido como igual ao número de componentes principais. Os componentes principais são obtidos por meio da decomposição em valores característicos da matriz de correlação de X. A seguir está listada a rotina que calcula a estrutura fatorial utilizando a função compute_factor_structure().

//+------------------------------------------------------------------+
//| computes the factor structure of a correlation matrix            |
//+------------------------------------------------------------------+
matrix            compute_factor_structure(matrix &covar,matrix &eigenvectors,vector &eigenvalues,vector &cumeigenvalues)
  {
   if(!covar.EigenSymmetricDC(EIGVALUES_V,eigenvalues,eigenvectors))
     {
      Print(__FUNCTION__, " error ", GetLastError());
      return matrix::Zeros(1,1);
     }
   double sum = 0.0;


   if(!np::reverseVector(eigenvalues) ||  !np::reverseMatrixCols(eigenvectors))
     {
      Print(__FUNCTION__, " reverse operation error ", GetLastError());
      return matrix::Zeros(1,1);
     }

   double cumulate[];
   for(ulong i=0 ; i<eigenvalues.Size() ; i++)
     {
      if(eigenvalues[i]>1.e-8)
        {
         sum += eigenvalues[i] ;
         if(!cumulate.Push(sum))
           {
            Print(__FUNCTION__," error adding element ", GetLastError());
            return matrix::Zeros(1,1);
           }
        }
     }

   if(!cumeigenvalues.Assign(cumulate))
     {
      Print(__FUNCTION__," vector assignment error ", GetLastError());
      return matrix::Zeros(1,1);
     }

   cumeigenvalues/=cumeigenvalues[cumeigenvalues.Size()-1];

   cumeigenvalues*=100.0;

   matrix structmat=eigenvectors;

   for(ulong i = 0; i<structmat.Cols(); i++)
      if(!structmat.Col(eigenvectors.Col(i)*sqrt(eigenvalues[i]>=0.0?eigenvalues[i]:0.0),i))
        {
         Print(__FUNCTION__, "error ", GetLastError());
         return matrix::Zeros(1,1);
        }

   if(!structmat.Clip(-1.0,1.0))
     {
      Print(__FUNCTION__, "error ", GetLastError());
      return matrix::Zeros(1,1);
     }

   return structmat;
  }
//+------------------------------------------------------------------+
//| computes the factor structure of a correlation matrix            |
//+------------------------------------------------------------------+
matrix compute_factor_structure(matrix &covar, matrix &eigenvectors, vector &eigenvalues, vector &cumeigenvalues) {
    if (!covar.EigenSymmetricDC(EIGVALUES_V, eigenvalues, eigenvectors)) {
        Print(__FUNCTION__, " error ", GetLastError());
        return matrix::Zeros(1, 1);
    }

    double sum = 0.0;

    if (!np::reverseVector(eigenvalues) || !np::reverseMatrixCols(eigenvectors)) {
        Print(__FUNCTION__, " reverse operation error ", GetLastError());
        return matrix::Zeros(1, 1);
    }

    double cumulate[];
    for (ulong i = 0; i < eigenvalues.Size(); i++) {
        if (eigenvalues[i] > 1.e-8) {
            sum += eigenvalues[i];
            if (!cumulate.Push(sum)) {
                Print(__FUNCTION__, " error adding element ", GetLastError());
                return matrix::Zeros(1, 1);
            }
        }
    }

    if (!cumeigenvalues.Assign(cumulate)) {
        Print(__FUNCTION__, " vector assignment error ", GetLastError());
        return matrix::Zeros(1, 1);
    }

    cumeigenvalues /= cumeigenvalues[cumeigenvalues.Size() - 1];
    cumeigenvalues *= 100.0;

    matrix structmat = eigenvectors;

    for (ulong i = 0; i < structmat.Cols(); i++) {
        if (!structmat.Col(eigenvectors.Col(i) * sqrt(eigenvalues[i] >= 0.0 ? eigenvalues[i] : 0.0), i)) {
            Print(__FUNCTION__, "error ", GetLastError());
            return matrix::Zeros(1, 1);
        }
    }

    if (!structmat.Clip(-1.0, 1.0)) {
        Print(__FUNCTION__, "error ", GetLastError());
        return matrix::Zeros(1, 1);
    }

    return structmat;
}

Essa função recebe a matriz de correlação covar como entrada e retorna uma matriz contendo as cargas fatoriais. Ela realiza a decomposição em valores característicos da matriz de correlação utilizando a função EigenSymmetricDC. Os valores característicos resultantes são armazenados em um vetor de autovalores, enquanto os autovetores são guardados em uma matriz de autovetores. Em seguida, a função reordena os valores característicos e os vetores em ordem inversa, garantindo que fiquem classificados de forma decrescente. Ela calcula os valores característicos cumulativos por meio da soma iterativa e os armazena no vetor cumeigenvalues. Esses valores característicos acumulados são normalizados para representar a porcentagem da variância total.

Depois, a função calcula as cargas fatoriais multiplicando cada autovetor pela raiz quadrada do valor característico correspondente, armazenando o resultado na matriz structmat. Para garantir que as cargas fatoriais permaneçam dentro de limites razoáveis, elas são ajustadas para o intervalo de -1 a 1. Finalmente, a função retorna a matriz estrutural contendo as cargas fatoriais calculadas.

A estrutura fatorial derivada da matriz de correlação consiste em cargas fatoriais que representam as relações entre as variáveis e os fatores latentes subjacentes. Essas cargas ajudam a interpretar o significado dos fatores ocultos e a avaliar a relevância de cada variável para a explicação da dispersão nos dados.

Os dados de saída da função compute_factor_structure() são utilizados na função compute_principal_components() para calcular os componentes principais.

//+------------------------------------------------------------------+
//|   calculates the principal components                            |
//+------------------------------------------------------------------+
matrix compute_principal_components(void) {
    matrix out(m_data.Rows(), ulong(m_num_comps));
    vector drow, eigcol, nv;
    double sum;

    for (ulong i = 0; i < m_data.Rows(); i++) {
        drow = m_data.Row(i);
        for (ulong j = 0; j < m_num_comps; j++) {
            sum = 0.0;
            for (ulong k = 0; k < m_data.Cols(); k++) {
                sum += drow[k] * m_eigvectors[k][j] / sqrt(m_eigvalues[j]);
            }
            out[i][j] = sum;
        }
    }

    return out;
}

A função compute_principal_components() não recebe parâmetros de entrada e retorna uma matriz contendo os componentes principais. Para armazenar esses componentes, é inicializada a matriz de saída out, cujas dimensões correspondem ao número de linhas dos dados de entrada e ao número desejado de componentes principais. A função percorre cada linha da matriz de entrada, calculando cada componente principal por meio do produto escalar da linha pelo autovetor correspondente da matriz de covariância, dividido pela raiz quadrada do respectivo valor característico. O componente principal resultante é então armazenado na matriz out. Essa função calcula os componentes principais utilizando a fórmula padrão de projeção dos pontos de dados no subespaço dos componentes principais.

O conceito fundamental por trás dos componentes principais é a aproximação da matriz original de dados X, que contém múltiplas variáveis, utilizando um conjunto reduzido de variáveis constituintes. Essa aproximação é obtida por meio de uma transformação linear.

Componentes principais

Podemos estimar o erro dessa aproximação calculando a soma dos quadrados das diferenças entre X e sua aproximação.

Error of Approximation

Como alternativa, podemos avaliar a qualidade da aproximação medindo a fração da variância total de X que é explicada pelas variáveis componentes.

Explained Variance

Para alcançar a melhor aproximação, é necessário escolher estrategicamente um subconjunto de colunas de X para formar Z, que será utilizado no cálculo das variáveis constituintes. É importante destacar que os componentes principais e as variáveis finais representadas pela matriz M não são iguais.


Maximização da variância explicada

Um dos passos-chave do algoritmo FSCA é a seleção da variável ótima a ser adicionada ao subconjunto existente. Para isso, atribuímos um índice a cada variável candidata e escolhemos aquela que obtiver o maior valor. O cálculo desse critério, conforme descrito no trabalho de pesquisa, é relativamente complexo. Entretanto, os autores apresentam uma demonstração matemática de que a ordem de classificação desse critério entre as variáveis concorrentes é equivalente à ordem de classificação de suas variâncias explicadas. Isso significa que a variável com maior índice também maximiza a variância explicada.

Uma forma mais simples de descrever o cálculo do índice para cada variável candidata é utilizando a seguinte equação. Os leitores interessados na dedução matemática dessa fórmula podem consultar o trabalho de pesquisa.

Formula for Intermediate term

Formula for the criterion

Z(i) é a matriz Z com a adição da coluna i proveniente de X.
A variável x(j) é a coluna j de X.
O termo v representa a quantidade de variáveis (colunas) em X.
O termo intermediário q é um vetor-coluna de comprimento k. A

seguir está o código que implementa o cálculo do critério.

//+------------------------------------------------------------------+
//|  calculates the criterion for assessing a component              |
//+------------------------------------------------------------------+
double compute_criterion(matrix &covar, ulong &keptcols[], ulong nkept, ulong trial_col) {
    ulong i, j, k, irow, new_kept;
    double sum, crit, dtemp;
    new_kept = nkept + 1;
    matrix mt(new_kept, new_kept);

    for (i = 0; i < new_kept; i++) {
        if (i < nkept)
            irow = keptcols[i];
        else
            irow = trial_col;

        for (j = 0; j < nkept; j++)
            mt[i][j] = covar[irow][keptcols[j]];

        mt[i][nkept] = covar[irow][trial_col];
    }

    matrix mtinv = mt.Inv();
    vector vec(new_kept);

    crit = 0.0;
    for (j = 0; j < m_preds; j++) {
        for (i = 0; i < nkept; i++)
            vec[i] = covar[j][keptcols[i]];

        vec[nkept] = covar[j][trial_col];

        sum = 0.0;
        for (i = 0; i < new_kept; i++)
            sum += vec[i] * vec[i] * mtinv[i][i];

        crit += sum;

        sum = 0.0;
        for (i = 1; i < new_kept; i++) {
            dtemp = vec[i];
            for (k = 0; k < i; k++)
                sum += dtemp * vec[k] * mtinv[i][k];
        }

        crit += 2.0 * sum;
    }

    return crit;
}

A função compute_criterion() calcula o critério utilizado para avaliar um componente no processo de seleção de característica. Como dados de entrada, ela recebe a matriz de correlação covar, o array de variáveis selecionadas keptcols, a quantidade de variáveis já escolhidas nkept e o índice da variável de teste a ser avaliada.

A função começa criando uma nova matriz mt, que estende o conjunto existente de variáveis selecionadas com a variável de teste. Em seguida, calcula o inverso dessa matriz expandida mt. A função itera sobre todas as variáveis do conjunto de dados original, computando o critério de cada variável com base na covariância entre essa variável e as variáveis já escolhidas, ponderada pelo inverso da matriz mt. O valor do critério é acumulado na variável crit.

O objetivo dessa função é avaliar o impacto da inclusão de uma nova variável no conjunto já selecionado. Um valor mais alto do critério indica que a nova variável provavelmente aumentará a eficiência do modelo, enquanto um valor mais baixo sugere que ela pode ser irrelevante. Essa função pode ser empregada em algoritmos de seleção de características para identificar as variáveis mais informativas para um modelo específico.


Refinamento reverso recursivo

O refinamento reverso é uma variação da seleção direta, como demonstrado no código backward_refinement() listado abaixo.

//+------------------------------------------------------------------+
//| backward refinement routine                                      |
//+------------------------------------------------------------------+
ulong backward_refinement(matrix &covar, ulong &kept_columns[], ulong nkept, double &best_crit) {
    ulong i, old_col, new_col, best_col, refined;
    double crit;

    best_crit = substvar(covar, kept_columns, nkept, 0, kept_columns[0]);
    refined = 0;

    for (old_col = 0; old_col < nkept; old_col++) {

        if (old_col == nkept - 1 && !refined)
            break;

        best_col = ULONG_MAX;
        for (new_col = 0; new_col < m_preds; new_col++) {

            for (i = 0; i < nkept; i++) {
                if (new_col == kept_columns[i])
                    break;
            }

            if (i < nkept)
                continue;

            crit = substvar(covar, kept_columns, nkept, old_col, new_col);

            if (crit > best_crit) {
                best_crit = crit;
                best_col = new_col;
            }
        }

        if (best_col != ULONG_MAX && best_col >= 0) {
            // Print(__FUNCTION__,"  Replaced predictor at column ",kept_columns[old_col], " with ",best_col," to get criterion = ", best_crit);
            kept_columns[old_col] = best_col;
            refined = 1;
        }
    }
    return refined;
}

Esse código em MQL5 implementa o algoritmo de refinamento reverso para seleção de características. Ele avalia iterativamente o efeito da remoção de cada variável escolhida sobre o critério considerado. Ao calcular o critério para cada variável removida, a função determina qual remoção resulta na menor alteração significativa do critério. Se a mudança estiver abaixo de um limite predefinido, a variável é eliminada do conjunto selecionado, e o processo se repete até que não haja mais possibilidade de ajustes adicionais. A função retorna 1 se algum refinamento foi realizado e 0 caso contrário.

O procedimento substvar(), chamado dentro da função backward_refinement(), implementa o mecanismo de substituição de variáveis, que calcula o critério com base na matriz de correlação e no conjunto de variáveis selecionadas. Essa procedimento é utilizado no algoritmo de seleção de características para avaliar o impacto da troca de uma variável por outra.

//+------------------------------------------------------------------+
//| variable substitution routine                                    |
//+------------------------------------------------------------------+
double substvar(matrix &covar, ulong &keptcols[], ulong nkept, ulong old_col, ulong new_col) {
    ulong i, j, k, irow, saved_col;
    double sum, crit, dtemp;
    matrix mt(nkept, nkept);

    saved_col = keptcols[old_col];
    keptcols[old_col] = new_col;

    for (i = 0; i < nkept; i++) {
        irow = keptcols[i];
        for (j = 0; j < nkept; j++) {
            mt[i][j] = covar[irow][keptcols[j]];
        }
    }

    matrix mtinv = mt.Inv();
    vector vec(nkept);

    crit = 0.0;
    for (j = 0; j < m_preds; j++) {

        for (i = 0; i < nkept; i++) {
            vec[i] = covar[j][keptcols[i]];
        }

        sum = 0.0;
        for (i = 0; i < nkept; i++) {
            sum += vec[i] * vec[i] * mtinv[i][i];
        }
        crit += sum;

        sum = 0.0;
        for (i = 1; i < nkept; i++) {
            dtemp = vec[i];
            for (k = 0; k < i; k++) {
                sum += dtemp * vec[k] * mtinv[i][k];
            }
        }
        crit += 2.0 * sum;
    }

    keptcols[old_col] = saved_col;

    return crit;
}

A função recebe como entrada a matriz de correlação covar, o array de variáveis selecionadas keptcols, a quantidade de variáveis escolhidas nkept e os índices da variável antiga e da nova variável a serem substituídas. Ela cria uma matriz temporária mt para armazenar a submatriz de covar correspondente às variáveis selecionadas. Em seguida, a função calcula o inverso da matriz mt utilizando a função Inv(). Depois, itera sobre todas as variáveis do conjunto de dados original, calculando o critério de cada variável com base na covariância entre essa variável e as variáveis já escolhidas, ponderada pelo inverso da matriz mt. O valor do critério é acumulado na variável crit. Após o cálculo, a função restaura a variável original no array keptcols e retorna o critério obtido.

O objetivo dessa função é avaliar o efeito da substituição de uma variável por outra no modelo global. Um valor mais alto do critério indica que a substituição provavelmente aumentará a eficiência do modelo, enquanto um valor mais baixo sugere que ela pode ser irrelevante. Essa função pode ser integrada em algoritmos de seleção de características para determinar a combinação ótima de variáveis para um modelo específico.


Ortogonalização dos componentes

A seleção de variáveis por meio de refinamento estritamente direto, sem considerar o refinamento reverso, estabelece uma hierarquia baseada em sua relevância, começando pelas mais influentes e decrescendo progressivamente em termos de importância. Essa ordenação muitas vezes tem grande valor prático. Enquanto os valores originais podem ser utilizados diretamente, variáveis com componentes ortogonais, construídas como combinações lineares das variáveis originais, apresentam vantagens indiscutíveis. Esses componentes são independentes entre si, o que facilita o treinamento do modelo e minimiza a redundância. Além disso, a ausência de redundância nessas variáveis pode simplificar a interpretação de suas contribuições.

Para obter variáveis com componentes ortogonais preservando a ordem original, utiliza-se o método de ortogonalização de Gram-Schmidt (Gram-Schmidt orthogonalization method). Esse procedimento começa definindo o componente inicial como a primeira variável escolhida, ajustada em escala. Para os componentes seguintes, subtraem-se as projeções sobre os componentes já existentes. Ao subtrair sistematicamente essas projeções e normalizá-las para comprimento unitário, garante-se a ortogonalidade dos componentes. Por fim, o redimensionamento para um desvio padrão unitário assegura consistência. Em essência, a ortogonalização de Gram-Schmidt transforma as variáveis selecionadas em um conjunto de componentes ortogonais que mantêm a ordem original de relevância, oferecendo vantagens potenciais em termos de interpretabilidade e eficiência do modelo.

A seguir está a implementação da transformação de Gram-Schmidt. O resultado da função é uma nova matriz, onde cada coluna corresponde a um vetor ortogonalizado.

//+------------------------------------------------------------------+
//|   Gram Schmidt routine                                           |
//+------------------------------------------------------------------+
matrix gram_schmidt(matrix &input_) {
    ulong irow, icol, inner;
    double dtemp, sum;
    ulong nrows = input_.Rows();
    ulong ncols = input_.Cols();
    matrix output = input_;

    sum = 0.0;

    vector colsum = output.Col(0);
    colsum = MathPow(colsum, 2.0);
    sum = colsum.Sum();
    sum = sqrt(sum);

    if (sum == 0.0) {
        Print(__FUNCTION__, " sum == 0.0 ");
        return matrix::Zeros(0, 0);
    }

    if (!output.Col(output.Col(0) / sum, 0)) {
        Print(__FUNCTION__, " failed column insertion ", GetLastError());
        return matrix::Zeros(0, 0);
    }

    for (icol = 1; icol < ncols; icol++) {
        for (inner = 0; inner < icol; inner++) {
            sum = 0.0;
            for (irow = 0; irow < nrows; irow++)
                sum += (output[irow][icol] * output[irow][inner]);

            for (irow = 0; irow < nrows; irow++)
                output[irow][icol] -= (sum * output[irow][inner]);
        }

        sum = 0.0;
        for (irow = 0; irow < nrows; irow++) {
            dtemp = output[irow][icol];
            sum += dtemp * dtemp;
        }

        sum = sqrt(sum);

        if (sum == 0.0) {
            Print(__FUNCTION__, " sum == 0.0 ");
            return matrix::Zeros(0, 0);
        }

        if (!output.Col(output.Col(icol) / sum, icol)) {
            Print(__FUNCTION__, " failed column insertion ", GetLastError());
            return matrix::Zeros(0, 0);
        }
    }

    return output;
}

O algoritmo funciona ortogonalizando iterativamente cada coluna da matriz de entrada em relação às colunas já ortogonalizadas. Isso é feito projetando a coluna atual no subespaço formado pelas colunas anteriores e subtraindo essa projeção da coluna atual. O vetor resultante é então normalizado para comprimento unitário. O código inclui várias otimizações para melhorar a eficiência, como o uso de operações vetoriais nos cálculos e a eliminação de operações redundantes. Além disso, a função incorpora tratamento de erros para identificar problemas potenciais, como vetores de comprimento nulo ou falhas na inserção de colunas.

Após detalhar todos os principais procedimentos para realizar a seleção direta com suporte a refinamento reverso, na próxima seção será mostrado como essas rotinas são integradas na implementação completa do algoritmo FSCA.


Classe CFsca

A classe CFsca encapsula a funcionalidade necessária para executar a análise de componentes de seleção direta em um conjunto de dados. A definição completa da classe está em fsca.mqh, junto com uma rotina simples de padronização chamada stdmat(). Essa função recebe uma matriz como entrada e retorna a matriz padronizada.

//+------------------------------------------------------------------+
//| standardize a matrix                                             |
//+------------------------------------------------------------------+
matrix stdmat(matrix &in) {
    vector mean = in.Mean(0);
    vector std = in.Std(0);
    std += 1e-10;
    matrix out = in;

    for (ulong row = 0; row < out.Rows(); row++) {
        if (!out.Row((in.Row(row) - mean) / std, row)) {
            Print(__FUNCTION__, " error ", GetLastError());
            return matrix::Zeros(in.Rows(), in.Cols());
        }
    }

    return out;
}

A função stdmat() calcula a média e o desvio padrão de cada coluna da matriz de entrada, utilizando as funções Mean e Std. Em seguida, cria uma matriz de saída com as mesmas dimensões da matriz original. A função percorre cada linha da matriz de entrada, padronizando-a pela subtração da média da respectiva coluna e pela divisão pelo desvio padrão dessa coluna. A linha padronizada é armazenada na matriz de saída out, que é retornada ao final da execução. Essa função foi definida fora da classe CFsca para permitir seu uso independente.


A classe CFsca começa com a definição de seus membros privados, usados para armazenar resultados intermediários e estruturas de dados necessárias ao FSCA.

//+------------------------------------------------------------------+
//| fsca class implementation                                        |
//+------------------------------------------------------------------+
class CFsca
  {
private:
   bool              m_fitted;              //flag showing if principal factors were extracted
   matrix m_corrmat,             //correlation matrix
          m_covar,               //altered correlation matrix
          m_data,                //standardized data is here
          m_eigvectors,          //matrix of eigen vectors of m_corrmat matrix
          m_structmat,           //factor loading matrix of m_corrmat matrix
          m_principal_components,             //principal components
          m_fscv_struct,        //fsca factor structure
          m_fscv_eigvects,      //fsca eigen structure
          m_Fsca,        //ordered fsca variables
          m_coeffs,      //fsca component coefficients
          m_Fscv;        //refined fsca variables
   vector m_eigvalues,           //vector of eigen values of m_corrmat matrix
          m_sqcorr,              //mean squared correlation matrix
          m_fscv_eigvals,        //fsca eigen values
          m_fscv_cumeigvals,     //fsca cumulative variance contribution
          m_cumeigvalues;        //cumulative variance contributions of m_corrmat matrix
   ulong             m_num_comps;           //unique instances of redundent variation in m_data
   ulong             m_preds;               //number of variables (columns) in dataset (m_data)
   ulong  m_keptorderedcolumns[],//indices of columns upon which components are calculated for ordered fsca
          m_keptrefinedcolumns[],//indices of columns upon which components are calculated for backward refined fsca
          m_keptcolumns[],
          m_bestcolumn;          //index of first selected column in analysis
   double            m_best_crit;           //best criterion value

Em seguida, vêm os métodos privados da classe CFsca, alguns dos quais já foram descritos nas seções anteriores deste texto. Outros métodos serão apenas mencionados de forma resumida quando discutirmos os métodos públicos da classe.

Os métodos públicos são definidos no final da classe CFsca. O método fit() deve ser chamado primeiro, logo após a inicialização de uma instância da classe. Esse método executa a análise de componentes de seleção direta no conjunto de dados original. Ele recebe como entrada uma matriz contendo os dados brutos e retorna um valor lógico indicando se o processo de treinamento foi bem-sucedido.

//+------------------------------------------------------------------+
//| perform forward selection component analysis on a raw dataset    |
//+------------------------------------------------------------------+
bool fit(matrix &data) {
    m_preds = data.Cols();
    m_fitted = false;
    m_sqcorr = vector::Zeros(m_preds);

    m_data = stdmat(data);
    m_corrmat = m_data.CorrCoef(false);

    m_structmat = compute_factor_structure(m_corrmat, m_eigvectors, m_eigvalues, m_cumeigvalues);

    if (m_structmat.Rows() == 1)
        return false;

    m_num_comps = m_cumeigvalues.Size();

    if (ArrayResize(m_keptorderedcolumns, int(m_num_comps)) < 0 || 
        ArrayResize(m_keptrefinedcolumns, int(m_num_comps)) < 0 ||
        ArrayResize(m_keptcolumns, int(m_num_comps)) < 0 || 
        ArrayInitialize(m_keptcolumns, ULONG_MAX) < 0) {
        Print(__FUNCTION__, " array error ", GetLastError());
        return false;
    }

    m_principal_components = compute_principal_components();

    for (ulong i = 0; i < m_preds; i++)
        m_sqcorr[i] = (compute_criterion(m_corrmat, m_keptcolumns, 0, i) - 1.0) / double(m_preds - 1);

    vector evd_vals = m_eigvalues;
    while (evd_vals[m_preds - 1] <= 0.0) {
        for (ulong j = 1; j < m_preds; j++) {
            for (ulong k = 0; k < j; k++) {
                m_corrmat[j][k] *= 0.99999;
                m_corrmat[k][j] = m_corrmat[j][k];
            }
        }

        matrix empty;
        if (!m_corrmat.EigenSymmetricDC(EIGVALUES_N, evd_vals, empty)) {
            Print(__FUNCTION__, " failed eig decomp ", GetLastError());
            return false;
        }
    }

    m_Fsca = compute_fsca_components(m_data);
    m_Fscv = compute_fscv_components(m_data);

    m_fitted = (m_Fsca.Rows() > 1 && m_Fscv.Rows() > 1);

    return m_fitted;
}

A função fit() inicializa diversas variáveis e matrizes usadas no processo do FSCA. Primeiro, ela padroniza a matriz de entrada, garantindo que apresente média zero e variância unitária. Em seguida, calcula a matriz de correlação dos dados padronizados, armazenando-a em m_corrmat. Depois, calcula a estrutura fatorial da matriz de correlação utilizando a função compute_factor_structure. Essa estrutura fatorial inclui os autovetores (m_eigvectors), os autovalores (m_eigvalues) e os autovalores acumulados (m_cumeigvalues) da matriz de correlação. A função verifica se a matriz da estrutura fatorial (m_structmat) contém apenas uma linha; se isso ocorrer, significa que houve um erro no cálculo da estrutura fatorial, e a função retorna o valor false.

O número de componentes (m_num_comps) é definido como o número de autovalores não nulos. Em seguida, a função inicializa diferentes arrays utilizados no processo do FSCA. Ela calcula os componentes principais dos dados padronizados com a função compute_principal_components e, em seguida, calcula as correlações quadráticas entre cada variável e o primeiro componente principal. Além disso, a função verifica se existem autovalores negativos. Caso sejam detectados, faz pequenos ajustes na matriz de correlação e recalcula os autovalores até que todos se tornem positivos. Por fim, a função calcula os componentes do FSCA com a função compute_fsca_components e os componentes FSCV com a função compute_fscv_components. Se ambos forem calculados com sucesso, a função define o indicador m_fitted como true.

Concluindo a definição da classe CFsca, apresentamos as funções de acesso, que fornecem meios para obter diferentes aspectos dos cálculos analíticos realizados durante o processo do FSCA.

//+------------------------------------------------------------------+
//| get the principal components                                     |
//+------------------------------------------------------------------+
matrix get_principal_components(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return matrix::Zeros(0, 0);
    }

    return m_principal_components;
}

//+------------------------------------------------------------------+
//| get the ordered fsca components                                  |
//+------------------------------------------------------------------+
matrix get_fsca_components(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return matrix::Zeros(0, 0);
    }

    return m_Fsca;
}

//+------------------------------------------------------------------+
//| get the backward refined fsca components                         |
//+------------------------------------------------------------------+
matrix get_fscv_components(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return matrix::Zeros(0, 0);
    }

    return m_Fscv;
}

//+------------------------------------------------------------------+
//| get indices of variables defining the ordered fsca components    |
//+------------------------------------------------------------------+
bool get_fsca_var_indices(ulong &indices[]) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return false;
    }

    return (ArrayCopy(indices, m_keptorderedcolumns, 0, 0, int(m_num_comps)) > 0);
}

//+---------------------------------------------------------------------------+
//| get indices of variables defining the backward refined fsca components    |
//+---------------------------------------------------------------------------+
bool get_fscv_var_indices(ulong &indices[]) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return false;
    }

    return (ArrayCopy(indices, m_keptrefinedcolumns, 0, 0, int(m_num_comps)) > 0);
}

//+-------------------------------------------------------------------+
//| get cumulative variance contribution based on principal components|
//+-------------------------------------------------------------------+
vector get_principal_components_cumulative_variance_contribution(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return vector::Zeros(0);
    }

    return m_cumeigvalues;
}

//+-------------------------------------------------------------------+
//| get cumulative variance contribution based on fscv components     |
//+-------------------------------------------------------------------+
vector get_fscv_cumulative_variance_contribution(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return vector::Zeros(0);
    }

    return m_fscv_cumeigvals;
}

//+-------------------------------------------------------------------+
//| get eigen structure of principal components                       |
//+-------------------------------------------------------------------+
bool get_principal_components_eigstructure(matrix &vectors, vector &values) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return false;
    }

    vectors = m_eigvectors;
    values = m_eigvalues;

    return true;
}

//+-------------------------------------------------------------------+
//| get eigen structure of backward refined FSCs                      |
//+-------------------------------------------------------------------+
bool get_fscv_eigstructure(matrix &vectors, vector &values) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return false;
    }

    vectors = m_fscv_eigvects;
    values = m_fscv_eigvals;

    return true;
}

//+-------------------------------------------------------------------+
//| get principal components factor structure                         |
//+-------------------------------------------------------------------+
matrix get_principal_components_factorstructure(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return matrix::Zeros(0, 0);
    }

    return m_structmat;
}

//+-------------------------------------------------------------------+
//| get the factor structure of FSC with backward refinement          |
//+-------------------------------------------------------------------+
matrix get_fscv_factorstructure(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return matrix::Zeros(0, 0);
    }

    return m_fscv_struct;
}

//+------------------------------------------------------------------+
//| get mean squared correlations                                    |
//+------------------------------------------------------------------+
vector get_avg_correlations(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return vector::Zeros(0);
    }

    return m_sqcorr;
}

//+-------------------------------------------------------------------+
//| get forward selection component coefficients matrix               |
//+-------------------------------------------------------------------+
matrix get_fsca_component_coeffs(void) {
    if (!m_fitted) {
        Print(__FUNCTION__, " either analyze() returned an error or it was not called ");
        return matrix::Zeros(0, 0);
    }

    return m_coeffs;
}

Essas funções permitem que os usuários recuperem resultados importantes, como os dados padronizados, a matriz de correlação, a estrutura fatorial, os componentes principais e as variáveis componentes, facilitando a análise posterior e a interpretação dos resultados. Encerramos essa explicação demonstrando como aplicar a classe CFsca.


Exemplo

O código listado abaixo define um script em MQL5 chamado FSCA_Demo.mq5, que executa a Análise de Componentes de Seleção Direta (FSCA) em um conjunto de dados gerado aleatoriamente. O script inclui o arquivo de cabeçalho fsca.mqh, que contém a definição da classe CFsca, utilizada para a análise FSCA.

//+------------------------------------------------------------------+
//|                                                    FSCA_Demo.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include<fsca.mqh>
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   MathSrand(120);
//---
   matrix mat(100,9);
//---
   mat.Random(0.0,1.0);
//---
   vector var1 = mat.Col(0) + mat.Col(1);
// ---
   vector var2 = mat.Col(2) + mat.Col(3);
//---
   vector var3 = var1 + var2;
//---
   if(!mat.Col(var1,6) || !mat.Col(var2,7) || !mat.Col(var3,8))
     {
      Print("failed column assignment ", GetLastError());
      return;
     }
//---
   CFsca fsca;
//---
   if(!fsca.fit(mat))
      return;
//---
   ulong index[];
   Print("Principal components cumulative variance conributions \n", fsca.get_principal_components_cumulative_variance_contribution());
   Print(" Principal components factor structure \n", fsca.get_principal_components_factorstructure());
   Print("Mean squared correlation of each variable with all others \n", fsca.get_avg_correlations());
//---
   if(fsca.get_fsca_var_indices(index))
     {
      Print(" Ordered FSCA components based on variables located in column indices ");
      ArrayPrint(index);
     }
//---
   Print(" Ordered FSCA component coefficients matrix \n", fsca.get_fsca_component_coeffs());
//---
   if(fsca.get_fscv_var_indices(index))
     {
      Print(" Backward refined FSCA components based on variables located in column indices ");
      ArrayPrint(index);
     }
//---
   matrix vects;
   vector vals;
//---
   if(fsca.get_fscv_eigstructure(vects,vals))
     Print("Backward refined fsca component eigenvalues \n", vals);
//---
   Print(" Backward refined cumulative variance contributions \n", fsca.get_fscv_cumulative_variance_contribution());
//---
   Print(" Backward refined fsca components factor structure \n", fsca.get_fscv_factorstructure());
  }
//+------------------------------------------------------------------+

Tudo começa com a definição de uma semente aleatória e a criação de uma matriz de valores randômicos. Três novos vetores são criados pela soma de colunas existentes da matriz. Em seguida, o script tenta treinar um modelo FSCA com esses dados matriciais e extrai diferentes resultados do FSCA, incluindo a contribuição para a variância acumulada, a estrutura fatorial e as correlações quadráticas médias. Ele também executa tanto o FSCA padrão quanto o FSCA com refinamento reverso, exibindo os índices das variáveis orderadas e os coeficientes dos componentes. Além disso, mostra a estrutura fatorial dos componentes do FSCA refinados de forma reversa. O script simplesmente gera um conjunto de dados de variáveis aleatórias com 100 amostras e 9 características. As últimas 3 variáveis do conjunto de dados são construídas de modo a depender de outras variáveis, enquanto as restantes são independentes. 

Ao executar o script e analisar as características dos principais componentes, verificamos que existem apenas seis fontes únicas de variação entre as nove variáveis.

DG      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   Principal components cumulative variance conributions 
RM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   [31.72121326219056,54.98374706330443,70.21399790786099,82.34742379766755,91.9067775629936,100]
CM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    Principal components factor structure 
MH      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   [[-0.5430877600903072,0.4698851388299595,-0.02139789374204959,0.5468988095320395,-0.4254498037566715,-0.06082703269184352,-1.3842452210817e-09,-7.527559895073524e-10,0]
RH      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.5283294988376427,0.4246506210338227,-0.1190026606589024,-0.6285471675075863,0.3351428244532062,0.1377893424956133,-1.351333204556555e-09,-7.34858353171856e-10,0]
ES      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.5563230307728618,-0.3632156082945803,0.4770295070287525,0.3051596297191441,0.4575827252246154,-0.1688715686919785,-1.694159101189325e-09,2.164855665400724e-10,0]
NF      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.2379617600470182,-0.6943076552356867,-0.4580766878219635,-0.2737307249578351,-0.4112051990027778,0.08636320534609224,-1.769139142521297e-09,2.260667780777343e-10,0]
DO      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [0.02487447101754412,-0.08203927651647476,-0.6079889924620585,0.4701685445643955,0.3604839483405348,0.5215295442601863,1.313244252124911e-25,1.228203109452781e-25,0]
IP      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.07016546285360215,-0.110242984252018,0.7306990214221818,-0.07491798042552207,-0.2276538908363994,0.6257501374625694,4.158606798395552e-25,-7.369218656716687e-26,0]
NE      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.7598477338270035,0.6346843827822741,-0.09872272409405579,-0.04786755450396145,-0.0705236166963274,0.05287812507625003,-1.359967783374346e-10,1.333715775589249e-09,0]
NO      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.5934182368659159,-0.8024046895986707,-0.000973814713973191,0.01424096330384607,0.02077689502375221,-0.05801790712583575,2.667167815186452e-10,-1.355042254629964e-11,0]
RM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-0.9897757278036692,-0.1138316306178367,-0.07343616940955985,-0.02494592427830717,-0.03690113231990194,-0.0030829919659495,2.802923111601039e-09,-3.865027968054199e-10,0]]

Isso é esperado, já que três das variáveis são combinações de outras. Ao observarmos a contribuição acumulada de cada variável para a variância, notamos que o primeiro componente representa aproximadamente um terço da variação total. A estrutura fatorial indica uma correlação inversa moderada ou forte entre o primeiro componente principal e as variáveis nas colunas de 0 a 3 e de 6 a 8, enquanto as variáveis das colunas 4 e 5 exibem a tendência oposta. O segundo componente reflete as diferenças entre as variáveis 0 e 1 em comparação com 2 e 3. Juntos, os dois primeiros componentes explicam mais de 55 por cento da variação total.

QK      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   Mean squared correlation of each variable with all others 
GK      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   [0.09874555673359317,0.09196664871445229,0.09678803260182336,0.08640965168371836,0.009232616218980055,0.01341075732654295,0.1892345119240549,0.1695472310064176,0.2291509382521983]

Passando ao vetor de correlações quadráticas médias de cada variável com o conjunto combinado de todas as variáveis, esse vetor mostra as correlações na mesma ordem em que as variáveis aparecem no conjunto de dados original. As três últimas variáveis apresentam as maiores correlações médias, enquanto as variáveis das colunas 4 e 5 apresentam os menores valores.

LR      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    Ordered FSCA components based on variables located in column indices 
QQ      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   8 6 2 4 1 5

Em seguida, analisamos os valores selecionados diretamente, obtidos por meio da função get_fscv_var_indices(). Nela são exibidos os índices das variáveis que melhor capturam a maior parte das variações do conjunto de dados, organizados em ordem decrescente conforme a contribuição de cada variável para a dispersão total dos dados. Nesse caso, observamos que a última variável reflete as maiores diferenças nos dados, já que aparece em primeiro lugar.

QJ      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    Ordered FSCA component coefficients matrix 
DM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   [[0.9999999989356357,-0.9551778313323678,-1.196676438579672,-0.163265209103464,-0.1301792726137802,0.0741114239785734]
IR      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [7.62883988565579e-10,1.382882745177175,0.7080052470472653,0.1327136589445282,-0.8962870520067646,-0.01038862969019799]
LF      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [6.044914586250671e-10,-1.162965671680505e-09,1.327736785211269,0.1291890234653878,0.1244453203448803,-0.2315140872599129]
EM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [5.84342504938995e-11,-9.115276242144255e-11,-1.685031073006549e-10,1.005785752630206,0.08917398176616295,0.2288955899392838]
JM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [6.626278020206711e-11,8.05911615654048e-10,2.135397240976555e-10,-3.939133914887538e-11,1.404086244047662,0.03569800251260542]
KK      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    [-2.859616016204214e-11,3.48387846349496e-11,2.600743786995707e-10,-1.479500966183878e-10,-3.333024481411151e-11,1.048952273510343]]

Depois, examinamos a tabela de coeficientes necessária para calcular os seis componentes. As variáveis são listadas na mesma ordem de relevância apresentada pelos índices do array que acabamos de analisar. Aqui, percebemos que, como a última variável é a mais importante, o coeficiente correspondente é muito próximo de 1. À medida que descemos pela matriz, notamos que os coeficientes se tornam tão pequenos que praticamente se aproximam de zero.

CM      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)    Backward refined FSCA components based on variables located in column indices 
ND      0       07:16:46.014    FSCA_Demo (BTCUSD,D1)   3 0 2 4 1 5

Por fim, investigamos os índices das variáveis selecionadas por meio da combinação de seleção direta e refinamento reverso. É importante destacar que, nesse tipo de FSCA, a ordem das variáveis escolhidas não tem importância. No entanto, o conjunto resultante é mais relevante do que aquele obtido pelo método de ordenação estrita, pois mantém apenas variáveis aleatórias independentes, descartando as que apresentam algum grau de dependência. Isso fica evidente ao compararmos os resultados tanto da seleção direta padrão quanto da seleção direta com refinamento reverso.


Considerações finais

Em conclusão, apresentamos a implementação da análise de componentes de seleção direta em MQL5, demonstrando sua eficácia como ferramenta de redução de dimensionalidade e seleção de características. Esse recurso também pode ser utilizado como etapa preliminar de análise de dados. Um dos subprodutos do algoritmo é a estrutura fatorial do conjunto de dados, que pode ser útil para compreender os efeitos em ação. Todo o código mencionado neste artigo está incluído no anexo abaixo.
Nome do arquivo
 Descrição
MQL5/include/np.mqh
arquivo de cabeçalho com funções utilitárias genéricas para vetores e matrizes
MQL5/include/fsca.mqh
arquivo de cabeçalho contendo a definição da classe CFsca, que implementa a Análise de Componentes de Seleção Direta
MQL5/scripts/FSCA_Demo.mq5
script do MetaTrader 5 que demonstra o uso da classe CFsca

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

Arquivos anexados |
np.mqh (74.16 KB)
fsca.mqh (26.89 KB)
FSCA_Demo.mq5 (2.46 KB)
Mql5.zip (16.32 KB)
Últimos Comentários | Ir para discussão (1)
Aleksey Vyazmikin
Aleksey Vyazmikin | 11 jul. 2025 em 12:07

O tópico é, obviamente, eterno e sempre relevante.

Seria bom ter métodos diferentes no artigo para comparar sua eficácia, não em dados sintéticos, mas em dados reais.

Tentei aumentar o número de recursos para 5.000 e de linhas para 10.000 - esperei três dias pelo resultado - nenhum resultado. Então, eu me pergunto se a qualidade sofreria significativamente se dividíssemos o número de recursos em grupos, digamos, 100 exemplos cada, e depois reuníssemos os vencedores de cada grupo para uma seleção final?

Criação de um painel de administração de trading em MQL5 (Parte V): Autenticação de dois fatores (2FA) Criação de um painel de administração de trading em MQL5 (Parte V): Autenticação de dois fatores (2FA)
Este artigo aborda o aumento da segurança do painel de administração de trading, atualmente em desenvolvimento. Vamos explorar como integrar o MQL5 a uma nova estratégia de segurança, utilizando a API do Telegram para autenticação de dois fatores (2FA). O artigo traz informações valiosas sobre a aplicação de MQL5 para reforçar medidas de segurança. Além disso, veremos a função MathRand, focando em sua funcionalidade e na forma como pode ser usada de forma eficiente em nosso sistema de segurança.
Criando um painel MQL5 interativo usando a classe Controls (Parte 2): Adicionando responsividade aos botões Criando um painel MQL5 interativo usando a classe Controls (Parte 2): Adicionando responsividade aos botões
Neste artigo, vamos transformar nosso painel de monitoramento MQL5 estático em uma ferramenta interativa, adicionando responsividade aos botões. Veremos como automatizar a funcionalidade dos componentes da interface gráfica, garantindo que eles respondam corretamente aos cliques do usuário. Ao final do artigo, criaremos uma interface dinâmica que melhora o engajamento do usuário e a praticidade da negociação.
Técnicas do MQL5 Wizard que você deve conhecer (Parte 44): Indicador técnico Average True Range (ATR) Técnicas do MQL5 Wizard que você deve conhecer (Parte 44): Indicador técnico Average True Range (ATR)
O oscilador ATR é um indicador muito popular para atuar como um proxy de volatilidade, especialmente nos mercados de forex onde os dados de volume são escassos. Nós o examinamos com base em padrões, assim como fizemos com indicadores anteriores, e compartilhamos estratégias e relatórios de testes graças às classes da biblioteca MQL5 wizard e sua montagem.
Recursos do Assistente MQL5 que você precisa conhecer (Parte 43): Aprendizado por reforço com SARSA Recursos do Assistente MQL5 que você precisa conhecer (Parte 43): Aprendizado por reforço com SARSA
O SARSA (State-Action-Reward-State-Action, estado–ação–recompensa–estado–ação) é outro algoritmo que pode ser utilizado na implementação de aprendizado por reforço. Vamos analisar como esse algoritmo pode ser implementado como um modelo independente (e não apenas como um mecanismo de aprendizado) em Expert Advisors gerados no Wizard, de forma semelhante ao que fizemos nos casos de Q-learning e DQN.