简体   繁体   中英

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

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.

Use SKKeyframeSequence from SpriteKit https://developer.apple.com/documentation/spritekit/skinterpolationmode/spline

import SpriteKit

let sequence = SKKeyframeSequence(keyframeValues: [0, 20, 40, 60, 80, 100], times: [64, 128, 256, 512, 1024, 2048])
sequence.interpolationMode = .spline // .linear, .step

let estimatedValue = sequence.sample(atTime: CGFloat(1500)) as! Double
print(estimatedValue)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM