Logistic.java
Upload User: rhdiban
Upload Date: 2013-08-09
Package Size: 15085k
Code Size: 19k
Category:

Windows Develop

Development Platform:

Java

  1. /*
  2.  *    This program is free software; you can redistribute it and/or modify
  3.  *    it under the terms of the GNU General Public License as published by
  4.  *    the Free Software Foundation; either version 2 of the License, or
  5.  *    (at your option) any later version.
  6.  *
  7.  *    This program is distributed in the hope that it will be useful,
  8.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  9.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10.  *    GNU General Public License for more details.
  11.  *
  12.  *    You should have received a copy of the GNU General Public License
  13.  *    along with this program; if not, write to the Free Software
  14.  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15.  */
  16. /*
  17.  *    Logisticf.java
  18.  *    Copyright (C) 1999 Len Trigg, Eibe Frank, Tony Voyle
  19.  *
  20.  */
  21. package weka.classifiers;
  22. import java.util.*;
  23. import java.io.*;
  24. import weka.core.*;
  25. import weka.filters.*;
  26. /**
  27.  * Class for building and using a two-class logistic regression model
  28.  * with a ridge estimator.  <p>
  29.  * 
  30.  * This class utilizes globally convergent Newtons Method adapted from
  31.  * Numerical Recipies in C.
  32.  *
  33.  * Reference: le Cessie, S. and van Houwelingen, J.C. (1997). <i>
  34.  * Ridge Estimators in Logistic Regression.</i> Applied Statistics,
  35.  * Vol. 41, No. 1, pp. 191-201. <p>
  36.  *
  37.  * Missing values are replaced using a ReplaceMissingValuesFilter, and
  38.  * nominal attributes are transformed into numeric attributes using a
  39.  * NominalToBinaryFilter.<p>
  40.  *
  41.  * Valid options are:<p>
  42.  *
  43.  * -D <br>
  44.  * Turn on debugging output.<p>
  45.  *
  46.  * @author Len Trigg (trigg@cs.waikato.ac.nz)
  47.  * @author Eibe Frank (eibe@cs.waikato.ac.nz)
  48.  * @author Tony Voyle (tv6@cs.waikato.ac.nz)
  49.  * @version $Revision: 1.12.2.1 $ 
  50.  */
  51. public class Logistic extends DistributionClassifier implements OptionHandler {
  52.   /** The log-likelihood of the built model */
  53.   protected double m_LL;
  54.   /** The log-likelihood of the null model */
  55.   protected double m_LLn;
  56.   /** The coefficients of the model */
  57.   protected double [] m_Par;
  58.   /** The number of attributes in the model */
  59.   protected int m_NumPredictors;
  60.   /** The index of the class attribute */
  61.   protected int m_ClassIndex;
  62.   /** The ridge parameter. */
  63.   protected double m_Ridge = 1e-8;
  64.   /** The filter used to make attributes numeric. */
  65.   private NominalToBinaryFilter m_NominalToBinary;
  66.   /** The filter used to get rid of missing values. */
  67.   private ReplaceMissingValuesFilter m_ReplaceMissingValues;
  68.   /** Debugging output */
  69.   protected boolean m_Debug;
  70.   
  71.   private double m_f;
  72.   
  73.   private double[] fvec;
  74.   private int m_nn;
  75.   
  76.   private int m_check;
  77.   
  78.   private double m_ALF = 1.0e-4;
  79.   private double m_TOLX = 1.0e-7;
  80.   
  81.   private double m_TOLMIN = 1.0e-6;
  82.   private double m_STPMX = 100.0;
  83.   private int m_MAXITS = 200;
  84.   
  85.   /**
  86.    * Finds a new point x in the direction p from
  87.    * a point xold at which the value of the function
  88.    * has decreased sufficiently.
  89.    *
  90.    * @param n number of variables
  91.    * @param xold old point
  92.    * @param fold value at that point
  93.    * @param g gtradient at that point
  94.    * @param p direction
  95.    * @param x new value along direction p from xold
  96.    * @param stpmax maximum step length
  97.    * @param X instance data
  98.    * @param Y class values
  99.    * @exception Exception if an error occurs
  100.    */
  101.   public void lnsrch(int n, double[] xold, double fold, double[] g, double[] p, 
  102.      double[] x, double stpmax, double[][] X, double[] Y)
  103.     throws Exception {
  104.     
  105.     
  106.     int i, j, k, outl=0, outx = 0;
  107.     double a,alam,alam2 = 0,alamin,b,disc,f2 = 0,fold2 = 0,rhs1,
  108.       rhs2,slope,sum,temp,test,tmplam;
  109.     m_check = 0;
  110.     for (sum=0.0,i=0;i<=n-1;i++)sum += p[i]*p[i];
  111.     sum = Math.sqrt(sum);
  112.     if (m_Debug)
  113.       System.out.print("fold:   " + Utils.doubleToString(fold,10,7)+ "n");
  114.     if (sum > stpmax)
  115.       for (i=0;i<=n-1;i++) p[i] *= stpmax/sum;
  116.     for (slope=0.0,i=0;i<=n-1;i++)
  117.       slope += g[i]*p[i];
  118.     if (m_Debug)
  119.       System.out.print("slope:  " + Utils.doubleToString(slope,10,7)+ "n");
  120.     test=0.0;
  121.     for (i=0;i<=n-1;i++) {
  122.       temp=Math.abs(p[i])/Math.max(Math.abs(xold[i]),1.0);
  123.       if (temp > test) test=temp;
  124.     }
  125.     alamin = m_TOLX/test;
  126.     if (m_Debug)
  127.       System.out.print("alamin: " + Utils.doubleToString(alamin,10,7)+ "n");
  128.     
  129.     alam=1.0;
  130.     for (k=0;;k++) {
  131.       if (m_Debug)
  132. System.out.print("itteration: " + k + "n");
  133.       for (i=0;i<=n-1;i++) {
  134. x[i]=xold[i]+alam*p[i];
  135.       }
  136.       m_f = fmin(x,X,Y);
  137.       if (m_Debug) {
  138. System.out.print("m_f:    " + 
  139.  Utils.doubleToString(m_f, 10, 7)+ "n");
  140. System.out.print("cLL:    " + 
  141.  Utils.doubleToString(cLL(X,Y,x), 10, 7)+ "n");
  142.       
  143. System.out.print("alam:   " + alam + "n");
  144. System.out.print("max:    " + 
  145.  Utils.doubleToString(fold-m_ALF*alam*slope,10,7)+ "n");
  146.       }
  147.       if (Double.isInfinite(m_f)){
  148. if(m_Debug)
  149.   for (i=0;i<=n-1;i++) {
  150.     System.out.println("x[" + i + "] = " + 
  151.        Utils.doubleToString(x[i], 10, 4));
  152.   }
  153. System.err.println("Infinite");
  154. if(Double.isNaN(alam))
  155.   return;
  156.       }
  157.       if (Double.isInfinite(f2)){
  158. m_check = 2;
  159. return;
  160.       }
  161.       if (alam < alamin) {
  162. for (i=0;i<=n-1;i++) x[i]=xold[i];
  163. m_check=1;
  164. return;
  165.       } else if (m_f <= fold-m_ALF*alam*slope) return;
  166.       else {
  167. if (alam == 1.0)
  168.   tmplam = -1*slope/(2.0*(m_f-fold-slope));
  169. else {
  170.   rhs1 = m_f-fold-alam*slope;
  171.   rhs2 = f2-fold2-alam2*slope;
  172.   a=(rhs1/(alam*alam)-rhs2/(alam*alam2))/(alam-alam2);
  173.   b=(-1*alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
  174.   if (a == 0.0) tmplam = -1*slope/(2.0*b);
  175.   else {
  176.     disc=b*b-3.0*a*slope;
  177.     if (disc < 0.0) disc = disc*-1;
  178.     tmplam=(-1*b+Math.sqrt(disc))/(3.0*a);
  179.   }
  180.   if (m_Debug)
  181.     System.out.print("newstuff: n" + 
  182.      "a:   " + Utils.doubleToString(alam,10,7)+ "n" +
  183.      "b:   " + Utils.doubleToString(alam,10,7)+ "n" +
  184.      "disc:   " + Utils.doubleToString(alam,10,7)+ "n" +
  185.      "tmplam:   " + alam + "n" +
  186.      "alam:   " + Utils.doubleToString(alam,10,7)+ "n");
  187.   if (tmplam>0.5*alam)
  188.     tmplam=0.5*alam;
  189. }
  190.       }
  191.       alam2=alam;
  192.       f2=m_f;
  193.       fold2=fold;
  194.       alam=Math.max(tmplam,0.1*alam);
  195.     }
  196.   }
  197.     
  198.   /**
  199.    * Globaly convergent Newtons method utilising 
  200.    * line search and backtracking
  201.    *
  202.    * @param x initial point
  203.    * @param n dimension of point
  204.    * @param X instance data
  205.    * @param Y class values
  206.    * @exception Exception if an error occurs
  207.    */
  208.   private void newtn(double[] x, int n, double[][] X, double[] Y ) 
  209.     throws Exception{
  210.     
  211.     int i,its,j,indx[],startnum;;
  212.     double pLL,d,den,f,fold,stpmax,sum,temp,test,g[],p[],xold[];
  213.      
  214.     indx = new int[n];
  215.     Matrix fjac = new Matrix(n,n);
  216.     fvec = new double[n];
  217.     g = new double[n];
  218.     p = new double[n];
  219.     xold = new double[n];
  220.     m_nn = n;
  221.     m_f = fmin(x, X,Y);
  222.     
  223.     for (sum=0.0,i=0;i<=n-1;i++) sum+=x[i]*x[i];
  224.     stpmax=m_STPMX*Math.max(Math.sqrt(sum),(float)n);
  225.     m_LL=calculateLogLikelihood(X,Y,fjac,fvec);
  226.     
  227.     for (its=1,startnum=0;its<=m_MAXITS;its++) {
  228.       pLL=m_LL;
  229.       if (m_Debug) {
  230. System.out.println("n-2 Log Likelihood = " 
  231.    + Utils.doubleToString(m_LL, 10, 5)
  232.    + ((m_LLn == m_LL) ? " (Null Model)" : ""));
  233. System.err.println("-2 Log Likelihood = " 
  234.    + Utils.doubleToString(m_LL, 10, 5)
  235.    + ((m_LLn == m_LL) ? " (Null Model)" : ""));
  236.       }
  237.       for (i=1;i<=n-1;i++) {
  238. for (sum=0.0,j=0;j<=n-1;j++) sum+=fjac.getElement(j,i)*fvec[i];
  239. g[i]=sum;
  240.       }
  241.       for (i=0;i<=n-1;i++) xold[i]=x[i];
  242.       fold=m_f;
  243.       for (i=0;i<=n-1;i++) p[i] = fvec[i];
  244.       fjac.lubksb(fjac.ludcmp(),p);
  245.       lnsrch(n,xold,fold,g,p,x,stpmax,X,Y);
  246.       for (j = 0; j < x.length; j++) { 
  247. m_Par[j] = x[j];
  248.       }
  249.       m_LL = calculateLogLikelihood(X, Y, fjac, fvec);
  250.       if(Math.abs(pLL-m_LL) < 0.00001)
  251. return;
  252.       
  253.     }
  254.     throw new Exception("MAXITS exceeded in newtn");
  255.   }
  256.   /**
  257.    * returns 1/2*cLL^2 at x
  258.    *
  259.    * @param x a point
  260.    * @param X instance data
  261.    * @param Y class values
  262.    * @return function value
  263.    */
  264.   private double fmin( double[] x, double[][] X, double[] Y){
  265.     int i;
  266.     double sum;
  267.     sum=cLL(X,Y,x);
  268.     return sum * sum * 0.5;
  269.   }
  270.    
  271.   /**
  272.    * Returns probability.
  273.    */
  274.   protected static double Norm(double z) { 
  275.     return Statistics.chiSquaredProbability(z * z, 1);
  276.   }
  277.   /**
  278.    * Evaluate the probability for this point using the current coefficients
  279.    *
  280.    * @param instDat the instance data
  281.    * @return the probability for this instance
  282.    */
  283.   protected double evaluateProbability(double [] instDat) {
  284.     double v = m_Par[0];
  285.     for(int k = 1; k <= m_NumPredictors; k++)
  286. v += m_Par[k] * instDat[k];
  287.     v = 1 / (1 + Math.exp(-v));
  288.     return v;
  289.   }
  290.   /**
  291.    * Returns the -2 Log likelihood with parameters in x
  292.    *
  293.    * @param X instance data
  294.    * @param Y class values
  295.    * @param x a point
  296.    * @return function value
  297.    */
  298.   private double cLL(double[][] X, double[] Y, double[] x) {
  299.   
  300.     double LL = 0;
  301.     for (int i=0; i < X.length; i++) {
  302.       
  303.       double v = x[0];
  304.       for(int k = 1; k <= m_NumPredictors; k++)
  305. v += x[k] * X[i][k];
  306.       v = 1 / (1 + Math.exp(-v));
  307.       if ( v == 1.0 || v == 0.0 ) continue; 
  308.       // Update the log-likelihood of this set of coefficients
  309.       if( Y[i]==1 ) {
  310. LL = LL - 2 * Math.log(v);
  311.       } else {
  312. LL = LL - 2 * Math.log(1 - v);
  313.       }
  314.     }
  315.     return LL;
  316.   }
  317.   /**
  318.    * Calculates the log likelihood of the current set of
  319.    * coefficients (stored in m_Par), given the data.
  320.    *
  321.    * @param X the instance data
  322.    * @param Y the class values for each instance
  323.    * @param jacobian the matrix which will contain the jacobian matrix after
  324.    * the method returns
  325.    * @param deltas an array which will contain the parameter adjustments after
  326.    * the method returns
  327.    * @return the log likelihood of the data.
  328.    */
  329.   protected double calculateLogLikelihood(double [][] X, double [] Y, 
  330.   Matrix jacobian, double [] deltas) {
  331.     
  332.     double LL = 0;
  333.     double [][] Arr = new double [jacobian.numRows()][jacobian.numRows()];
  334.    
  335.     for (int j = 0; j < Arr.length; j++) {
  336.       for (int k = 0; k < Arr.length; k++) {
  337. Arr[j][k] = 0;
  338.       }
  339.       deltas[j] = 0;
  340.     }
  341.     // For each data point
  342.     for (int i = 0; i < X.length; i++) {
  343.       // Evaluate the probability for this point using the current coefficients
  344.       double p = evaluateProbability(X[i]);
  345.       if (p == 1.0 || p == 0.0) continue;
  346.       // Update the log-likelihood of this set of coefficients
  347.       if( Y[i]==1 ) {
  348. LL = LL - 2 * Math.log(p);
  349.       } else {
  350. LL = LL - 2 * Math.log(1 - p);
  351.       }
  352.       double w = p * (1 - p);     // Weight
  353.       double z = (Y[i] - p);      // The error of this prediction
  354.       for (int j = 0; j < Arr.length; j++) {
  355. double xij = X[i][j];
  356. deltas[j] += xij * z;
  357. for (int k = j; k < Arr.length; k++) {
  358.   Arr[j][k] += xij * X[i][k] * w;
  359. }
  360.       }
  361.     }
  362.     // Add ridge adjustment to first derivative
  363.     for (int j = 0; j < m_Par.length; j++) {
  364.       deltas[j] -= 2 * m_Ridge * m_Par[j];
  365.     }
  366.     // Add ridge adjustment to second derivative
  367.     for (int j = 0; j < Arr.length; j++) {
  368.       Arr[j][j] += 2 * m_Ridge;
  369.     }
  370.     // Fill out the rest of the array
  371.     for (int j = 1; j < Arr.length; j++) {
  372.       for (int k = 0; k < j; k++) {
  373. Arr[j][k] = Arr[k][j];
  374.       }
  375.     }
  376.     for (int j = 0; j < Arr.length; j++) {
  377.       jacobian.setRow(j, Arr[j]);
  378.     }
  379.     return LL;
  380.   }
  381.   /**
  382.    * Returns an enumeration describing the available options
  383.    *
  384.    * @return an enumeration of all the available options
  385.    */
  386.   public Enumeration listOptions() {
  387.     Vector newVector = new Vector(1);
  388.     newVector.addElement(new Option(
  389.               "tTurn on debugging output.",
  390.               "D", 0, "-D"));
  391.     return newVector.elements();
  392.   }
  393.   /**
  394.    * Parses a given list of options. Valid options are:<p>
  395.    *
  396.    * -D <br>
  397.    * Turn on debugging output.<p>
  398.    *
  399.    * @param options the list of options as an array of strings
  400.    * @exception Exception if an option is not supported
  401.    */
  402.   public void setOptions(String[] options) throws Exception {
  403.     setDebug(Utils.getFlag('D', options));
  404.   }
  405.   /**
  406.    * Gets the current settings of the classifier.
  407.    *
  408.    * @return an array of strings suitable for passing to setOptions
  409.    */
  410.   public String [] getOptions() {
  411.     String [] options = new String [1];
  412.     int current = 0;
  413.     if (getDebug()) {
  414.       options[current++] = "-D";
  415.     }
  416.     while (current < options.length) {
  417.       options[current++] = "";
  418.     }
  419.     return options;
  420.   }
  421.   /**
  422.    * Sets whether debugging output will be printed.
  423.    *
  424.    * @param debug true if debugging output should be printed
  425.    */
  426.   public void setDebug(boolean debug) {
  427.     m_Debug = debug;
  428.   }
  429.   /**
  430.    * Gets whether debugging output will be printed.
  431.    *
  432.    * @return true if debugging output will be printed
  433.    */
  434.   public boolean getDebug() {
  435.     return m_Debug;
  436.   }
  437.   /**
  438.    * Builds the classifier
  439.    *
  440.    * @param data the training data to be used for generating the
  441.    * boosted classifier.
  442.    * @exception Exception if the classifier could not be built successfully
  443.    */
  444.   public void buildClassifier(Instances train) throws Exception {
  445.     if (train.classAttribute().type() != Attribute.NOMINAL) {
  446.       throw new Exception("Class attribute must be nominal.");
  447.     }
  448.     if (train.classAttribute().numValues() != 2) {
  449.       throw new Exception("Only 2-class problems allowed.");
  450.     }
  451.     if (train.checkForStringAttributes()) {
  452.       throw new Exception("Can't handle string attributes!");
  453.     }
  454.     train = new Instances(train);
  455.     train.deleteWithMissingClass();
  456.     if (train.numInstances() == 0) {
  457.       throw new Exception("No train instances without missing class value!");
  458.     }
  459.     m_ReplaceMissingValues = new ReplaceMissingValuesFilter();
  460.     m_ReplaceMissingValues.setInputFormat(train);
  461.     train = Filter.useFilter(train, m_ReplaceMissingValues);
  462.     m_NominalToBinary = new NominalToBinaryFilter();
  463.     m_NominalToBinary.setInputFormat(train);
  464.     train = Filter.useFilter(train, m_NominalToBinary);
  465.     m_ClassIndex = train.classIndex();
  466.     int nR = m_NumPredictors = train.numAttributes() - 1;
  467.     int nC = train.numInstances();
  468.     double [][] X  = new double [nC][nR + 1];       // Data values
  469.     double [] Y    = new double [nC];               // Class values
  470.     double [] xMean= new double [nR + 1];           // Attribute means
  471.     double [] xSD  = new double [nR + 1];           // Attribute stddev's
  472.     double sY0 = 0;                                 // Number of class 0
  473.     double sY1 = 0;                                 // Number of class 1
  474.     if (m_Debug) {
  475.       System.out.println("Extracting data...");
  476.     }
  477.     for (int i = 0; i < X.length; i++) {
  478.       Instance current = train.instance(i);
  479.       X[i][0] = 1;
  480.       int j = 1;
  481.       for (int k = 0; k <= nR; k++) {
  482. if (k != m_ClassIndex) {
  483.   double x = current.value(k);
  484.   X[i][j] = x;
  485.   xMean[j] = xMean[j] + x;
  486.   xSD[j] = xSD[j] + x*x;
  487.   j++;
  488. }
  489.       }
  490.       Y[i] = current.classValue();
  491.       if (Y[i] != 0) {
  492. Y[i] = 1;
  493.       }
  494.       if (Y[i] == 0) {
  495. sY0 = sY0 + 1;
  496.       } else {
  497. sY1 = sY1 + 1;
  498.       }
  499.     }
  500.     xMean[0] = 0; xSD[0] = 1;
  501.     for (int j = 1; j <= nR; j++) {
  502.       xMean[j] = xMean[j] / nC;
  503.       xSD[j] = xSD[j] / nC;
  504.       xSD[j] = Math.sqrt(Math.abs( xSD[j] - xMean[j] * xMean[j]));
  505.     }
  506.     if (m_Debug) {
  507.       // Output stats about input data
  508.       System.out.println("Descriptives...");
  509.       System.out.println("" + sY0 + " cases have Y=0; " 
  510.  + sY1 + " cases have Y=1.");
  511.       System.out.println("n Variable     Avg       SD    ");
  512.       for (int j = 1; j <= nR; j++) 
  513. System.out.println(Utils.doubleToString(j,8,4) 
  514.    + Utils.doubleToString(xMean[j], 10, 4) 
  515.    + Utils.doubleToString(xSD[j], 10, 4)
  516.    );
  517.     }
  518.     
  519.     // Normalise input data and remove ignored attributes
  520.     for (int i = 0; i < nC; i++) {
  521.       for (int j = 0; j <= nR; j++) {
  522. if (xSD[j] != 0) {
  523.   X[i][j] = (X[i][j] - xMean[j]) / xSD[j];
  524. }
  525.       }
  526.     }
  527.     if (m_Debug) {
  528.       System.out.println("nIteration History..." );
  529.     }
  530.     m_Par = new double [nR + 1];    // Coefficients
  531.     double LLp = 2e+10;             // Log-likelihood on previous iteration
  532.     m_LL  = 1e+10;                  // Log-likelihood of current iteration
  533.     double [] deltas = new double [nR + 1];
  534.     Matrix jacobian = new Matrix(nR + 1, nR + 1);
  535.     // Set up parameters for null model
  536.     m_Par[0] = Math.log((sY1+1) / (sY0+1));
  537.     for (int j = 1; j < m_Par.length; j++) {
  538.       m_Par[j] = 0;
  539.     }
  540.     
  541.     m_LLn = cLL(X, Y, m_Par);        // Log-likelihood of null hypothesis
  542.     double x[] = new double[m_Par.length];
  543.     for (int q=0; q < x.length;q++) x[q] = 0;
  544.     newtn(x,x.length,X,Y);
  545.     if (m_Debug) {
  546.       System.out.println(" (Converged)");
  547.     }
  548.     // Convert coefficients back to non-normalized attribute units
  549.     for(int j = 1; j <= nR; j++) {
  550.       if (xSD[j] != 0) {
  551. m_Par[j] = m_Par[j] / xSD[j];
  552. m_Par[0] = m_Par[0] - m_Par[j] * xMean[j];
  553.       }
  554.     }
  555.   }
  556.   
  557.   /**
  558.    * Computes the distribution for a given instance
  559.    *
  560.    * @param instance the instance for which distribution is computed
  561.    * @return the distribution
  562.    * @exception Exception if the distribution can't be computed successfully
  563.    */
  564.   public double [] distributionForInstance(Instance instance) 
  565.     throws Exception {
  566.     m_ReplaceMissingValues.input(instance);
  567.     instance = m_ReplaceMissingValues.output();
  568.     m_NominalToBinary.input(instance);
  569.     instance = m_NominalToBinary.output();
  570.     // Extract the predictor columns into an array
  571.     double [] instDat = new double [m_NumPredictors + 1];
  572.     int j = 1;
  573.     for (int k = 0; k <= m_NumPredictors; k++) {
  574.       if (k != m_ClassIndex) {
  575. instDat[j++] = instance.value(k);
  576.       }
  577.     }
  578.     double [] distribution = new double [2];
  579.     distribution[1] = evaluateProbability(instDat);
  580.     distribution[0] = 1.0-distribution[1];
  581.     return distribution;
  582.   }
  583.   /**
  584.    * Gets a string describing the classifier.
  585.    *
  586.    * @return a string describing the classifer built.
  587.    */
  588.   public String toString() {
  589.     double CSq = m_LLn - m_LL;
  590.     int df = m_NumPredictors;
  591.     String result = "Logistic Regression (2 classes)";
  592.     if (m_Par == null) {
  593.       return result + ": No model built yet.";
  594.     }
  595.     result += "nnOverall Model Fit...n" 
  596.       +"  Chi Square=" + Utils.doubleToString(CSq, 10, 4) 
  597.       + ";  df=" + df
  598.       + ";  p=" 
  599.       + Utils.doubleToString(Statistics.chiSquaredProbability(CSq, df), 10, 2)
  600.       + "n";
  601.     
  602.     result += "nCoefficients...n"
  603.       + "Variable      Coeff.n";
  604.     for (int j = 1; j <= m_NumPredictors; j++) {
  605.       result += Utils.doubleToString(j, 8, 0) 
  606.       + Utils.doubleToString(m_Par[j], 12, 4) 
  607.       + "n";
  608.     }
  609.     result += "Intercept " + Utils.doubleToString(m_Par[0], 10, 4) + "n";
  610.     
  611.     result += "nOdds Ratios...n"
  612.       + "Variable         O.R.n";
  613.     for (int j = 1; j <= m_NumPredictors; j++) {
  614.       double ORc = Math.exp( m_Par[j] );
  615.       result += Utils.doubleToString(j, 8, 0) 
  616. + " " 
  617. + ((ORc > 1e10) ?  "" + ORc : Utils.doubleToString(ORc, 12, 4))
  618. + "n";
  619.     }
  620.     return result;
  621.   }
  622.   /**
  623.    * Main method for testing this class.
  624.    *
  625.    * @param argv should contain the command line arguments to the
  626.    * scheme (see Evaluation)
  627.    */
  628.   public static void main(String [] argv) {
  629.     try {
  630.       System.out.println(Evaluation.evaluateModel(new Logistic(), argv));
  631.     } catch (Exception e) {
  632.       e.printStackTrace();
  633.       System.err.println(e.getMessage());
  634.     }
  635.   }
  636. }