# Tangent line on a chart

**URL:** <https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278>\
**Category:** Ignition\
**Created:** [September 12, 2011, 11:50am UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278 "2011-09-12T11:50:57Z")\
**Posts on this page:** 19\
**Page:** 1

<div class="post-metadata">

**Author:** ![diat150](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@diat150](https://forum.inductiveautomation.com/u/diat150)\
**Post date:** [September 12, 2011, 11:50am UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/1 "2011-09-12T11:50:57Z")

</div>

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.

![](https://us1.discourse-cdn.com/inductiveautomation/original/3X/c/7/c7cffc5ccb589a240502cc5918aa8c747474794b.jpeg)

---

<div class="post-metadata">

**Author:** ![gnguyen](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@gnguyen](https://forum.inductiveautomation.com/u/gnguyen)\
**Post date:** [September 12, 2011, 12:01pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/2 "2011-09-12T12:01:54Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Travis.Cox](https://avatars.discourse-cdn.com/v4/letter/t/b9bd4f/32.png) [@Travis.Cox](https://forum.inductiveautomation.com/u/Travis.Cox)\
**Post date:** [September 12, 2011, 3:35pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/3 "2011-09-12T15:35:43Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [September 13, 2011, 10:42am UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/4 "2011-09-13T10:42:26Z")

</div>

The [Math Extensions for Ignition](http://inductiveautomation.com/forum/viewtopic.php?f=25&t=6688) has a simple linear regression function included. Something some genius put together… 😉

---

<div class="post-metadata">

**Author:** ![diat150](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@diat150](https://forum.inductiveautomation.com/u/diat150)\
**Post date:** [September 13, 2011, 8:27pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/5 "2011-09-13T20:27:47Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![gnguyen](https://avatars.discourse-cdn.com/v4/letter/g/41988e/32.png) [@gnguyen](https://forum.inductiveautomation.com/u/gnguyen)\
**Post date:** [September 13, 2011, 8:35pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/6 "2011-09-13T20:35:25Z")

</div>

Thank you Mister Genius ![:thumb_right:](https://us1.discourse-cdn.com/inductiveautomation/original/1X/2026e28c0c425034352c1b75807edf956adc75f4.gif ":thumb\_right:")  
Very cool.

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [September 14, 2011, 12:17am UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/7 "2011-09-14T00:17:44Z")

</div>

> [@diat150](#):
>
> 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.

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... 😃

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [September 16, 2011, 4:05pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/8 "2011-09-16T16:05:01Z")

</div>

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

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… 😉

[Trend.vwin](https://us1.discourse-cdn.com/inductiveautomation/original/1X/ef32178a703ba3eb317de7ae4dd910e3d4291443.vwin) (22.1 KB)

 ![](https://us1.discourse-cdn.com/inductiveautomation/original/1X/c56480b0eee2182337a29daa6fbf792ee6ca14a1.png)  

[code]#initialize  
startDate=event.source.start  
endDate=event.source.end  
slope=float(0)  
intercept=float(0)  
r=float(0)

#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  
i=0  
xList=  
yList=

#count rows and create x and y lists  
for row in DataIn:  
i=i+1  
#xList is from the UNIX\_TIMESTAMP(date) from the above query.  
xList.append(row[1])  
#yList is the values for the liear regression input.  
yList.append(row[2])

#Check for at least three points before running the linreg function. Otherwise there will be an error.  
if i\>2:  
slope,intercept,r=app.math.linreg(xList,yList)

#Prep dataset for chart  
headers=[“t\_stamp”, “Takt”, “Trend”]  
rows=

#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  
trend=slope\*row[1]+intercept  
oneRow=[row[0],row[2],trend]  
rows.append(oneRow)

#send the dataset to the chart  
data = system.dataset.toDataSet(headers, rows)  
event.source.parent.getComponent(‘Chart’).Data=data

[/code]

---

<div class="post-metadata">

**Author:** ![diat150](https://avatars.discourse-cdn.com/v4/letter/d/a587f6/32.png) [@diat150](https://forum.inductiveautomation.com/u/diat150)\
**Post date:** [September 16, 2011, 5:19pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/9 "2011-09-16T17:19:02Z")

</div>

Thanks,

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  
[/quote]

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [September 16, 2011, 6:14pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/10 "2011-09-16T18:14:25Z")

</div>

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:

```auto
meanerror = residual = 0.0
```

Try changing it to:

```auto
meanerror = residual = float(0.0)
```

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

---

<div class="post-metadata">

**Author:** ![Step7](https://avatars.discourse-cdn.com/v4/letter/s/45deac/32.png) [@Step7](https://forum.inductiveautomation.com/u/Step7)\
**Post date:** [September 17, 2011, 8:11pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/11 "2011-09-17T20:11:51Z")

</div>

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’.

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [September 18, 2011, 10:10pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/12 "2011-09-18T22:10:55Z")

</div>

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. 😆

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

---

<div class="post-metadata">

**Author:** ![Brett\_Yeomans](https://avatars.discourse-cdn.com/v4/letter/b/35a633/32.png) [@Brett\_Yeomans](https://forum.inductiveautomation.com/u/Brett_Yeomans)\
**Post date:** [November 23, 2011, 1:13pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/13 "2011-11-23T13:13:45Z")

</div>

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 + x_x  
to: Sxx = Sxx + 1L_x_x  
and  
from:Sxy = Sxy + x_y  
to: Sxy = Sxy + 1L_x_y  
then it worked perfectly (I hope ![:scratch:](https://us1.discourse-cdn.com/inductiveautomation/original/1X/76d1620bec252bfac07050c889ff5a91dc8a9c25.gif ":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” 😉

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [November 23, 2011, 2:31pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/14 "2011-11-23T14:31:45Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![aidamcg](https://avatars.discourse-cdn.com/v4/letter/a/90db22/32.png) [@aidamcg](https://forum.inductiveautomation.com/u/aidamcg)\
**Post date:** [November 24, 2011, 3:22pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/15 "2011-11-24T15:22:00Z")

</div>

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.

Thanks

Aidan  
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

---

<div class="post-metadata">

**Author:** ![Brett\_Yeomans](https://avatars.discourse-cdn.com/v4/letter/b/35a633/32.png) [@Brett\_Yeomans](https://forum.inductiveautomation.com/u/Brett_Yeomans)\
**Post date:** [November 24, 2011, 3:55pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/16 "2011-11-24T15:55:32Z")

</div>

aidamcg  
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.

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [November 28, 2011, 2:30pm UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/17 "2011-11-28T14:30:51Z")

</div>

@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. 🙂That’s why we call it debugging! 😆

---

<div class="post-metadata">

**Author:** ![aidamcg](https://avatars.discourse-cdn.com/v4/letter/a/90db22/32.png) [@aidamcg](https://forum.inductiveautomation.com/u/aidamcg)\
**Post date:** [November 29, 2011, 7:16am UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/18 "2011-11-29T07:16:58Z")

</div>

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 ❗

Thanks

Aidan

---

<div class="post-metadata">

**Author:** ![JordanCClark](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/jordancclark/32/83362_2.png) [@JordanCClark](https://forum.inductiveautomation.com/u/JordanCClark)\
**Post date:** [November 30, 2011, 11:48am UTC](https://forum.inductiveautomation.com/t/tangent-line-on-a-chart/3278/19 "2011-11-30T11:48:39Z")

</div>

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. 😃
