/* * copyright: Anthony Bagnall * */ package weka.filters.timeseries; import weka.filters.*; import weka.core.Attribute; import weka.core.DenseInstance; import weka.core.FastVector; import weka.core.Instance; import weka.core.Instances; public class PACF extends SimpleBatchFilter { //Max number of AR terms to consider. public static int globalMaxLag=25; private double[] autos; private double[][] partials; //Defaults to 1/4 length of series public int maxLag=globalMaxLag; public void setMaxLag(int a){maxLag=a;} @Override protected Instances determineOutputFormat(Instances inputFormat) throws Exception { //Check all attributes are real valued, otherwise throw exception for(int i=0;i<inputFormat.numAttributes();i++) if(inputFormat.classIndex()!=i) if(!inputFormat.attribute(i).isNumeric()) throw new Exception("Non numeric attribute not allowed in ACF"); if(inputFormat.classIndex()>=0) //Classification set, dont transform the target class! maxLag=(inputFormat.numAttributes()-1>maxLag)?maxLag:inputFormat.numAttributes()-1; else maxLag=(inputFormat.numAttributes()>maxLag)?maxLag:inputFormat.numAttributes(); //Set up instances size and format. FastVector atts=new FastVector(); String name; for(int i=0;i<maxLag;i++){ name = "PACF_"+i; atts.addElement(new Attribute(name)); } if(inputFormat.classIndex()>=0){ //Classification set, set class //Get the class values as a fast vector Attribute target =inputFormat.attribute(inputFormat.classIndex()); FastVector vals=new FastVector(target.numValues()); for(int i=0;i<target.numValues();i++) vals.addElement(target.value(i)); atts.addElement(new Attribute(inputFormat.attribute(inputFormat.classIndex()).name(),vals)); } Instances result = new Instances("PACF"+inputFormat.relationName(),atts,inputFormat.numInstances()); if(inputFormat.classIndex()>=0) result.setClassIndex(result.numAttributes()-1); return result; } @Override public Instances process(Instances inst) throws Exception { Instances output=determineOutputFormat(inst); //For each data, first extract the relevan int seriesLength=inst.numAttributes(); int acfLength=output.numAttributes(); if(inst.classIndex()>=0){ seriesLength--; acfLength--; } double[] d; for(int i=0;i<inst.numInstances();i++){ //1. Get series d=inst.instance(i).toDoubleArray(); //Need to remove the class int c=inst.classIndex(); if(c>=0){ double[] temp=new double[d.length-1]; int count=0; //Arraycopy more efficient, dont really trust it for(int k=0;k<d.length;k++){ if(k!=c){ temp[count]=d[k]; count++; } } d=temp; } //2. Fit Autocorrelations autos=ACF.fitAutoCorrelations(d,maxLag); //3. Form Partials partials=formPartials(autos); //5. Find parameters double[] pi= new double[maxLag]; for(int k=0;k<maxLag;k++){ //Set NANs to zero if(Double.isNaN(partials[k][k]) || Double.isInfinite(partials[k][k])){ pi[k]=0; } else pi[k]=partials[k][k]; } //6. Stuff back into new Instances. Instance in= new DenseInstance(output.numAttributes()); //Set class value. int cls=output.classIndex(); if(cls>=0) in.setValue(cls, inst.instance(i).classValue()); int count=0; //Allows for a class index not at the end, or should do so. for(int k=0;k<pi.length;k++){ if(k!=cls){ in.setValue(count, pi[k]); count++; } } output.add(in); } return output; } public static double[][] formPartials(double[] r){ //Using the Durban-Leverson int p=r.length; double[][] phi = new double[p][p]; double numerator,denominator; phi[0][0]=r[0]; for(int k=1;k<p;k++){ //Find diagonal k,k //Naive implementation, should be able to do with running sums numerator=r[k]; for(int i=0;i<k;i++) numerator-=phi[i][k-1]*r[k-1-i]; denominator=1; for(int i=0;i<k;i++) denominator-=phi[k-1-i][k-1]*r[k-1-i]; phi[k][k]=numerator/denominator; //Find terms 1,k to k-1,k for(int i=0;i<k;i++) phi[i][k]=phi[i][k-1]-phi[k][k]*phi[k-1-i][k-1]; } return phi; } public double[][] getPartials(){return partials;} @Override public String globalInfo() { return null; } @Override protected boolean hasImmediateOutputFormat() { return false; } public String getRevision() { return null; } public static void main(String[] args){ } }