/** * Process a classifier's prediction for an instance and update a set of plotting instances and * additional plotting info. m_PlotShape for nominal class datasets holds shape types (actual data * points have automatic shape type assignment; classifier error data points have box shape type). * For numeric class datasets, the actual data points are stored in m_PlotInstances and m_PlotSize * stores the error (which is later converted to shape size values). * * @param toPredict the actual data point * @param classifier the classifier * @param eval the evaluation object to use for evaluating the classifier on the instance to * predict * @see #m_PlotShapes * @see #m_PlotSizes * @see #m_PlotInstances */ public void process(Instance toPredict, Classifier classifier, Evaluation eval) { double pred; double[] values; int i; try { pred = eval.evaluateModelOnceAndRecordPrediction(classifier, toPredict); if (classifier instanceof weka.classifiers.misc.InputMappedClassifier) { toPredict = ((weka.classifiers.misc.InputMappedClassifier) classifier) .constructMappedInstance(toPredict); } if (!m_SaveForVisualization) return; if (m_PlotInstances != null) { values = new double[m_PlotInstances.numAttributes()]; for (i = 0; i < m_PlotInstances.numAttributes(); i++) { if (i < toPredict.classIndex()) { values[i] = toPredict.value(i); } else if (i == toPredict.classIndex()) { values[i] = pred; values[i + 1] = toPredict.value(i); i++; } else { values[i] = toPredict.value(i - 1); } } m_PlotInstances.add(new DenseInstance(1.0, values)); if (toPredict.classAttribute().isNominal()) { if (toPredict.isMissing(toPredict.classIndex()) || Utils.isMissingValue(pred)) { m_PlotShapes.addElement(new Integer(Plot2D.MISSING_SHAPE)); } else if (pred != toPredict.classValue()) { // set to default error point shape m_PlotShapes.addElement(new Integer(Plot2D.ERROR_SHAPE)); } else { // otherwise set to constant (automatically assigned) point shape m_PlotShapes.addElement(new Integer(Plot2D.CONST_AUTOMATIC_SHAPE)); } m_PlotSizes.addElement(new Integer(Plot2D.DEFAULT_SHAPE_SIZE)); } else { // store the error (to be converted to a point size later) Double errd = null; if (!toPredict.isMissing(toPredict.classIndex()) && !Utils.isMissingValue(pred)) { errd = new Double(pred - toPredict.classValue()); m_PlotShapes.addElement(new Integer(Plot2D.CONST_AUTOMATIC_SHAPE)); } else { // missing shape if actual class not present or prediction is missing m_PlotShapes.addElement(new Integer(Plot2D.MISSING_SHAPE)); } m_PlotSizes.addElement(errd); } } } catch (Exception ex) { ex.printStackTrace(); } }
/** * Use <code> classifyInstance </code> from <code> OSDLCore </code> and assign probability one to * the chosen label. The implementation is heavily based on the same method in the <code> * Classifier </code> class. * * @param instance the instance to be classified * @return an array containing a single '1' on the index that <code> classifyInstance </code> * returns. */ public double[] distributionForInstance(Instance instance) { // based on the code from the Classifier class double[] dist = new double[instance.numClasses()]; int classification = 0; switch (instance.classAttribute().type()) { case Attribute.NOMINAL: try { classification = (int) Math.round(classifyInstance(instance)); } catch (Exception e) { System.out.println("There was a problem with classifyIntance"); System.out.println(e.getMessage()); e.printStackTrace(); } if (Utils.isMissingValue(classification)) { return dist; } dist[classification] = 1.0; return dist; case Attribute.NUMERIC: try { dist[0] = classifyInstance(instance); } catch (Exception e) { System.out.println("There was a problem with classifyIntance"); System.out.println(e.getMessage()); e.printStackTrace(); } return dist; default: return dist; } }
protected void searchMedian(Instances instances) { medians = new double[instances.numAttributes()]; imputations = new int[instances.numAttributes()]; for (int j = 0; j < instances.numAttributes(); ++j) { int numPresentValues = 0; if (instances.attribute(j).isNumeric()) { double[] values = new double[instances.numInstances()]; for (int i = 0; i < instances.numInstances(); ++i) { Instance current = instances.get(i); if (Utils.isMissingValue(current.value(j)) == false) { values[numPresentValues] = current.value(j); numPresentValues += 1; } } if (numPresentValues > 0) { double[] goodValues = Arrays.copyOf(values, numPresentValues); Median median = new Median(); medians[j] = median.evaluate(goodValues); } } } for (int j = 0; j < instances.numAttributes(); ++j) { if (instances.attribute(j).isNumeric()) { Conversion.log( "OK", "Impute Numeric", "Attribute " + instances.attribute(j) + " - Median: " + medians[j]); } } }
/** * Gets the raw output from the classifier * * @return the raw output from the classifier */ public String getRawResultOutput() { StringBuffer result = new StringBuffer(); if (m_Classifier == null) { return "<null> classifier"; } result.append(toString()); result.append("Classifier model: \n" + m_Classifier.toString() + '\n'); // append the performance statistics if (m_result != null) { result.append(m_result); if (m_doesProduce != null) { for (int i = 0; i < m_doesProduce.length; i++) { if (m_doesProduce[i]) { try { double dv = ((AdditionalMeasureProducer) m_Classifier).getMeasure(m_AdditionalMeasures[i]); if (!Utils.isMissingValue(dv)) { Double value = new Double(dv); result.append(m_AdditionalMeasures[i] + " : " + value + '\n'); } else { result.append(m_AdditionalMeasures[i] + " : " + '?' + '\n'); } } catch (Exception ex) { System.err.println(ex); } } } } } return result.toString(); }
/** * Convert an <code>Instance</code> to an array of values that matches the format of the mining * schema. First maps raw attribute values and then applies rules for missing values, outliers * etc. * * @param inst the <code>Instance</code> to convert * @param miningSchema the mining schema incoming instance attributes * @return an array of doubles that are values from the incoming Instances, correspond to the * format of the mining schema and have had missing values, outliers etc. dealt with. * @throws Exception if something goes wrong */ public double[] instanceToSchema(Instance inst, MiningSchema miningSchema) throws Exception { Instances miningSchemaI = miningSchema.getMiningSchemaAsInstances(); // allocate enough space for both mining schema fields and any derived fields double[] result = new double[miningSchema.getFieldsAsInstances().numAttributes()]; // Copy over the values for (int i = 0; i < miningSchemaI.numAttributes(); i++) { // if (miningSchemaI.attribute(i).isNumeric()) { result[i] = inst.value(m_fieldsMap[i]); if (miningSchemaI.attribute(i).isNominal() || miningSchemaI.attribute(i).isString()) { // If not missing, look up the index of this incoming categorical value in // the mining schema if (!Utils.isMissingValue(inst.value(m_fieldsMap[i]))) { int[] valueMap = m_nominalValueMaps[i]; int index = valueMap[(int) inst.value(m_fieldsMap[i])]; String incomingAttValue = inst.attribute(m_fieldsMap[i]).value((int) inst.value(m_fieldsMap[i])); /*int index = miningSchemaI.attribute(i).indexOfValue(incomingAttValue); */ if (index >= 0) { result[i] = index; } else { // set this to "unknown" (-1) for nominal valued attributes result[i] = UNKNOWN_NOMINAL_VALUE; String warningString = "[MappingInfo] WARNING: Can't match nominal value " + incomingAttValue; if (m_log != null) { m_log.logMessage(warningString); } else { System.err.println(warningString); } } } } } // Now deal with missing values and outliers... miningSchema.applyMissingAndOutlierTreatments(result); // printInst(result); // now fill in any derived values ArrayList<DerivedFieldMetaInfo> derivedFields = miningSchema.getDerivedFields(); for (int i = 0; i < derivedFields.size(); i++) { DerivedFieldMetaInfo temp = derivedFields.get(i); // System.err.println("Applying : " + temp); double r = temp.getDerivedValue(result); result[i + miningSchemaI.numAttributes()] = r; } /*System.err.print("==> "); for (int i = 0; i < result.length; i++) { System.err.print(" " + result[i]); } System.err.println();*/ return result; }
protected void imputeMedian(Instances instances) { Attribute indicator = instances.attribute(ATTNAME_INDICATOR); for (int i = 0; i < instances.numInstances(); ++i) { Instance current = instances.get(i); current.setValue(indicator, 0.0); // 0.0 means "false" for (int j = 0; j < instances.numAttributes(); ++j) { if (instances.attribute(j).isNumeric() == false) { continue; } if (Utils.isMissingValue(current.value(j))) { current.setValue(j, medians[j]); current.setValue(indicator, 1.0); imputations[j] += 1; } } } }
/** * Predicts the class memberships for a given instance. If an instance is unclassified, the * returned array elements must be all zero. If the class is numeric, the array must consist of * only one element, which contains the predicted value. Note that a classifier MUST implement * either this or classifyInstance(). * * @param instance the instance to be classified * @return an array containing the estimated membership probabilities of the test instance in each * class or the numeric prediction * @exception Exception if distribution could not be computed successfully */ @Override public double[] distributionForInstance(Instance instance) throws Exception { double[] dist = new double[instance.numClasses()]; switch (instance.classAttribute().type()) { case Attribute.NOMINAL: double classification = classifyInstance(instance); if (Utils.isMissingValue(classification)) { return dist; } else { dist[(int) classification] = 1.0; } return dist; case Attribute.NUMERIC: case Attribute.DATE: dist[0] = classifyInstance(instance); return dist; default: return dist; } }
/** * Outputs a tree at a certain level. * * @param level the level at which the tree is to be printed * @return the tree as string at the given level */ private String toString(int level) { StringBuffer text = new StringBuffer(); if (m_Attribute == null) { if (Utils.isMissingValue(m_ClassValue)) { text.append(": null"); } else { text.append(": " + m_ClassAttribute.value((int) m_ClassValue)); } } else { for (int j = 0; j < m_Attribute.numValues(); j++) { text.append("\n"); for (int i = 0; i < level; i++) { text.append("| "); } text.append(m_Attribute.name() + " = " + m_Attribute.value(j)); text.append(m_Successors[j].toString(level + 1)); } } return text.toString(); }
/** * Signify that this batch of input to the filter is finished. If the filter requires all * instances prior to filtering, output() may now be called to retrieve the filtered instances. * * @return true if there are instances pending output * @throws Exception if an error occurs * @throws IllegalStateException if no input structure has been defined */ public boolean batchFinished() throws Exception { if (getInputFormat() == null) throw new IllegalStateException("No input instance format defined"); if (m_MinArray == null) { Instances input = getInputFormat(); // Compute minimums and maximums m_MinArray = new double[input.numAttributes()]; m_MaxArray = new double[input.numAttributes()]; for (int i = 0; i < input.numAttributes(); i++) m_MinArray[i] = Double.NaN; for (int j = 0; j < input.numInstances(); j++) { double[] value = input.instance(j).toDoubleArray(); for (int i = 0; i < input.numAttributes(); i++) { if (input.attribute(i).isNumeric() && (input.classIndex() != i)) { if (!Utils.isMissingValue(value[i])) { if (Double.isNaN(m_MinArray[i])) { m_MinArray[i] = m_MaxArray[i] = value[i]; } else { if (value[i] < m_MinArray[i]) m_MinArray[i] = value[i]; if (value[i] > m_MaxArray[i]) m_MaxArray[i] = value[i]; } } } } } // Convert pending input instances for (int i = 0; i < input.numInstances(); i++) convertInstance(input.instance(i)); } // Free memory flushInput(); m_NewBatch = true; return (numPendingOutput() != 0); }
/** * Convert a single instance over. The converted instance is added to the end of the output queue. * * @param instance the instance to convert * @throws Exception if conversion fails */ protected void convertInstance(Instance instance) throws Exception { Instance inst = null; if (instance instanceof SparseInstance) { double[] newVals = new double[instance.numAttributes()]; int[] newIndices = new int[instance.numAttributes()]; double[] vals = instance.toDoubleArray(); int ind = 0; for (int j = 0; j < instance.numAttributes(); j++) { double value; if (instance.attribute(j).isNumeric() && (!Utils.isMissingValue(vals[j])) && (getInputFormat().classIndex() != j)) { if (Double.isNaN(m_MinArray[j]) || (m_MaxArray[j] == m_MinArray[j])) { value = 0; } else { value = (vals[j] - m_MinArray[j]) / (m_MaxArray[j] - m_MinArray[j]) * m_Scale + m_Translation; if (Double.isNaN(value)) { throw new Exception( "A NaN value was generated " + "while normalizing " + instance.attribute(j).name()); } } if (value != 0.0) { newVals[ind] = value; newIndices[ind] = j; ind++; } } else { value = vals[j]; if (value != 0.0) { newVals[ind] = value; newIndices[ind] = j; ind++; } } } double[] tempVals = new double[ind]; int[] tempInd = new int[ind]; System.arraycopy(newVals, 0, tempVals, 0, ind); System.arraycopy(newIndices, 0, tempInd, 0, ind); inst = new SparseInstance(instance.weight(), tempVals, tempInd, instance.numAttributes()); } else { double[] vals = instance.toDoubleArray(); for (int j = 0; j < getInputFormat().numAttributes(); j++) { if (instance.attribute(j).isNumeric() && (!Utils.isMissingValue(vals[j])) && (getInputFormat().classIndex() != j)) { if (Double.isNaN(m_MinArray[j]) || (m_MaxArray[j] == m_MinArray[j])) { vals[j] = 0; } else { vals[j] = (vals[j] - m_MinArray[j]) / (m_MaxArray[j] - m_MinArray[j]) * m_Scale + m_Translation; if (Double.isNaN(vals[j])) { throw new Exception( "A NaN value was generated " + "while normalizing " + instance.attribute(j).name()); } } } } inst = new DenseInstance(instance.weight(), vals); } inst.setDataset(instance.dataset()); push(inst); }
/** * Convert a single instance over. The converted instance is added to the end of the output queue. * * @param instance the instance to convert * @throws Exception if instance cannot be converted */ private void convertInstance(Instance instance) throws Exception { Instance inst = null; HashMap symbols = new HashMap(5); if (instance instanceof SparseInstance) { double[] newVals = new double[instance.numAttributes()]; int[] newIndices = new int[instance.numAttributes()]; double[] vals = instance.toDoubleArray(); int ind = 0; double value; for (int j = 0; j < instance.numAttributes(); j++) { if (m_SelectCols.isInRange(j)) { if (instance.attribute(j).isNumeric() && (!Utils.isMissingValue(vals[j])) && (getInputFormat().classIndex() != j)) { symbols.put("A", new Double(vals[j])); symbols.put("MAX", new Double(m_attStats[j].numericStats.max)); symbols.put("MIN", new Double(m_attStats[j].numericStats.min)); symbols.put("MEAN", new Double(m_attStats[j].numericStats.mean)); symbols.put("SD", new Double(m_attStats[j].numericStats.stdDev)); symbols.put("COUNT", new Double(m_attStats[j].numericStats.count)); symbols.put("SUM", new Double(m_attStats[j].numericStats.sum)); symbols.put("SUMSQUARED", new Double(m_attStats[j].numericStats.sumSq)); value = eval(symbols); if (Double.isNaN(value) || Double.isInfinite(value)) { System.err.println("WARNING:Error in evaluating the expression: missing value set"); value = Utils.missingValue(); } if (value != 0.0) { newVals[ind] = value; newIndices[ind] = j; ind++; } } } else { value = vals[j]; if (value != 0.0) { newVals[ind] = value; newIndices[ind] = j; ind++; } } } double[] tempVals = new double[ind]; int[] tempInd = new int[ind]; System.arraycopy(newVals, 0, tempVals, 0, ind); System.arraycopy(newIndices, 0, tempInd, 0, ind); inst = new SparseInstance(instance.weight(), tempVals, tempInd, instance.numAttributes()); } else { double[] vals = instance.toDoubleArray(); for (int j = 0; j < getInputFormat().numAttributes(); j++) { if (m_SelectCols.isInRange(j)) { if (instance.attribute(j).isNumeric() && (!Utils.isMissingValue(vals[j])) && (getInputFormat().classIndex() != j)) { symbols.put("A", new Double(vals[j])); symbols.put("MAX", new Double(m_attStats[j].numericStats.max)); symbols.put("MIN", new Double(m_attStats[j].numericStats.min)); symbols.put("MEAN", new Double(m_attStats[j].numericStats.mean)); symbols.put("SD", new Double(m_attStats[j].numericStats.stdDev)); symbols.put("COUNT", new Double(m_attStats[j].numericStats.count)); symbols.put("SUM", new Double(m_attStats[j].numericStats.sum)); symbols.put("SUMSQUARED", new Double(m_attStats[j].numericStats.sumSq)); vals[j] = eval(symbols); if (Double.isNaN(vals[j]) || Double.isInfinite(vals[j])) { System.err.println("WARNING:Error in Evaluation the Expression: missing value set"); vals[j] = Utils.missingValue(); } } } } inst = new DenseInstance(instance.weight(), vals); } inst.setDataset(instance.dataset()); push(inst); }
/** * Evaluates a feature subset by cross validation * * @param feature_set the subset to be evaluated * @param num_atts the number of attributes in the subset * @return the estimated accuracy * @throws Exception if subset can't be evaluated */ protected double estimatePerformance(BitSet feature_set, int num_atts) throws Exception { m_evaluation = new Evaluation(m_theInstances); int i; int[] fs = new int[num_atts]; double[] instA = new double[num_atts]; int classI = m_theInstances.classIndex(); int index = 0; for (i = 0; i < m_numAttributes; i++) { if (feature_set.get(i)) { fs[index++] = i; } } // create new hash table m_entries = new Hashtable((int) (m_theInstances.numInstances() * 1.5)); // insert instances into the hash table for (i = 0; i < m_numInstances; i++) { Instance inst = m_theInstances.instance(i); for (int j = 0; j < fs.length; j++) { if (fs[j] == classI) { instA[j] = Double.MAX_VALUE; // missing for the class } else if (inst.isMissing(fs[j])) { instA[j] = Double.MAX_VALUE; } else { instA[j] = inst.value(fs[j]); } } insertIntoTable(inst, instA); } if (m_CVFolds == 1) { // calculate leave one out error for (i = 0; i < m_numInstances; i++) { Instance inst = m_theInstances.instance(i); for (int j = 0; j < fs.length; j++) { if (fs[j] == classI) { instA[j] = Double.MAX_VALUE; // missing for the class } else if (inst.isMissing(fs[j])) { instA[j] = Double.MAX_VALUE; } else { instA[j] = inst.value(fs[j]); } } evaluateInstanceLeaveOneOut(inst, instA); } } else { m_theInstances.randomize(m_rr); m_theInstances.stratify(m_CVFolds); // calculate 10 fold cross validation error for (i = 0; i < m_CVFolds; i++) { Instances insts = m_theInstances.testCV(m_CVFolds, i); evaluateFoldCV(insts, fs); } } switch (m_evaluationMeasure) { case EVAL_DEFAULT: if (m_classIsNominal) { return m_evaluation.pctCorrect(); } return -m_evaluation.rootMeanSquaredError(); case EVAL_ACCURACY: return m_evaluation.pctCorrect(); case EVAL_RMSE: return -m_evaluation.rootMeanSquaredError(); case EVAL_MAE: return -m_evaluation.meanAbsoluteError(); case EVAL_AUC: double[] classPriors = m_evaluation.getClassPriors(); Utils.normalize(classPriors); double weightedAUC = 0; for (i = 0; i < m_theInstances.classAttribute().numValues(); i++) { double tempAUC = m_evaluation.areaUnderROC(i); if (!Utils.isMissingValue(tempAUC)) { weightedAUC += (classPriors[i] * tempAUC); } else { System.err.println("Undefined AUC!!"); } } return weightedAUC; } // shouldn't get here return 0.0; }
/** * Gets the results for the supplied train and test datasets. Now performs a deep copy of the * classifier before it is built and evaluated (just in case the classifier is not initialized * properly in buildClassifier()). * * @param train the training Instances. * @param test the testing Instances. * @return the results stored in an array. The objects stored in the array may be Strings, * Doubles, or null (for the missing value). * @throws Exception if a problem occurs while getting the results */ public Object[] getResult(Instances train, Instances test) throws Exception { if (train.classAttribute().type() != Attribute.NUMERIC) { throw new Exception("Class attribute is not numeric!"); } if (m_Template == null) { throw new Exception("No classifier has been specified"); } ThreadMXBean thMonitor = ManagementFactory.getThreadMXBean(); boolean canMeasureCPUTime = thMonitor.isThreadCpuTimeSupported(); if (canMeasureCPUTime && !thMonitor.isThreadCpuTimeEnabled()) thMonitor.setThreadCpuTimeEnabled(true); int addm = (m_AdditionalMeasures != null) ? m_AdditionalMeasures.length : 0; Object[] result = new Object[RESULT_SIZE + addm + m_numPluginStatistics]; long thID = Thread.currentThread().getId(); long CPUStartTime = -1, trainCPUTimeElapsed = -1, testCPUTimeElapsed = -1, trainTimeStart, trainTimeElapsed, testTimeStart, testTimeElapsed; Evaluation eval = new Evaluation(train); m_Classifier = AbstractClassifier.makeCopy(m_Template); trainTimeStart = System.currentTimeMillis(); if (canMeasureCPUTime) CPUStartTime = thMonitor.getThreadUserTime(thID); m_Classifier.buildClassifier(train); if (canMeasureCPUTime) trainCPUTimeElapsed = thMonitor.getThreadUserTime(thID) - CPUStartTime; trainTimeElapsed = System.currentTimeMillis() - trainTimeStart; testTimeStart = System.currentTimeMillis(); if (canMeasureCPUTime) CPUStartTime = thMonitor.getThreadUserTime(thID); eval.evaluateModel(m_Classifier, test); if (canMeasureCPUTime) testCPUTimeElapsed = thMonitor.getThreadUserTime(thID) - CPUStartTime; testTimeElapsed = System.currentTimeMillis() - testTimeStart; thMonitor = null; m_result = eval.toSummaryString(); // The results stored are all per instance -- can be multiplied by the // number of instances to get absolute numbers int current = 0; result[current++] = new Double(train.numInstances()); result[current++] = new Double(eval.numInstances()); result[current++] = new Double(eval.meanAbsoluteError()); result[current++] = new Double(eval.rootMeanSquaredError()); result[current++] = new Double(eval.relativeAbsoluteError()); result[current++] = new Double(eval.rootRelativeSquaredError()); result[current++] = new Double(eval.correlationCoefficient()); result[current++] = new Double(eval.SFPriorEntropy()); result[current++] = new Double(eval.SFSchemeEntropy()); result[current++] = new Double(eval.SFEntropyGain()); result[current++] = new Double(eval.SFMeanPriorEntropy()); result[current++] = new Double(eval.SFMeanSchemeEntropy()); result[current++] = new Double(eval.SFMeanEntropyGain()); // Timing stats result[current++] = new Double(trainTimeElapsed / 1000.0); result[current++] = new Double(testTimeElapsed / 1000.0); if (canMeasureCPUTime) { result[current++] = new Double((trainCPUTimeElapsed / 1000000.0) / 1000.0); result[current++] = new Double((testCPUTimeElapsed / 1000000.0) / 1000.0); } else { result[current++] = new Double(Utils.missingValue()); result[current++] = new Double(Utils.missingValue()); } // sizes if (m_NoSizeDetermination) { result[current++] = -1.0; result[current++] = -1.0; result[current++] = -1.0; } else { ByteArrayOutputStream bastream = new ByteArrayOutputStream(); ObjectOutputStream oostream = new ObjectOutputStream(bastream); oostream.writeObject(m_Classifier); result[current++] = new Double(bastream.size()); bastream = new ByteArrayOutputStream(); oostream = new ObjectOutputStream(bastream); oostream.writeObject(train); result[current++] = new Double(bastream.size()); bastream = new ByteArrayOutputStream(); oostream = new ObjectOutputStream(bastream); oostream.writeObject(test); result[current++] = new Double(bastream.size()); } // Prediction interval statistics result[current++] = new Double(eval.coverageOfTestCasesByPredictedRegions()); result[current++] = new Double(eval.sizeOfPredictedRegions()); if (m_Classifier instanceof Summarizable) { result[current++] = ((Summarizable) m_Classifier).toSummaryString(); } else { result[current++] = null; } for (int i = 0; i < addm; i++) { if (m_doesProduce[i]) { try { double dv = ((AdditionalMeasureProducer) m_Classifier).getMeasure(m_AdditionalMeasures[i]); if (!Utils.isMissingValue(dv)) { Double value = new Double(dv); result[current++] = value; } else { result[current++] = null; } } catch (Exception ex) { System.err.println(ex); } } else { result[current++] = null; } } // get the actual metrics from the evaluation object List<AbstractEvaluationMetric> metrics = eval.getPluginMetrics(); if (metrics != null) { for (AbstractEvaluationMetric m : metrics) { if (m.appliesToNumericClass()) { List<String> statNames = m.getStatisticNames(); for (String s : statNames) { result[current++] = new Double(m.getStatistic(s)); } } } } if (current != RESULT_SIZE + addm + m_numPluginStatistics) { throw new Error("Results didn't fit RESULT_SIZE"); } return result; }