Tangent line on a chart

any way to do something like this on a easy chart? basically just looking for something to do a rate of decline. For some reason I thought the calculated pens had this function but apparently not.

+1 for builtin linéarité régression.
Vert usefull.

The easy chart does not have a function for this right now? I will add a ticket in.

However, you can accomplish this on the classic chart since you can control the data that comes into it. Through scripting you can loop through the data creating the rate of decline data.

The Math Extensions for Ignition has a simple linear regression function included. Something some genius put together… :wink:

1 Like

so how could I chart something based off of slope, y-intercept, and r-squared? the custom chart only seems to take data as a xy relationship.

Thank you Mister Genius :thumb_right:
Very cool.

Well, a datetime can be cast to an integer. That would be your x.

Linear regression is by definition-- well, linear. All linear functoins can be defined as y=ax+b, where:
a is the slope
b is the y-intercept, or where the line would cross the y-axis.

I’ll post an example as soon as I can install igniiton on my laptop… :smiley:

Sorry to take so long, but had some unexpected work to spoil the otherwise fun research and development. :laughing:

The only extra thing you can’t see here was that I added two String dynamic properties to the Date Range component labeled start and end bound to the startDate and endDate properties. This made for a little bit smoother translation of the dates into the script.

One last thing. Don’t use the query as-is… chances are, it won’t work for your system. Yeah, it seems self explanatory… :wink:

Trend.vwin (22.1 KB)


#set query string. UNIX_TIMESTAMP(date) returns the value of the argument as seconds since epoch (‘1970-01-01 00:00:00’)
query=“SELECT t_stamp, unix_timestamp(t_stamp) AS x, Takt_Time as y FROM ds_3425_takt where StationID=‘40’ and t_stamp>=’%s’ and t_stamp<=’%s’” % (startDate,endDate)

#run the query
DataIn=table = system.db.runQuery(query,“Takt”)

#prep for iteration

#count rows and create x and y lists
for row in DataIn:
#xList is from the UNIX_TIMESTAMP(date) from the above query.
#yList is the values for the liear regression input.

#Check for at least three points before running the linreg function. Otherwise there will be an error.
if i>2:

#Prep dataset for chart
headers=[“t_stamp”, “Takt”, “Trend”]

#Run through the query results again. This time we’ll keep the original timestamp and raw value, plus another point calculated from the linreg function.
for row in DataIn:
#use the UNIX_TIMESTAMP value as our source to generate the trend line

#send the dataset to the chart
data = system.dataset.toDataSet(headers, rows)


1 Like


But of course I have to have a problem! I dont understand why it would throw this if it didnt for you. I did have to rewrite the query for sql server since the command to convert to seconds since epoch is a little different, but the query seems to be returning the correct data.

[quote]Traceback (innermost last):
File “event:propertyChange”, line 21, in ?
File “module:math”, line 85, in linreg
OverflowError: integer multiplication: 1315353600 * 1315353600

