使用Haskell'statistics'软件包进行线性回归

时间:2020-08-16 21:50:44

标签: haskell statistics linear-regression

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

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

以及如何解释结果。 我期望得到方程a = 1.5的值b = 0.5y = a + bx

如何构造输入MatrixVector,以及生成的Vector中会有什么?

2 个答案:

答案 0 :(得分:2)

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

要显示如何使用具有单个因果变量的库,我将假定您只需要一个使用普通Haskell数据类型的接口,如下所示:

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

使用olsRegressols似乎更容易,因为:

  1. 不需要Matrix数据类型
  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

答案 1 :(得分:0)

在概念上,让我重写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 +Math.LinearMap.Category
> import Linear (V2(..), V3(..))
> lfun (\(V2 a b) -> fmap (\x -> a*x + b) (V3 1 2 3)) \$ V3 2 2.5 3
V2 0.4999999999999998 1.5000000000000018

我怀疑在statisics中,您需要根据线性函数构建矩阵。这有点古怪,但并不困难:建立矩阵意味着简单地放入可能的基础输入(1,0)(0,1),为它们评估函数(即一次对a=1,{{1} },然后一次用于b=0a=0),并将所有b=1向下的结果记为列向量。

相关问题