Diferença entre R.loess e org.apache.commons.math LoessInterpolator
-
13-12-2019 - |
Pergunta
Estou tentando calcular a conversão de um script R para java usando o apache.commons.math biblioteca.Eu posso usar org.apache.commons.math.análise.interpolation.LoessInterpolator no lugar de R loess ?Não consigo obter o mesmo resultado.
EDITAR.
aqui está um programa java que cria um array aleatório (x,y) e calcula o loess com LoessInterpolator ou chamando R.Ao final, os resultados são impressos.
import java.io.*;
import java.util.Random;
import org.apache.commons.math.analysis.interpolation.LoessInterpolator;
public class TestLoess
{
private String RScript="/usr/local/bin/Rscript";
private static class ConsummeInputStream
extends Thread
{
private InputStream in;
ConsummeInputStream(InputStream in)
{
this.in=in;
}
@Override
public void run()
{
try
{
int c;
while((c=this.in.read())!=-1)
System.err.print((char)c);
}
catch(IOException err)
{
err.printStackTrace();
}
}
}
TestLoess()
{
}
private void run() throws Exception
{
int num=100;
Random rand=new Random(0L);
double x[]=new double[num];
double y[]=new double[x.length];
for(int i=0;i< x.length;++i)
{
x[i]=rand.nextDouble()+(i>0?x[i-1]:0);
y[i]=Math.sin(i)*100;
}
LoessInterpolator loessInterpolator=new LoessInterpolator(
0.75,//bandwidth,
2//robustnessIters
);
double y2[]=loessInterpolator.smooth(x, y);
Process proc=Runtime.getRuntime().exec(
new String[]{RScript,"-"}
);
ConsummeInputStream errIn=new ConsummeInputStream(proc.getErrorStream());
BufferedReader stdin=new BufferedReader(new InputStreamReader(proc.getInputStream()));
PrintStream out=new PrintStream(proc.getOutputStream());
errIn.start();
out.print("T<-as.data.frame(matrix(c(");
for(int i=0;i< x.length;++i)
{
if(i>0) out.print(',');
out.print(x[i]+","+y[i]);
}
out.println("),ncol=2,byrow=TRUE))");
out.println("colnames(T)<-c('x','y')");
out.println("T2<-loess(y ~ x, T)");
out.println("write.table(residuals(T2),'',col.names= F,row.names=F,sep='\\t')");
out.flush();
out.close();
double y3[]=new double[x.length];
for(int i=0;i< y3.length;++i)
{
y3[i]=Double.parseDouble(stdin.readLine());
}
System.out.println("X\tY\tY.java\tY.R");
for(int i=0;i< y3.length;++i)
{
System.out.println(""+x[i]+"\t"+y[i]+"\t"+y2[i]+"\t"+y3[i]);
}
}
public static void main(String[] args)
throws Exception
{
new TestLoess().run();
}
}
compilação e execução:
javac -cp commons-math-2.2.jar TestLoess.java && java -cp commons-math-2.2.jar:. TestLoess
saída:
X Y Y.java Y.R
0.730967787376657 0.0 6.624884763714674 -12.5936186703287
0.9715042030481429 84.14709848078965 6.5263049649584 71.9725380029913
1.6089216283982513 90.92974268256818 6.269100654071115 79.839773167581
2.159358633515885 14.112000805986721 6.051308261720918 3.9270340708818
2.756903911313087 -75.68024953079282 5.818424835586378 -84.9176311089431
3.090122310789737 -95.89242746631385 5.689740879461759 -104.617807889069
3.4753114955304554 -27.941549819892586 5.541837854229562 -36.0902352062634
4.460153035730264 65.6986598718789 5.168028655980764 58.9472823439219
5.339335553602744 98.93582466233818 4.840314399516663 93.3329030534449
6.280584733084859 41.21184852417566 4.49531113985498 36.7282165788057
6.555538699120343 -54.40211108893698 4.395343460231256 -58.5812856445538
6.68443584999412 -99.99902065507035 4.348559404444451 -104.039069260889
6.831037507640638 -53.657291800043495 4.295400167908642 -57.5419313320511
6.854275630124528 42.016703682664094 4.286978656933373 38.1564179414478
7.401015387322993 99.06073556948704 4.089252482141094 95.7504087842369
8.365502247999844 65.02878401571168 3.7422883733498726 62.5865641279576
8.469992934250815 -28.790331666506532 3.704793544880599 -31.145867173504
9.095139297716374 -96.13974918795569 3.4805388562453574 -98.0047896609079
9.505935493207435 -75.09872467716761 3.3330472034239405 -76.6664588290508
os valores de saída para y claramente não são os mesmos entre R e Java;A coluna Y.R parece boa (está próxima da coluna Y original).Como devo mudar isso para obter Y.java ~ Y.R ?
Solução
Você precisa alterar os valores padrão de três parâmetros de entrada para tornar as versões Java e R idênticas:
O Java LoessInterpolator faz apenas regressão polinomial local linear, mas R suporta linear (grau = 1), quadrático (grau = 2) e uma opção estranha de grau = 0.Então você precisa especificar
degree=1
em R para ser idêntico ao Java.LoessInterpolator padroniza o número de iterações
DEFAULT_ROBUSTNESS_ITERS=2
, mas o padrão Riterations=4
.Então você precisa definircontrol = loess.control(iterations=X)
em R (X é o número de iterações).Padrões do LoessInterpolator
DEFAULT_BANDWIDTH=0.3
mas R é o padrãospan=0.75
.
Outras dicas
Não posso falar pela implementação do Java, mas lowess
tem vários parâmetros que controlam a largura de banda do ajuste.A menos que você esteja ajustando os mesmos parâmetros de controle, você deve esperar que os resultados sejam diferentes.Minha recomendação sempre que as pessoas estiverem suavizando dados é traçar os dados originais, bem como o ajuste, e decidir por si mesmo quais parâmetros de controle geram a compensação desejada entre fidelidade aos dados e suavização (também conhecida como remoção de ruído).
Existem dois problemas aqui.Primeiro, se você plotar os dados que está gerando, eles parecerão quase aleatórios e o ajuste gerado pelo loess em R será muito ruim, por exemplo.
plot(T$x, T$y)
lines(T$s, T2$fitted, col="blue", lwd=3)
Então, no seu script R, você está escrevendo os resíduos, não as previsões, nesta linha
out.println("write.table(residuals(T2),'',
col.names= F,row.names=F,sep='\\t')");
você precisa mudar residuals(T2)
para predict(T2)
por exemplo.
out.println("write.table(predict(T2),'',
col.names= F,row.names=F,sep='\\t')");
Portanto, foi por puro acaso em seu exemplo de código que as primeiras linhas de resíduos gerados por R parecessem adequadas.
Para mim, se eu tentar ajustar alguns dados mais apropriados, Java e R retornarão resultados semelhantes, mas não idênticos.Também descobri que os resultados seriam mais próximos se eu não ajustasse o padrão robustezIter configurações.