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
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
:
b
:
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With