at org.python.core.Py.OverflowError(Py.java)
at org.python.core.PyInteger.__mul__(PyInteger.java)
at org.python.core.PyObject._mul(PyObject.java)
at org.python.pycode._pyx41.linreg$9(<module:math>:85)
at org.python.pycode._pyx41.call_function(<module:math>)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyFunction.__call__(PyFunction.java)
at org.python.core.PyObject.invoke(PyObject.java)
at org.python.pycode._pyx81.f$0(<event:propertyChange>:21)
at org.python.pycode._pyx81.call_function(<event:propertyChange>)
at org.python.core.PyTableCode.call(PyTableCode.java)
at org.python.core.PyCode.call(PyCode.java)
at org.python.core.Py.runCode(Py.java)
at com.inductiveautomation.ignition.common.script.ScriptManager.runCode(ScriptManager.java:391)
at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.runActions(ActionAdapter.java:139)
at com.inductiveautomation.factorypmi.application.binding.action.ActionAdapter.invoke(ActionAdapter.java:247)
at com.inductiveautomation.factorypmi.application.binding.action.RelayInvocationHandler.invoke(RelayInvocationHandler.java:55)
at $Proxy0.propertyChange(Unknown Source)
at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
at java.awt.Component.firePropertyChange(Unknown Source)
at com.inductiveautomation.factorypmi.application.components.PMIDateRange.propertyChange(PMIDateRange.java:871)
at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
at java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
at java.awt.Component.firePropertyChange(Unknown Source)
at com.inductiveautomation.factorypmi.application.components.PMIDateRange$DateRangeSlider.fireSelectionEvents(PMIDateRange.java:1306)
at com.inductiveautomation.factorypmi.application.components.PMIDateRange$DateRangeSlider.setSelectedRange(PMIDateRange.java:1297)
at com.inductiveautomation.factorypmi.application.components.PMIDateRange$DateRangeSlider.moveSelectionToX(PMIDateRange.java:1332)
at com.inductiveautomation.factorypmi.application.components.PMIDateRange$DateRangeSlider$RangeBox.handleDragEvent(PMIDateRange.java:1931)
at com.inductiveautomation.factorypmi.application.components.PMIDateRange$DateRangeSlider$RangeBox.mouseReleased(PMIDateRange.java:1963)
at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source)
at java.awt.Component.processMouseEvent(Unknown Source)
at javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.awt.Component.processEvent(Unknown Source)
at java.awt.Container.processEvent(Unknown Source)
at java.awt.Component.dispatchEventImpl(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.awt.Container.dispatchEventImpl(Unknown Source)
at java.awt.Window.dispatchEventImpl(Unknown Source)
at java.awt.Component.dispatchEvent(Unknown Source)
at java.awt.EventQueue.dispatchEvent(Unknown Source)
at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.awt.EventDispatchThread.run(Unknown Source)

Ignition v7.2.5 (b76)
Java: Sun Microsystems Inc. 1.6.0_23

Well! For whatever reason, the arguments are staying as an integer.

looks like our maximum integer size is 2,147,483,647 or (2^31)-1

in line 73 of the math script you’ll see this line:

meanerror = residual = 0.0

Try changing it to:

meanerror = residual = float(0.0)

That should initialize them to be floats at the beginning-- and still be accurate to something like 15 places. :unamused:

Fwiw, I do a ton of math with unix timestamps, you can get around this limitation by forcing the integers to longs. For instance, your math error above would work fine if you start the math sequence with ‘1L*’, i.e. ‘1L * 1315353600 * 1315353600’.

Yep! In fact it was about to be my first suggestion. But I wanted to keep it compatible with any float information that may be used for for the list of x-and-y-cordinates that may be later in the list. And while it should properly coerce things on the fly, it doesn’t necessarily mean that it will. :laughing:

EDIT: I’ve updated the download to reflect the discussion here. :smiley:

I’ve just tried out this and yes had to alter the query for MS SQL
from: unix_timestamp(t_stamp)
to: datediff(s,‘1970-01-01 00:00:00’, t_stamp)
I then had the maths overflow, even with the alteration for mean error but from reading above changed 2 lines
from:Sxx = Sxx + xx
to: Sxx = Sxx + 1L
from:Sxy = Sxy + x
to: Sxy = Sxy + 1Lxy
then it worked perfectly (I hope :scratch: )
Just thought I would add this to either help a NooB like me or perhaps even for someone who knows what I’ve done to correct my “playing” :wink:

Don’t sweat it, Brett. While it’d be nice to always have an “out of the box” solution, it’s stuff like this that challenge us and-- while frustrating at times-- makes it all worth while.

Hi Jordan

I tries using the example you provided but I am getting some very high values back for the trend. My data must be wrong in some way. Attached is the dataset that I am returning to the function from the sql query. Can you let me know if the data looks correct looks correct. Basically I am trying to trend the pressure reading across a filter.


t_stamp xTStamp yPressure
24/11/2011 1322098990 117.5
24/11/2011 1322098930 116.3
24/11/2011 1322098870 115.1
24/11/2011 1322098810 113.9
24/11/2011 1322098750 112.7
24/11/2011 1322098690 116.5
24/11/2011 1322098630 116.3
24/11/2011 1322098510 115.2
24/11/2011 1322098450 116.5
24/11/2011 1322098390 116.5
24/11/2011 1322098330 116.5
24/11/2011 1322098270 116.5
24/11/2011 1322098210 116.5
24/11/2011 1322098150 116.5
24/11/2011 1322098090 116.5
24/11/2011 1322098030 116.5
24/11/2011 1322097970 116.5
24/11/2011 1322097910 116.5
24/11/2011 1322097850 117

My data is way different but I am reffering to the data that is goint to the Chart. My values in the two “data columns” (i.e. not the t_stamp column) all look fairly similar:
Data for trend: 30,50,22,48,80
Linear Regression pen data::21.9,46.5, 49.0, 50.6, 61.7
It looks like you have the seconds from Epock in your example, this would be correct for the maths function but not for the chart???
P.S. Jordan - had to go in and change the code from if i>1 to if i>2 under:
#Check for at least two points before running the linreg function. Otherwise there will be an error -
to eliminate a “divide float by zero error” or similar brought about when only two points were given to the linreg formula.

@aidan: Is that data the result of the query similar to the one in the above script?

@ Brett: Good call! I’ve edited the above script to reflect that. :slight_smile: That’s why we call it debugging! :laughing:

Hi Jordan

I am using this query to return the dataset I have shown above
SELECT t_stamp, datediff(s,‘1970-01-01 00:00:00’, t_stamp) AS xTStamp, WashPressure as yPressure FROM MW2 where Equipid = 17 and LineID = 2. I am just wondering what the raw data should look like before it is passed to the Linear Regression formula. Forgive my ignorance but statistical formulas are not my strong point :exclamation:



The data for xTStamp is okay, even if the numbers seem huge. Remember, we’re using the number of seconds over the last (almost) 42 years. :smiley: