Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Linear regression - accelerate framework in Swift

My first question here at Stackoverflow... hope my question is specific enough.

I have an array in Swift with measurements at certain dates. Like:

var myArray:[(day: Int, mW: Double)] = []
myArray.append(day:0, mW: 31.98)
myArray.append(day:1, mW: 31.89)
myArray.append(day:2, mW: 31.77)
myArray.append(day:4, mW: 31.58)
myArray.append(day:6, mW: 31.46)

Some days are missing, I just didn't take a measurement... All measurements should be on a line, more or less. So I thought about linear regression. I found the Accelerate framework, but the documentation is missing and I can't find examples.

For the missing measurements I would like to have a function, with as input a missing day and as output a best guess, based on the other measurements.

func bG(day: Int) -> Double {
    return // return best guess for measurement
}

Thanks for helping out. Jan

like image 660
arakweker Avatar asked Apr 16 '15 12:04

arakweker


2 Answers

My answer doesn't specifically talk about the Accelerate Framework, however I thought the question was interesting and thought I'd give it a stab. From what I gather you're basically looking to create a line of best fit and interpolate or extrapolate more values of mW from that. To do that I used the Least Square Method, detailed here: http://hotmath.com/hotmath_help/topics/line-of-best-fit.html and implemented this in Playgrounds using Swift:

//  The typealias allows us to use '$X.day' and '$X.mW',
//  instead of '$X.0' and '$X.1' in the following closures.
typealias PointTuple = (day: Double, mW: Double)

//  The days are the values on the x-axis.
//  mW is the value on the y-axis.
let points: [PointTuple] = [(0.0, 31.98),
                            (1.0, 31.89),
                            (2.0, 31.77),
                            (4.0, 31.58),
                            (6.0, 31.46)]

// When using reduce, $0 is the current total.
let meanDays = points.reduce(0) { $0 + $1.day } / Double(points.count)
let meanMW   = points.reduce(0) { $0 + $1.mW  } / Double(points.count)

let a = points.reduce(0) { $0 + ($1.day - meanDays) * ($1.mW - meanMW) }
let b = points.reduce(0) { $0 + pow($1.day - meanDays, 2) }

// The equation of a straight line is: y = mx + c
// Where m is the gradient and c is the y intercept.
let m = a / b
let c = meanMW - m * meanDays

In the code above a and b refer to the following formula from the website:

a: enter image description here

b:enter image description here

Now you can create the function which uses the line of best fit to interpolate/extrapolate mW:

func bG(day: Double) -> Double {
    return m * day + c
}

And use it like so:

bG(3) // 31.70
bG(5) // 31.52
bG(7) // 31.35
like image 71
ABakerSmith Avatar answered Nov 10 '22 22:11

ABakerSmith


If you want to do fast linear regressions in Swift, I suggest using the Upsurge framework. It provides a number of simple functions that wrap the Accelerate library and so you get the benefits of SIMD on either iOS or OSX without having to worry about the complexity of vDSP calls.

To do a linear regression with base Upsurge functions is simply:

let meanx = mean(x)
let meany = mean(y)
let meanxy = mean(x * y)
let meanx_sqr = measq(x)

let slope = (meanx * meany - meanxy) / (meanx * meanx - meanx_sqr)
let intercept = meany - slope * meanx

This is essentially what is implemented in the linregress function.

You can use it with an array of [Double], other classes such as RealArray (comes with Upsurge) or your own objects if they can expose contiguous memory.

So a script to meet your needs would look like:

#!/usr/bin/env cato

import Upsurge

typealias PointTuple = (day: Double, mW:Double)

var myArray:[PointTuple] = []

myArray.append((0, 31.98))
myArray.append((1, 31.89))
myArray.append((2, 31.77))
myArray.append((4, 31.58))
myArray.append((6, 31.46))

let x = myArray.map { $0.day }
let y = myArray.map { $0.mW }

let (slope, intercept) = Upsurge.linregress(x, y)

func bG(day: Double) -> Double {
    return slope * day + intercept
}

(I left in the appends rather than using literals as you are likely programmatically adding to your array if it is of significant length)

and full disclaimer: I contributed the linregress code. I hope to also add the co-efficient of determination at some point in the future.

like image 31
timbo Avatar answered Nov 10 '22 20:11

timbo