【问题标题】:Using Haskell 'statistics' package for linear regression使用 Haskell 'statistics' 包进行线性回归
【发布时间】:2020-12-06 01:59:04
【问题描述】:

我需要对一组测量数据进行线性回归。我知道statistics 是去图书馆工作。 Statistics.Regression 模块中ols 函数的描述表明它是我需要的东西。但是,从文档中不清楚如何表示我的测量数据,例如

measurements = [(1.0, 2.0), (2.0, 2.5), (3.0, 3.0)] :: [(Double, Double)]  -- (X, Y) coordinates of points

到签名为的函数

ols :: Matrix   -- A has at least as many rows as columns.
    -> Vector   -- b has the same length as columns in A.
    -> Vector

以及如何解释结果。 我希望得到等式y = a + bx 的值a = 1.5b = 0.5

我必须如何构造输入 MatrixVector,以及生成的 Vector 中的内容是什么?

【问题讨论】:

  • 我想你的意思是你期望a = 0.5b = 1.5
  • 这条线将在 y = 1.5 处穿过 Y 轴。当x = 0y = a
  • 啊,你写了 a + bx。这种情况很少见。

标签: haskell statistics linear-regression


【解决方案1】:

以某种方式期望统计库提供一些通用性的功能,因此如果您追求一个简单的案例,您必须缩小提供的功能。在这里,库函数需要几个因果变量,而不仅仅是一个。此外,它使用向量和矩阵,您可能只需要简单的列表。

为了展示如何使用带有单个因果变量的库,我假设您想要一个只使用普通 Haskell 数据类型的接口,如下所示:

-- [(x0, y0), (x1, y1), , (x2, y2) ... ] -> ((a, b), r2)  for  y ≃ a + b*x
simpleRegression1 :: [(Double, Double)] -> ((Double, Double), Double)

使用olsRegress 似乎比使用ols 更简单,因为:

  1. 不需要矩阵数据类型
  2. 您可以轻松获得 y 截距值(即表示法中的 a
  3. 您还可以从同一个调用中获得拟合优度系数

代码可以写成如下:

import qualified  Data.List              as  L
import qualified  Data.Vector.Unboxed    as  DVU
import qualified  Statistics.Regression  as  SR

-- [(x0, y0), (x1, y1), , (x2, y2) ... ] -> ((a, b), r2)  for  y ≃ a + b*x
simpleRegression1 :: [(Double, Double)] -> ((Double, Double), Double)
simpleRegression1 xyPairs =
   let  xList    = L.map  fst  xyPairs
        yList    = L.map  snd  xyPairs
        xVecList = [DVU.fromList  xList]
        yVec     =  DVU.fromList  yList
        (sv, r2) = SR.olsRegress xVecList yVec
        [b, a]   = DVU.toList sv
    in
        ((a,b), r2)

在我们的例子中,只有一个列向量,所以xVecList 只有一个元素。 正如olsRegress documentation 中提到的,y 截距“a”值是输出向量的最后一个元素。

测试代码:

main = do
    let  measurements = [(1.0, 1.9999), (2.0, 2.5001), (3.0, 2.9999)]
         ---- measurements = [(1.0, 2.0), (2.0, 2.5), (3.0, 3.0)]
         ((a,b), r2)  = simpleRegression1  measurements
    putStrLn $ "measurements = " ++ (show measurements)
    putStrLn $ "a = " ++ (show a) ++ "  b = " ++ (show b) ++
               "  r2 = " ++ (show r2)

    let  ypList   = L.map  (\x -> a+b*x)  (L.map fst measurements)
         diffList = L.zipWith  (-)  (L.map snd measurements)  ypList
    putStrLn $ "ypList = " ++ (show ypList)
    putStrLn $ "diffList = " ++ (show diffList)

测试输出:

measurements = [(1.0,1.9999),(2.0,2.5001),(3.0,2.9999)]
a = 1.4999666666666656  b = 0.5000000000000006  r2 = 0.9999999466666695
ypList = [1.999966666666666,2.4999666666666664,2.9999666666666673]
diffList = [-6.666666666599319e-5,1.3333333333376274e-4,-6.66666666675475e-5]

注意还有一个LinearRegression module

【讨论】:

    【解决方案2】:

    让我重写ols 的签名以反映它在概念上实际完成的工作:

    ols :: (VectorSpace u, VectorSpace v) => (u +> v) -> v -> u
    

    其中+> 表示linear function,这就是矩阵所代表的内容。

    所以,u 是您想要的结果(即参数ab)。矩阵参数是将这些参数映射到实际测量的预测的(线性)函数。因此,这涉及在数据集中的每个 x 位置计算 a·x + b

    在更受数学启发(而不是受 Matlab 或 R 启发)的库中,您可以大致这样编写它:

    m :: Matrix
    m = fromFunction $ \(a,b) -> fmap (\x -> a*x + b) [1,2,3])
    

    然后v 类型的测量向量是您在输出中拥有的y 值,因此您将评估

    ols m [2, 2.5, 3]
    

    事实上,在linearmap-category 中——与statistics 不同的是,它的类型是正确的——你几乎可以通过这种方式做到这一点:

    > :m +@987654323@
    > import @987654324@ (V2(..), V3(..))
    > lfun (\(V2 a b) -> fmap (\x -> a*x + b) (V3 1 2 3)) @987654325@ V3 2 2.5 3
    V2 0.4999999999999998 1.5000000000000018

    statisics 中,我怀疑您自己需要从线性函数中构建矩阵。这有点繁琐但并不困难:构建矩阵意味着简单地放入可能的基础输入(1,0)(0,1),为它们评估函数(即一次用于a=1b=0,一次用于a=0b=1) 并将所有x 的结果记录为列向量。

    【讨论】:

    • 不,我不明白。我没有线性函数。我有一组现货值。我希望库例程给我一个“最接近”我拥有的值的线性函数。 IE。线性函数f(x) = a + bxab
    • 这里有一个常见的误解:“线性回归”确实意味着你正在拟合一个“线性”函数,在直函数的意义上(仿射 实际上是正确的术语)。事实上,您也可以线性拟合多项式或正弦函数。重要的是该函数在拟合参数中是线性的,即当您将ab 加倍时,函数输出也会加倍。正是这个属性,给定x 值的任何向量,允许 (a,b) ⟼ a + bx 的相应输出被捕获在一个矩阵中。
    • 经过一番阅读后,我想到了这个:(predictor, responder) = both fromList $ unzip measurements; (regression, goodness) = olsRegress [predictor] responder; intercept = regression ! 1; coefficient = regression ! 0。预测器是 one 向量的列表,因为我们有一个单一的预测器案例。结果数字在我看来是合理的。我在做什么正确吗?如果是这样,我会将其发布为自我回答。
    • 嗯...啊,olsRegress 自动为截取添加一列,那就对了。 (如果它没有这样做,您可以添加一列全 1。)
    猜你喜欢
    • 2018-07-31
    • 2019-01-06
    • 2018-02-03
    • 2018-07-23
    • 2022-01-21
    • 2020-08-06
    • 2021-03-11
    • 1970-01-01
    相关资源
    最近更新 更多