Ajudar a escrever uma regressão linear - página 5

 
Rosh писал (а) >>

E isto é o que o Excel 2007 dá



Portanto, talvez seja necessário verificar o Matcad.

Se bem entendi, o Excel dá o 3º resultado, diferente dos dois primeiros ). Onde está a verdade? B é o mesmo que o Matcad, mas o coeficiente A é diferente.

 
Prival писал (а) >>

Se entendi corretamente, o Excel deu o 3º resultado, diferente dos dois primeiros ). Onde está a verdade? B é o mesmo que no Matcad, mas o coeficiente A é diferente.

Em geral, com tantos dígitos significativos e tal alcance, os cálculos vão para algum lugar na parte de trás da mantissa. Ou seja, mesmo elementos de aleatoriedade podem ser introduzidos na resposta. Acho que a exatidão só pode ser esperada para algum algoritmo especial de alta precisão. Neste caso, é melhor mudar a origem das coordenadas para mais perto da faixa X .



P.S. Especialmente quando a soma de X*X é calculada, as informações vão diretamente para o banheiro :)

 
lna01 писал (а) >>

Geralmente, com tantos algarismos significativos e uma gama tão grande, os cálculos vão para algum lugar na parte de trás da mantissa. Ou seja, mesmo elementos de aleatoriedade podem ser introduzidos na resposta. Acho que a exatidão só pode ser contada para algum algoritmo especial com maior precisão. Mas, neste caso, é melhor para mover a origem das coordenadas para mais perto da faixa X.

O problema é que eu comecei a me preparar para o campeonato. Comecei a traduzir meus desenvolvimentos em Matkadec em MQL. Se você se lembra, nós estávamos construindo a ACF (função de autocorrelação) e eu comecei com ela e decidi usar fórmulas diretas, pois coloquei muita carga na CPU através de transformadores Fourier.

Foi por isso que comecei a analisar onde o problema começou a crescer.

Vou tentar mudar X (Tempo) para 0. Mas terei que checar tudo novamente. Eu já tenho que desistir de cerca de 50% de minhas idéias.

 
Prival писал (а) >>

A questão é que eu comecei a me preparar para o campeonato. E eu comecei a transferir meus desenvolvimentos no Matcadet para a MQL. Se você se lembra, estávamos construindo ACF (função de autocorrelação), e eu comecei com ela e decidi calculá-la usando fórmulas diretas, já que estou carregando o processador pesadamente com transformadas de Fourier.

Foi por isso que comecei a analisar onde o problema começou a crescer.

Vou tentar mudar X (Tempo) para 0. Mas terei que checar tudo novamente. Como está, já tenho que abrir mão de 50% de minhas idéias

A MT mantém 15 dígitos na mantissa. Se extrairmos a raiz, obtemos 10^7. Ou seja, você tem que ajustar e somar números maiores que 10000000, ver postscript para o post anterior :). Felizmente, esse limite corresponde ao número de barras de minutos na história real, portanto, se for assim, ainda deve funcionar. Mas se chegar a hora, então uma mudança de origem coordenada é apenas inevitável.


P.S. A propósito, se você vai usar sua função no campeonato, acrescente proteção contra divisão por zero. Caso contrário, existe o risco de que seu indicador fique apenas no meio do campeonato. Ou no início. Lembrem-se, havia uma coisa assim com Fourier.

 

O mesmo algoritmo em Java

import java.util.ArrayList;

public class Prival {
public static void main(String arg[]){
int N = 6;
double Y[];
double X[];
ArrayList<Double> Parameters = new ArrayList<Double>();
Parameters.add(0.0);
Parameters.add(0.0);
X = new double[6];
Y = new double[6];
for ( int i = 0; i < N; i ++ )
{
// массивы Y и X для проверки работоспособности
// intercept = -3.33333333 slope = 5.00000000

X[i]=i;
Y[i]=i*i;
}

LinearRegr(X, Y, N,Parameters);
System.out.println("intercept = "+Parameters.get(0)+" slope = "+ Parameters.get(1));

// вторая проверка
X[0]=1216640160;
X[1]=1216640100;
X[2]=1216640040;
X[3]=1216639980;
X[4]=1216639920;
X[5]=1216639860;

Y[0]=1.9971;
Y[1]=1.9970;
Y[2]=1.9967;
Y[3]=1.9969;
Y[4]=1.9968;
Y[5]=1.9968;


LinearRegr(X, Y, N, Parameters);
System.out.println("intercept = "+Parameters.get(0)+" slope = "+ Parameters.get(1));

}
public static void LinearRegr(double X[], double Y[], int N, ArrayList<Double> Parameters){
double sumY = 0.0, sumX = 0.0, sumXY = 0.0, sumX2 = 0.0;
double A=0,B=0;
for ( int i = 0; i < N; i ++ ){
sumY +=Y[i];
sumXY +=X[i]*Y[i];
sumX +=X[i];
sumX2 +=X[i]*X[i];
}
B=(sumXY*N-sumX*sumY)/(sumX2*N-sumX*sumX);
A=(sumY-sumX*B)/N;
Parameters.set(0, A);
Parameters.set(1, B);
}
}


