Is there a Histogram Function?

I have a timeline chart to show faults in a PLC, Pictutre attached.

What the cusomer wants is a Histogram chart, basically a bar chart with faults at bottom (x Axis), number of total minutes (y Axis). they want to be able to select the last “8” hours or “16” hours or whatever hours. or the timeline.

Is this function built in somewhere or do I need to do some manual calculations to get total minutes for each fault?

1 Like

How is the data gathered now? Is that a status chart you’re using?

1 Like

Yes, it is a status chart, using historical tags.

I was able to summerize the columns to give me totals in minutes of downtime, but they want occurances… So as you can see on the picture, some red areas exist for a long time, but is still one occurance.

1 Like

Yeah, you’ll have to iterate through the dataset, looking for the transiston.

Someone else will have to chime in at this point. My refusal to use historical tags just limits how much I can help you. :wink:

1 Like

A while ago I wrote code to create a histogram, feel free to use/modify it if you want…

[code]#Script to create a histogram

#This script looks at an array of data and counts how many cells are between specific numbers (called “bins”) to use for plotting a histogram on a bar chart.

#----Change these values according to your needs-----
dat=event.source.parent.Xbar #load a dataset. Have the column of interest be labeled “Data”

mx=event.source.parent.xbarmax #the maximum value of the data
mn=event.source.parent.xbarmin #the minimum value of the data
bins=10 #should this be dynamic based on the row count? is 10 bins enough granularity? Change to adapt to your dataset!

#----histogram code-----
import math
rng=float(mx-mn) #the range of the dataset
binSize=float(rng/bins) #the size of the bins

mn1=mn-(2*binSize)#include some extra bins on the low end
bin_ar=[] #initalize the bin array

for x in range(bins+4): #Iterate through the bin array. include some bins on the high end.
bin_val=round(mn1+(x*binSize),1)
bin_ar.append([bin_val])
hd=[“Bins”]
bins_array= system.dataset.toDataSet(hd,bin_ar) #the complete array of the bins

resar=[]
for i in range(len(bin_ar)-1): #iterate through each bin.
cnt=0
for x in range(dat.rowCount): #iterate on each datapoint, for a specific bin, then count if it is in range.
val=dat.getValueAt(x,“Data”)
if val>= bins_array.getValueAt(i,“Bins”) and val< bins_array.getValueAt(i+1,“Bins”): #if the value is within between the current bin and the next bin, count it.
cnt+=1

if x==(dat.rowCount-1): 
	resar.append([str(bins_array.getValueAt(i,"Bins")),cnt]) #append to an array with the count results for the specific bin

hd1=[“Bin”,“Count”]
histdata=system.dataset.toDataSet(hd1,resar) #the output dataset
[/code]

2 Likes

Nice script, can you post a pic of the resulting histogram?

1 Like

Sorry guys, I have been pulled off on other projects and just getting back to this… I was not able to make the Histogram script work for me…

1 Like

I got this working for anyone interested, using a spinner to change the bin size on the fly

[code]#Script to create a histogram

#This script looks at an array of data and counts how many cells are between specific numbers (called “bins”) to use for plotting a histogram on a bar chart.

#----Change these values according to your needs-----
#data = system.db.runQuery(“select t_weld from laserweld where station = 3 and substation = 0 and datetime > DateAdd(HOUR,-12,getdate())”) #load a dataset. Have the column of interest be labeled “Data”
dropDownValue = event.source.parent.getComponent(‘Dropdown’).selectedStringValue
start_date = event.source.parent.getComponent(‘Date Range’).startDate
end_date = event.source.parent.getComponent(‘Date Range’).endDate
input = system.db.runPrepQuery(“select %s as data from laserweld where station = 3 and substation = 0 and datetime between ? and ?” %dropDownValue, [start_date, end_date])

dat = system.dataset.toDataSet(input)
mx= 0.4 #the maximum value of the data
mn= 0.1 #the minimum value of the data
bins=event.source.intValue #should this be dynamic based on the row count? is 10 bins enough granularity? Change to adapt to your dataset!

#----histogram code-----
import math
rng=float(mx-mn) #the range of the dataset
binSize=float(rng/bins) #the size of the bins

mn1=mn-(2*binSize)#include some extra bins on the low end
bin_ar=[] #initalize the bin array

for x in range(bins+4): #Iterate through the bin array. include some bins on the high end.
bin_val=round(mn1+(x*binSize),1)
bin_ar.append([bin_val])
hd=[“Bins”]
bins_array= system.dataset.toDataSet(hd,bin_ar) #the complete array of the bins

resar=[]
for i in range(len(bin_ar)-1): #iterate through each bin.
cnt=0
for x in range(dat.rowCount): #iterate on each datapoint, for a specific bin, then count if it is in range.
val=dat.getValueAt(x,“Data”)
if val>= bins_array.getValueAt(i,“Bins”) and val< bins_array.getValueAt(i+1,“Bins”): #if the value is within between the current bin and the next bin, count it.
cnt+=1

if x==(dat.rowCount-1):
resar.append([str(bins_array.getValueAt(i,“Bins”)),cnt]) #append to an array with the count results for the specific bin

hd1=[“Bin”,“Count”]
histdata=system.dataset.toDataSet(hd1,resar) #the output dataset
event.source.parent.getComponent(‘Bar Chart’).data = histdata
[/code]

3 Likes

[quote=“JordanCClark”]My refusal to use historical tags just limits how much I can help you. :wink:[/quote]Heh. I have nothing to add to the OP’s problem, but I do share your low opinion of narrow key-value history recording. I also have a low opinion of database backends that cannot do window aggregates, which make time delta computations for event analysis relatively easy.

1 Like

That Histogram works great, thank you. How about drawing a bell curve on top for deviation?

1 Like

@pturmel / @JordanCClark, I am no expert on this, I am interested on the reasons behind those low opinions? can any of you elaborate or point me to a good read for my own enlightenment on why its not a good idea to use historian data for this application? also expand on the DB back-end for aggregate functions?

With Controls background I find DB queries a simple solution for some of my reporting requirements, but if I understand correctly for some applications may not be the best solution. What would be the option for those cases?

1 Like

Hi Gregg,
The vast majority of my applications are event driven. Make a part, store values associated with that part. Often in the same row. This is where transaction groups excel.

The same can be done for time series data. multiple tags on one row in the db. This provides context that I’m not yet convinced exists in a single key-value structure, even if you query multiple historian points.

Window aggregates can be explained in a bit more detail here (using Postgres as an example).

3 Likes

Hi Gregg,

What Jordan said.

Plus, I’ve found that many, if not most, of the analysis I do with historical data involves correlations between different values in time. With key-value storage (tag historian), it is extraordinarily challenging to assemble datasets of multiple variables sampled at identical points in time. With transaction groups, it is a natural property of the value columns in each table.

Since it is common to not know ahead of time which value correlations will be interesting, I tend to select recording methods that enable easy ad-hoc analysis at a later date. Storage is cheap. It is easy to throw away data you later decide you don’t need. It is painful to realize you could solve a critical problem if only you had more historical data, or in a usable form.

4 Likes

I asked a similar question earlier on a post about auto switching between windows in a project. My question for this specific instance, is where would I do this scripting to generate a histogram? I am attempting to generate histograms for test data that I am pulling from an SQL database.

Excited to see how quickly JordanCClark answers this one!

Thanks!

If you know the bucket intervals in advance, I would make the DB do the work. Something like this (Named Query):

SELECT bucket, count(*)
FROM (
  SELECT :firstbucket + :bucketinterval * floor((valuecolumn - :firstbucket) / :bucketinterval) AS bucket
  FROM sometable
  WHERE somecondition
) bucketized
GROUP BY bucket
ORDER BY bucket
4 Likes

You caught me at a good time last time around. Don't always expect that! :wink:

You can also try the statsutils port.

1 Like

Is there a tutorial anywhere on how to use the Statsutils port? As I mentioned in another post, Im still learning how everything in Ignition works, and I am also in the process of migrating all of my projects and Ignition Gateway over to 8.0.2. College taught me to learn through stack overflow, and there doesn’t seem to be as much documentation for Ignition related items.

I appreciate the help.
-Ryan

Well, there’s the pdf in the post I referenced above… :confused: Refreshed to make sure the upload sticks…

In case you are still looking to use a histogram,

I've found the best place to put the code for a histogram is under the "propertyChange" event handler on the rootContainer; then you can use the code below to tie the script to your source dataset, which you add as a custom property on the rootContainer:

if event.propertyName == 'dataset':