All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alpine.result.ScoringResult.scala Maven / Gradle / Ivy

There is a newer version: 1.11
Show newest version
/*
 * Copyright (c) 2015 Alpine Data Labs
 * All rights reserved.
 */
package com.alpine.result

trait MLResult {

  def value: Any

  /**
   * Used in tests to compare results, within a given numerical tolerance range.
   */
  def equals(other: MLResult, tolerance: Double): Boolean
}

trait CategoricalResult extends MLResult {
  /**
   * @return The simple value of the prediction. e.g. "yes" or "1".
   */
  def value: String

  /**
   * @return The index by which the prediction value may be found in the list of labels.
   */
  def index: Int = labels.indexOf(value)

  /**
   * @return The list of potential labels that can be generated by the model. e.g. ["yes", "no"]
   */
  def labels: Seq[String]

  /**
   * A numeric detail generated by the model for each label. Typically, this is a value which is minimized or maximized
   * to determine the predicted value.
   * e.g. confidence probabilities (to be maximised)
   * or distance to the labelled cluster (to be minimised).
   *
   * This MUST be in the same order as the list returned by "labels".
   * @return A list of numeric details corresponding to the labels (order must be preserved).
   */
  def details: Array[Double]

  def toMap: Map[String, Double] = {
    (labels zip details).toMap
  }

  override def equals(other: MLResult, tolerance: Double): Boolean = {
    other match {
      case result: CategoricalResult =>
        if (result.labels == this.labels && result.details.length == details.length) {
          var i = 0
          var sum = 0d
          while (i < details.length) {
            sum += math.abs(details(i) - result.details(i))
            i += 1
          }
          sum <= tolerance
        } else {
          false
        }
      case _ => false
    }
  }

  override def equals(obj: scala.Any): Boolean = {
    obj.isInstanceOf[MLResult] && this.equals(obj.asInstanceOf[MLResult], 0)
  }
}

case class RealResult(value: Double) extends MLResult {
  override def equals(other: MLResult, tol: Double): Boolean = {
    other match {
      case result: RealResult =>
        math.abs(value - result.value) <= tol // Do <= in case tol is 0.
      case _ => false
    }
  }
}

/**
 * The value is the arg min of the distances.
 */
case class ClusteringResult(labels: Seq[String], distances: Array[Double])
  extends CategoricalResult {
  override lazy val (value, index) = {
    var i = 0
    var minDistance = Double.MaxValue
    var minIndex = -1
    while (i < labels.length) {
      if (distances(i) < minDistance) {
        minDistance = distances(i)
        minIndex = i
      }
      i += 1
    }
    if (minIndex < 0) ("", minIndex) else (labels(minIndex), minIndex)
  }

  override def details: Array[Double] = distances

  override def equals(other: MLResult, tolerance: Double): Boolean = {
    other.isInstanceOf[ClusteringResult] && super.equals(other, tolerance)
  }
}

/**
 * The value is the arg max of the confidences.
 */
case class ClassificationResult(labels: Seq[String], confidences: Array[Double])
  extends CategoricalResult {
  override lazy val (value, index) = {
    var i = 0
    var maxConfidence = Double.MinValue
    var maxIndex = -1
    while (i < labels.length) {
      if (confidences(i) > maxConfidence) {
        maxConfidence = confidences(i)
        maxIndex = i
      }
      i += 1
    }
    if (maxIndex < 0) ("", maxIndex) else (labels(maxIndex), maxIndex)
  }

  override def details: Array[Double] = confidences

  override def equals(other: MLResult, tolerance: Double = 0): Boolean = {
    other.isInstanceOf[ClassificationResult] && super.equals(other, tolerance)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy