加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala:计算标准偏差的通用方法是什么

发布时间:2020-12-16 09:01:18 所属栏目:安全 来源:网络整理
导读:我很好奇如何编写一个通用方法来计算 scala中的标准偏差和方差.我有一个计算平均值的通用方法(从这里被盗: Writing a generic mean function in Scala) 我试图将平均值计算转换为标准偏差和方差但我看起来不对.此外,泛型在Scala编程方面超越了我的技能. 计
我很好奇如何编写一个通用方法来计算 scala中的标准偏差和方差.我有一个计算平均值的通用方法(从这里被盗: Writing a generic mean function in Scala)

我试图将平均值计算转换为标准偏差和方差但我看起来不对.此外,泛型在Scala编程方面超越了我的技能.

计算均值,标准差和方差的代码如下:

package ca.mikelavender

import scala.math.{Fractional,Integral,Numeric,_}

package object genericstats {

  def stdDev[T: Numeric](xs: Iterable[T]): Double = sqrt(variance(xs))

  def variance[T: Numeric](xs: Iterable[T]): Double = implicitly[Numeric[T]] match {
    case num: Fractional[_] => {
      val avg = mean(xs)
      num.toDouble(
        xs.foldLeft(num.zero)((b,a) =>
          num.plus(b,num.times(num.minus(a,avg),num.minus(a,avg))))) /
        xs.size
    }
    case num: Integral[_] => {
      val avg = mean(xs)
      num.toDouble(
        xs.foldLeft(num.zero)((b,avg))))) /
        xs.size
    }
  }

  /**
    * https://stackoverflow.com/questions/6188990/writing-a-generic-mean-function-in-scala
    */
  def mean[T: Numeric](xs: Iterable[T]): T = implicitly[Numeric[T]] match {
    case num: Fractional[_] => import num._; xs.sum / fromInt(xs.size)
    case num: Integral[_] => import num._; xs.sum / fromInt(xs.size)
    case _ => sys.error("Undivisable numeric!")
  }

}

我觉得不需要方差方法中的匹配情况,或者可能更优雅.也就是说,代码的重复性似乎对我来说非常错误,我应该能够使用匹配来获取数字类型,然后将其传递给执行计算的单个代码块.

我不喜欢的另一件事是它总是返回一个Double.我觉得应该返回相同的输入数字类型,至少对于Fractional值.

那么,有没有关于如何改进代码并使其更漂亮的建议?

解决方法

像Numeric这样的类型类的目标是为类型提供一组操作,以便您可以编写在具有类型类实例的任何类型上一般工作的代码. Numeric提供了一组操作,其子类Integral和Fractional另外提供了更具体的操作(但它们也表征了更少的类型).如果您不需要这些更具体的操作,您可以简单地在Numeric级别工作,但不幸的是在这种情况下您可以.

让我们从卑鄙开始吧.这里的问题是除法对于整数和分数类型意味着不同的东西,并且根本不提供仅为数字的类型. Daniel的answer you’ve linked通过调度Numeric实例的运行时类型来解决这个问题,如果实例不是Fractional或Integral,则只会崩溃(在运行时).

我将不同意丹尼尔(或者至少是五年前的丹尼尔),并说这不是一个很好的方法 – 它既是真正的差异,也是同时抛出很多类型的安全.我认为有三种更好的解决方案.

仅为小数类型提供这些操作

您可能会认为取整数类型对整数类型没有意义,因为积分除失了结果的小数部分,并且仅为小数类型提供:

def mean[T: Fractional](xs: Iterable[T]): T = {
  val T = implicitly[Fractional[T]]

  T.div(xs.sum,T.fromInt(xs.size))
}

或者使用漂亮的隐式语法:

def mean[T: Fractional](xs: Iterable[T]): T = {
  val T = implicitly[Fractional[T]]
  import T._

  xs.sum / T.fromInt(xs.size)
}

最后一个句法要点:如果我发现我必须隐式编写[SomeTypeClass [A]]以获取对类型类实例的引用,我倾向于去除上下文绑定([A:SomeTypeClass]部分)以清理事物位:

def mean[T](xs: Iterable[T])(implicit T: Fractional[T]): T =
  T.div(xs.sum,T.fromInt(xs.size))

但这完全是品味问题.

返回具体的小数类型

你也可以使mean返回像Double这样的具体小数类型,并在执行操作之前简单地将Numeric值转换为该类型:

def mean[T](xs: Iterable[T])(implicit T: Numeric[T]): Double =
  T.toDouble(xs.sum) / xs.size

或者,等效但使用Numeric的toDouble语法:

import Numeric.Implicits._

def mean[T: Numeric](xs: Iterable[T]): Double = xs.sum.toDouble / xs.size

这为积分和分数类型(最高精度为Double)提供了正确的结果,但代价是使您的操作不那么通用.

创建一个新的类类

最后,您可以创建一个新类型类,为Fractional和Integral提供共享除法运算:

trait Divisible[T] {
  def div(x: T,y: T): T
}

object Divisible {
  implicit def divisibleFromIntegral[T](implicit T: Integral[T]): Divisible[T] =
    new Divisible[T] {
      def div(x: T,y: T): T = T.quot(x,y)
    }

  implicit def divisibleFromFractional[T](implicit T: Fractional[T]): Divisible[T] =
    new Divisible[T] {
      def div(x: T,y: T): T = T.div(x,y)
    }
}

然后:

def mean[T: Numeric: Divisible](xs: Iterable[T]): T =
  implicitly[Divisible[T]].div(xs.sum,implicitly[Numeric[T]].fromInt(xs.size))

这本质上是原始均值的更原则的版本 – 而不是在运行时调度子类型,您使用新类型表征子类型.有更多的代码,但没有运行时错误的可能性(除非当然xs是空的等等,但这是所有这些方法都遇到的正交问题).

结论

在这三种方法中,我可能会选择第二种,在您的情况下,由于您的方差和stdDev已经返回Double,因此在您的情况下看起来特别合适.在这种情况下,整个事情看起来像这样:

import Numeric.Implicits._

def mean[T: Numeric](xs: Iterable[T]): Double = xs.sum.toDouble / xs.size

def variance[T: Numeric](xs: Iterable[T]): Double = {
  val avg = mean(xs)

  xs.map(_.toDouble).map(a => math.pow(a - avg,2)).sum / xs.size
}

def stdDev[T: Numeric](xs: Iterable[T]): Double = math.sqrt(variance(xs))

……你已经完成了.

在实际代码中,我可能会查看像Spire这样的库,而不是使用标准库的类型类.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读