Resultado:


interceptar = -3,3333333333333335 declive = 5,0
interceptar = -1102.169141076954 declive = 9.075536028198574E-7

Processo terminado com o código de saída 0

 

Rosh

Eu concordo que estas fórmulas darão o mesmo resultado aqui é o matcad

Vejo que os resultados coincidem com MQL e Java, mas o matcad nunca me falhou antes, por isso tenho dúvidas. Eu verifiquei e classifiquei os resultados.

Eu verifiquei e ordenei por X e calculei novamente os coeficientes.

RESULTADO mudou!!!, este não deveria ser o caso. O mais provável é que o erro se deva ao acúmulo de erros devido à quadratura de grandes números(o candidato está certo). Investiguei a literatura e encontrei uma fórmula mais simples, sem quadratura e aparentemente com menos cálculos.

O resultado é o mesmo que no matcad, e não depende da classificação.

Recomendo usar esta fórmula para calcular os coeficientes de regressão linear.

//+------------------------------------------------------------------+
//|                                                       LinReg.mq4 |
//|                                                    Привалов С.В. |
//|                                             Skype -> privalov-sv |
//+------------------------------------------------------------------+
#property copyright "Привалов С.В."
#property link      "Skype -> privalov-sv"

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
//----
   int      N=6;                 // Размер массива
   double   Y[],X[],A=0.0, B=0.0;
   
  ArrayResize(X,N);
  ArrayResize(Y,N);
      
// проверка 
    X[0]=1216640160;
    X[1]=1216640100;
    X[2]=1216640040;
    X[3]=1216639980;
    X[4]=1216639920;
    X[5]=1216639860;
    
    Y[0]=1.9971;
    Y[1]=1.9970;    
    Y[2]=1.9967;
    Y[3]=1.9969;    
    Y[4]=1.9968;    
    Y[5]=1.9968;
    
    
  LinearRegr(X, Y, N, A, B);
  
  Print("A = ", DoubleToStr(A,8)," B = ",DoubleToStr(B,8));
           
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Рассчет коэффициентов A и B в уравнении                          |
//| y(x)=A*x+B                                                       |
//| используються формулы https://forum.mql4.com/ru/10780/page5       |
//+------------------------------------------------------------------+

void LinearRegr(double X[], double Y[], int N, double& A, double& B)
{
      double mo_X = 0.0, mo_Y = 0.0, var_0 = 0.0, var_1 = 0.0;
      
    for ( int i = 0; i < N; i ++ )
      {
        mo_X +=X[i];
        mo_Y +=Y[i];
      }
    mo_X /=N;
    mo_Y /=N;
        
    for ( i = 0; i < N; i ++ )
      {
        var_0 +=(X[i]-mo_X)*(Y[i]-mo_Y);
        var_1 +=(X[i]-mo_X)*(X[i]-mo_X);
      }
        A = var_0 / var_1;
        B = mo_Y - A * mo_X;
}

Em anexo, se alguém limpar o LinearRegr (para evitar erros ao trabalhar com dados reais e para aumentar o desempenho), ele será bom. Eu troquei A e B, porque

A notação y(x)=a*x+b é mais familiar (para mim de livros).

Arquivos anexados:
linreg_1.mq4  2 kb
 

Não vejo como o resultado pode depender da classificação. A classificação não é usada explicitamente em nenhum lugar nas fórmulas.


Além disso, este último algoritmo utiliza valores de expectativa de X e Y e pode também introduzir um erro nos cálculos. E outra coisa: usar dois loops contra um é pouco provável que melhore o desempenho.


Se precisarmos realizar cálculos de regressão linear maciça em várias seqüências de preços, é melhor selecionar buffers separados em um indicador e calcular usando o método do total cumulativo. Ele permite acelerar os cálculos por ordens de grandeza. Exemplo - Kaufman AMA otimizado : Perry Kaufman AMA otimizado

 
Rosh писал (а) >>

Não vejo como o resultado pode depender da classificação. A classificação não é usada explicitamente em nenhum lugar nas fórmulas.


Além disso, o último algoritmo usa a expectativa dos valores X e Y, e pode também introduzir algum erro nos cálculos. Mais uma coisa: usar dois loops contra um dificilmente melhoraria o desempenho.

Se precisarmos fazer os cálculos em massa da regressão linear para uma série de seqüências de preços, é melhor selecionar buffers separados em um indicador e usar o método de totalização cumulativa. Ele permite acelerar os cálculos por ordens de grandeza. Exemplo - Kaufman AMA otimizado : Perry Kaufman AMA otimizado

1. A questão é que o resultado não deve depender da classificação, enquanto o algoritmo depende. Confira.

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
//----
   int      N=6;                 // Размер массива
   double   Y[],X[],Y1[],X1[],A=0.0, B=0.0;
   
  ArrayResize(X,N);
  ArrayResize(Y,N);
  ArrayResize(X1,N);
  ArrayResize(Y1,N);
      
// проверка 
    X[0]=1216640160;
    X[1]=1216640100;
    X[2]=1216640040;
    X[3]=1216639980;
    X[4]=1216639920;
    X[5]=1216639860;
    
    Y[0]=1.9971;
    Y[1]=1.9970;    
    Y[2]=1.9967;
    Y[3]=1.9969;    
    Y[4]=1.9968;    
    Y[5]=1.9968;
    

// отсортируем массив по возрастанию X (исходный массив был по убыванию)
  for (int i = 0; i < N; i++)
   {
   X1[i]=X[N-i-1];
   Y1[i]=Y[N-i-1];
//   Print(X[i], " ", X1[i], " ", Y[i], " ", Y1[i]);
   }            
//----
// 
  LinearRegr(X, Y, N, A, B);
  Print("A = ", DoubleToStr(A,8)," B = ",DoubleToStr(B,8));
  LinearRegr(X1, Y1, N, A, B);
  Print(" A = ", DoubleToStr(A,8)," B = ",DoubleToStr(B,8));

  LinearRegr1(X, Y, N, A, B);
  Print("A = ", DoubleToStr(A,8)," B = ",DoubleToStr(B,8));
  LinearRegr1(X1, Y1, N, A, B);
  Print(" A = ", DoubleToStr(A,8)," B = ",DoubleToStr(B,8));

   return(0);
  }

//-------------------------------------------------------------------------------
// использование этой формулы приводит к ошибкам если X=Time
// формула предложена вот тут https://forum.mql4.com/ru/10780/page4
//| y(x)=A+B*x  

void LinearRegr(double X[], double Y[], int N, double& A, double& B)
{
      double sumY = 0.0, sumX = 0.0, sumXY = 0.0, sumX2 = 0.0;
      
    for ( int i = 0; i < N; i ++ )
    {
        sumY   +=Y[i];
        sumXY  +=X[i]*Y[i];
        sumX   +=X[i];
        sumX2  +=X[i]*X[i];
    }
   B=(sumXY*N-sumX*sumY)/(sumX2*N-sumX*sumX);
   A=(sumY-sumX*B)/N;
}

//+------------------------------------------------------------------+
//| Формула предлагаемая мной                                        |
//| Рассчет коэффициентов A и B в уравнении                          |
//| y(x)=A*x+B                                                       |
//| используються формулы https://forum.mql4.com/ru/10780/page5       |
//+------------------------------------------------------------------+

void LinearRegr1(double X[], double Y[], int N, double& A, double& B)
{
      double mo_X = 0.0, mo_Y = 0.0, var_0 = 0.0, var_1 = 0.0;
      
    for ( int i = 0; i < N; i ++ )
      {
        mo_X +=X[i];
        mo_Y +=Y[i];
      }
    mo_X /=N;
    mo_Y /=N;
        
    for ( i = 0; i < N; i ++ )
      {
        var_0 +=(X[i]-mo_X)*(Y[i]-mo_Y);
        var_1 +=(X[i]-mo_X)*(X[i]-mo_X);
      }
        A = var_0 / var_1;
        B = mo_Y - A * mo_X;
}

O resultado é

2008.07.30 13:51:08 LinReg EURUSD,M1: A = 0.00000090 B = -1098.77264952

2008.07.30 13:51:08 LinReg EURUSD,M1: A = 0.00000090 B = -1098.77264952

2008.07.30 13:51:08 LinReg EURUSD,M1: A = -1078.77267965 B = 0.00000089

2008.07.30 13:51:08 LinReg EURUSD,M1: A = -1102.16914108 B = 0.00000091

Este não deve ser o caso.

Posso ver que duas voltas aparecem, por isso pedi um desempenho mais rápido. O algoritmo 'Regressão: o que é?' pode ser mais rápido, mas devemos otimizá-lo também (acho que Vinin já o fez).

3. Obrigado por Kaufmann, é um bom indicador. Caso você não tenha esquecido antes do segundo campeonato, eu estava pegando imprecisões nele. Obrigado por corrigi-los.

Z.U. Eu pergunto quem tema Matemática. Digite nestas matrizes e calcule as fórmulas embutidas (até onde me lembro, existem), e poste o resultado aqui. A fim de chegar a um consenso. Obrigado. Ajuda )). Rosh é muito difícil de convencer, mas eu também tenho uma testa militar ))))

 
Prival писал (а) >>

2. vejo que duas voltas aparecem, por isso pedi uma aceleração. O algoritmo "Regressão: o que é?" pode ser mais rápido, mas devemos otimizá-lo também (acho que Vinin já o fez).

LWMA é de fato mais seguro que X*X, portanto, seu trabalho com Mathemat ganha um novo significado :). Mas eu ainda considero minha primeira recomendação (mudar a origem das coordenadas) como a melhor opção. A substituição formal de Time[pos] por Time[pos]-Time[Bars-1] em qualquer lugar é um risco de erro?

 
Prival писал (а) >>

1. O problema é esse: o resultado não deve depender da classificação, mas nesse algoritmo depende. Confira.

Resultado

2008.07.30 13:51:08 LinReg EURUSD,M1: A = 0.00000090 B = -1098.77264952

2008.07.30 13:51:08 LinReg EURUSD,M1: A = 0.00000090 B = -1098.77264952

2008.07.30 13:51:08 LinReg EURUSD,M1: A = -1078.77267965 B = 0.00000089

2008.07.30 13:51:08 LinReg EURUSD,M1: A = -1102.16914108 B = 0.00000091

Este não deve ser o caso.



Insira o ristrintokwa em seu código:

//-------------------------------------------------------------------------------
// использование этой формулы приводит к ошибкам если X=Time
// формула предложена вот тут https://forum.mql4.com/ru/10780/page4
//| y(x)=A+B*x  
 
void LinearRegr(double X[], double Y[], int N, double& A, double& B)
{
      double sumY = 0.0, sumX = 0.0, sumXY = 0.0, sumX2 = 0.0;
      
    for ( int i = 0; i < N; i ++ )
    {
        sumY   +=Y[i];
        sumXY  +=X[i]*Y[i];
        sumX   +=X[i];
        sumX2  +=X[i]*X[i];
    }
   Print("sumY = ",DoubleToStr(sumY,8)," sumX = ",DoubleToStr(sumX,8)," sumXY = ",DoubleToStr(sumXY,8)," sumX2 = ",DoubleToStr(sumX2,8));
   Print("sumXY*dN-sumX*sumY = ",DoubleToStr(sumXY*dN-sumX*sumY,8));    
   Print("sumX2*dN-sumX*sumX = ",DoubleToStr(sumX2*dN-sumX*sumX,8));    
 
   B=(sumXY*N-sumX*sumY)/(sumX2*N-sumX*sumX);
   A=(sumY-sumX*B)/N;
}

Obtenha algo como isto:

primeira chamada
sumY = 11,98130000 sumX = 7299840060.00000000 sumXY = 14576928951.87000100 sumX2 = 8881277483596863500.00000000
sumXY*dN-sumX*sumY = 0,34199524
sumX2*dN-sumX*sumX = 376832.00000000
A = -1102.16914108 B = 0.00000091
segunda chamada
sumY = 11,98130000 sumX = 7299840060.00000000 sumXY = 14576928951.87000300 sumX2 = 8881277483596864500.00000000
sumXY*dN-sumX*sumY = 0,34202576
sumX2*dN-sumX*sumX = 385024.00000000
A = -1078,77267965 B = 0,00000089

Esta é outra armadilha dos cálculos e arredondamentos feitos por computador. Por um lado, eu mesmo não esperava um ancinho assim, mas por outro lado tal diferença é compreensível quando duas séries de valores (X e Y) têm demasiada diferença na ordem dos valores.