Search code examples
pythonnumpyregressionnonlinear-functions

Input into a polynomial regression formula with Python


I inherited a project in the middle of pandemonium and to makes matters worse I am just learning python.

I managed to implement a polynomial function into my code and the results are the same as the ones posted in the examples of this web page. [z = numpy.polyfit(x, y, 5)]

However, I will like to know how to modify the program so I can insert an input of one of the know values of y to find x.

In other words. I have x and y arrays where: - array x holds values for know kilograms weights (0.0, 0.5, 1.0, 1.5, 2.0) - array y (0.074581967, 0.088474754, 0.106797419, 0.124461935, 0.133726833)

I have a program who reads a load of weight and the tension created by a phidget and generates the array to be used by this program.

What I need to accomplish is to read the next value from the phidget and be able to convert the reading into kilograms, from within the provided array.

Is there a way to do this? I feel I am only missing a line of code but I don't know how to implement the results of the returned values from the formula. (z)

Thanks in advance.

CODE ADDED AS REQUESTED

from Phidgets.PhidgetException import PhidgetException
from Phidgets.Devices.Bridge import Bridge, BridgeGain

import datetime
import os
import re
import sys
import time
import numpy


wgh = list()
avg = list()
buf = list()
x = []
y = []


def nonlinear_regression():   # reads the calibration mapping and generates the conversion coefficients

    fd = open("calibration.csv", "r")

    for line in fd:
        [v0, v1] = line.split(",")
        x.append(float(v0))
        y.append(float(v1[:len(v1) - 1]))

    xdata = numpy.array(x)
    ydata = numpy.array(y)


    z = numpy.polyfit(x, y, 5)      

    return z       


def create_data_directory():   # create the data directory
    if not os.path.exists("data"):
        os.makedirs("data")

def parse_config():   # get config-file value
    v = int()

    config = open("config.properties", "r")
    for line in config:
        toks = re.split(r"[\n= ]+", line)
        if toks[0] == "record_interval":
            v = int(toks[1])

    return v

def read_bridge_data(event):   # read the data
    buf.append(event.value)

def record_data(f_name, date, rms):
    if not os.path.isfile(f_name):
        fd = open(f_name, "w")
        fd.write("time,weight\n")
        fd.write(datetime.datetime.strftime(date, "%H:%M"))
        fd.write(",")
        fd.write(str(rms) + "\n")
        fd.close()
    else:
        fd = open(f_name, "a")
        fd.write(datetime.datetime.strftime(date, "%H:%M"))
        fd.write(",")
        fd.write(str(rms) + "\n")
        fd.close()
    print("Data recorded.")

def release_bridge(event):   # release the phidget device
    try:
        event.device.closePhidget()
    except:
        print("Phidget bridge could not be released properly.")
        sys.exit(1)


def main():

    create_data_directory()

    RECORD_INTERVAL = parse_config()        # get the config-file value
    calibrate = nonlinear_regression()      # get calibration function; use like: calibrate(some_input)
    bridge = Bridge()

    try:
        bridge.setOnBridgeDataHandler(read_bridge_data)
        bridge.setOnDetachHandler(release_bridge)   # when the phidget gets physically detached
        bridge.setOnErrorhandler(release_bridge)   # asynchronous exception (i.e. keyboard interrupt)
    except:
        print("Phidget bridge event binding failed.")
        sys.exit(1)

    try:
        bridge.openPhidget()
        bridge.waitForAttach(3000)
    except:
        print("Phidget bridge opening failed.")
        sys.exit(1)

    last_record = int()
    while (True):
        date = datetime.datetime.now()
        f_name = "data\\" + datetime.datetime.strftime(date, "%B_%d_%Y") + ".csv"

        curr = time.time() * 1000
        if (curr - last_record) > (RECORD_INTERVAL * 1000):
            try:
                bridge.setDataRate(10)
                last = time.time() * 1000
                bridge.setEnabled(0, True)
                while (time.time() * 1000 - last) < 1000:   # collects over 1 sec
                    pass
                bridge.setEnabled(0, False)
            except:
                print("Phidget bridge data reading error.")
                bridge.setEnabled(0, False)
                bridge.closePhidget()
                sys.exit(1)
            vol = sum(buf) / len(buf)

            del buf[:]
            last_record = curr

            record_data(f_name, date, vol)   # replace curr with calibrated data

        #THIS IS WHERE I WILL LIKE TO INCORPORATE THE CHANGES TO SAVE THE WEIGHT                
            #record_data(f_name, date,  conversion[0] * vol + conversion[1])   # using the linear conversion function
        else:
            time.sleep(RECORD_INTERVAL - 1)   # to reduce the CPU's busy-waiting

if __name__ == "__main__":
    main()

Solution

  • The linear conversion function from the calibrations is returned by numpy.polyfit as an array of coefficients. Since you passed 5 for the degree argument of polyfit, you will get an array of six coefficients:

    f(x) = ax5 + bx4 + cx3 + dx2 + ex + f

    Where a, b, c, d, e, and f are the elements of the z array returned by nonlinear_regression.

    To implement the linear conversion formula, simply use the power operator **, the elements of z, and the value of vol:

    vol_calibrated = vol**5 * z[0] + vol**4 * z[1] + vol**3 * z[2] + vol**2 * z[3] + vol * z[4] + z[5]

    Or more generally:

    degree = len(z) - 1
    vol_calibrated = sum(vol**(degree-i) * coeff for i, coeff in enumerate(z))