Best way to do heavy math operations in Ignition?

I know Jython is very powerful for scripting, but i was wondering if there’s any module (java or python) compatible in ignition that compares to the capabilites that numpy could have.

Best,

Consider building upon the JScience collection of libraries. All under a permissive license, suitable for redistribution within an Ignition module.

1 Like

Oh, nice find! :+1:

Also, depending on your needs, Apache Math is worth looking at.

I didn’t really find JScience myself. I learned about it when contributing to the now-defunct CompPad add-on for OpenOffice/LibreOffice.

1 Like

I came across this thread when looking for options on how to add some statistical math functions to ignition. I followed the Apache Math link, and it looks like there’s everything I could want there. However, I’m not sure how to go about adding the functionality to ignition.

Is this something that you need to make a module with? If so, how do you do that? I’m a bit surprised that someone hasn’t created a Math module, or ignition hasn’t added the functions to the core software, given the frequency with which questions regarding numpy are asked. Of course, I’m guessing my perception is a bit skewed after searching the forums for a while on this topic.

For what it’s worth, at the moment, I’m interested in mode, median, standard deviation, kurtosis, and skewness. I suppose it’s possible I’d be looking for more down the road sometime, but those would get me started.

If you’re willing to have gateway only access, you don’t need a module, but a module will absolutely be the easiest way to get access to Apache Math.

Honestly, the things you’re looking for are really easy to calculate in your own script module without Apache Math, especially if you add some functions to calculate the nth central moment (which, with your standard deviation calc, will give you kurtosis and skewness). And, of course, mean and std dev are already available in the expression language.

But adding those kind of math functions is a great idea, and you should suggest it at ideas.inductiveautomation.com. This is a great kind of project for me to give to new developers. :slight_smile:

1 Like

Yeah, I will probably try to code my own functions that I’m looking for. I’m just a little surprised that those kind of functions aren’t available already. I suppose data analysis like that might be a little outside the realm of traditional SCADA systems, but Ignition is so good at working with databases and scripting, it just makes sense to do it internally.

I added the idea. Here’s the link in case anyone else reading this wants to upvote.

Thanks!

I'd be hurt that no one mentioned this... if it wasn't for my huge ego... :wink:

2 Likes

I actually saw that post when I was looking around! However, it’s missing several of the functions I was looking for. And, worse, when I tried uploading my project to my 7.9.6 gateway, it complained that [global] was already existing on the gateway and let me rename it. I did, but it appeared to be an empty project. I couldn’t find any scripting assets in the project. I didn’t pursue it further as it didnt’ appear to have everything I needed anyway.

Import it by right-clicking on ‘Global’ at the top of the tree and select ‘Import’. Afterward, you should see it in the shared script library.

That being said, I’m always open to new functionality. I had originally hoped that it would be a collaborative effort, but so far it’s been a team of one. Heh.

Ohhhhh, that’s how you import that! I’ll have to give it a try!

1 Like

No worries, Brian!

Like I said I don’t mind working on new functionality. I just need to know what functionality to work on. :smiley:

1 Like

Below is a mode function I modified from an example I found on the web. I added it to your math script and it seems to be working. Give it a try and see what you think. I’m sure there’s other ways to be doing mode calculations (I’m no mathematician), but I think this will serve my purposes.

Probably the more proper thing to do instead of simple rounding for real values would be to calculate intervals to assign the data points to and then calculate a mode from that.

# Function to return all modes, this function takes into account multimodal distributions.
# Optional digits argument will round numbers to the specified decimal places.
# The rounding function is incompatible with string types.
# Function returns all modes as list or an empty list if such value doesn't exist.
def mode(l,digits=None):
    if len(l) > 1: #
        #Creates dictionary of values in l and their count
        d = {}
        for value in l:
            if digits != None:
            	value = round(value,digits)
            if value not in d:
                d[value] = 1
            else:
                d[value] += 1

        if len(d) == 1:
            return [value]
        else:
            # Finds most common value
            i = 0
            for value in d:
                if i < d[value]:
                    i = d[value]

            # All values with greatest number of occurrences can be a mode if:
            # other values with less number of occurrences exist
            modes = []
            counter = 0
            for value in d:
                if d[value] == i:
                    mode = (value, i)
                    modes.append(mode)
                    counter += mode[1] # Create the counter that sums the number of most common occurrences

            # Example [1, 2, 2, 3, 3]
            # 2 appears twice, 3 appears twice, [2, 3] are a mode
            # because sum of counter for them: 2+2 != 5
            if counter != len(l):
                return [mode[0] for mode in modes]
            else:
                return []
    else:
        return []

Some example results from the function.

>>> print shared.math.mode([1])
[]
>>> print shared.math.mode([1, 1])
[1]
>>> print shared.math.mode([1, 1, 2, 2])
[]
>>> print shared.math.mode([1, 2, 3, 4, 5])
[]
>>> print shared.math.mode([1, 1, 2, 2, 3, 3, 4])
[1, 2, 3]
>>> print shared.math.mode([1, 2, 3, 4, 4])
[4]
>>> print shared.math.mode(['string', 'string', 1])
['string']
>>> print shared.math.mode(['string', 'string', 1, 1, 1])
[1]
>>> print shared.math.mode([0.57099998,0.571367845,0.571367845,0.571657243,0.571657243],3)
[0.571]

I’m a bit confused on your output…

>>> print shared.math.mode([1, 1, 2, 2])
[]
gives an empty return set, but

[1, 2, 3]```
does not? It doesn't seem consistent.

I was a little confused about that myself. There’s a comment in the code:

# All values with greatest number of occurrences can be a mode if:
# other values with less number of occurrences exist

I haven’t done research on how modes should be handled with uniform distributions (like I said, not a mathematician). I just assumed that it was some edge case the programmer was handling. It’s one of the reasons I posted it to be picked apart :wink:

Edit: Interesting reference I found

Definitely agree with the “no consensus part”. LOL

Brings up a good thought for someone spec’ing a module (which may end up being me)… probably the best user experience is to allow an optional param to specify how this situation is handled. Because no matter how you handle it, someone will complain. :laughing:

Here’s what I came up with. include optional parameter to allow all modes vs. no mode for even distributions.

def mode(listIn, all_modes = 0):
  bins = list(set(listIn))
  counts = [listIn.count(x) for x in set(listIn)]
  maxCounts = max(counts)
  indices = [i for i, x in enumerate(counts) if x == maxCounts]

  dataOut = []
  if all_modes == 0:
    if len(set(counts)) != 1:
      for i in indices:
        dataOut.append(bins[i])
  else:
    for i in indices:
      dataOut.append(bins[i])
  
  return dataOut


1 Like

For what it’s worth, I’m modifying a statistics package that you may be interested in. More details coming. :slight_smile:

1 Like

Awesome! I haven’t had any time to play with this stuff today. Other duties and stuff…

If I could make a request, please include in the mode function, an optional parameter for either rounding or binning floats. The mean and median functions will work fine with floats in their raw form, but there’s a decent chance that an analog signal recorded to a float will have enough unique values over a short period to have all of the values be unique. Including the rounding/binning actions in the mode function should keep from having to loop through the data twice.