assurance-tunnel
assurance-tunnel
assurance-tunnel
assurance-tunnel

# [SOLVED] ​Measuring average of a numeric value

• Hi,

Has anybody built a simple patch to measure the average of a numeric value continuously time-wise? I would be grateful finding a simple solution.

• @lauri
Do you need the average? or are you looking to filter a stream to smoother the values?
If you are filtering you may want to look at the 'Smoother' actor, or the 'Filter' actor.

If you need the exact average of a set of numbers, can you tell me how many values you want used to calculate the average?

• @DusX Thank you for your reply!

I actually need the real average of a value that changes in time. Let’s say there’s a value that changes once per second and I want to have the average of that value for instance from a period of an hour.

• @lauri

Hello, unfortunately I think there is no easy way to do it internally in Isadora (for my knowledge).

But you can do the calculation externally, using application with calculation possibilities, as Max/Msp or TouchDesigner sending/receiving data by OSC.

Here is a test using Max/Msp, if you are interested you can use the test version of max to make an executable.

As you see the averaging is approaching 50…

• @lauri

second proposition with TouchDesigner

the python inside chopexec1

def onValueChange(channel, sampleIndex, val, prev):
op('table1')[1,'actualValue'] = val
op('table1')[1,'actualNumber'] += 1
op('table1')[1,'actualSum'] += op('table1')[1,'actualValue']
op('table1')[1,'average'] = op('table1')[1,'actualSum']/op('table1')[1,'actualNumber']
op('constant1').par.value0 = op('table1')[1,'average']
return

• i think it could be done in Isadora with Counter, Gates, Comparator, Trigger Values and Calculators. particularly if there is a regular interval between changes in data. i don't have time to look at it today, but it seems like a good challenge...

• @dbini

and the Isadora version :~)

average.izz

• @jhoepffner

that's the one. much more simple than i expected. (i haven't really explored the Float Counter yet, but i guessed it would be useful in this situation) :)

• @dbini

Yes but there is no easy solution inside Isadora for very long time or for exact time of average… If you need a very accurate solution to make an average with the last 3200 sample (as in the Lauri answer, 1 hour of second sample), it would be necessary to have the possibility to store a 3200 (or more) items list. For me it is doable easily in Max/Msp (item list) or TD (Python array) but I dont know an easy way to do it in Isadora. Javascript (in my knowledge) cannot because the lack of global variable, data array is limited in number of items and you cannot move all the sample as you can do it with array in Python or list in Max. I think it would be also easy in Processing with OSC in/out but I have to see the weather outside !

Another chalenge for json specialist.

• @dbini

40 sec interval, for this case.

r.

• Thank you ALL for your help! Yes, of course, that’s the solution, should have thought through it myself.

Anyway, here’s an image of my simple version, in this case, for measuring performance cycles. Seems to work well.

Thousand thanks again!

Lauri

• it's easily done with javascript..

I will write some code later today that will allow you to very flexibly setup the storing of values and calculation of there average.

• @lauri

Ok, I believe this Javascript code will do what you are looking for. https://gist.github.com/rwebbe...

```var dataSet = [];
function debug(printString){
if (DebugMode){
print(printString + "\n");
}
}
function main(){
// DO setup here: ONCE ONLY ************************************************
DebugMode = true; //example usage: debug("string" + var + "\n");
MaxValues = arguments[0]; // no Var makes global

debug("Start DataSet Length = " + dataSet.length + "\n\n");
// main function for LOOP **************************************************
main = function(){

if (dataSet.length >= MaxValues){
dataSet.splice(0, 1); // remove first value since buffer is at Max
}
dataSet.push(arguments[1]); // add last received value to end of array, increasing total.

debug("DataSet Length = " + dataSet.length + "\n");
//debug("DataSet = " + dataSet + "\n"); // gets big... but useful if you want to see all array values in smaller sets.

// reduce array using a function at add all values contained together.
var Sum = dataSet.reduce(function(accumulator, currentValue, currentIndex, array) {
return accumulator + currentValue;
});

debug("DataSet Sum = " + Sum + "\n");

var Average = Sum / dataSet.length;

debug("DataSet Avg = " + Average + "\n\n");

out = [Average] // set output array
return out; // return out array
};
// RUN ONCE FOR INIT ONLY
var out = []; // declare out
return out; // return from INIT state
}```

This will create an array that expands with every new value that is received and output the Average of all the received values.
It takes 2 inputs. The first is the Max number of values you want to hold for creating a sum (I tested with 3600).
If you receive 1 value per second, setting this to 60 would make it so the code always gives you the average for the last minute (older values are dropped, and if there are less than 60 values... say its been running for 5 seconds... it will use the 5 values).
You can of course set this number very high so the buffer/array covers a longer period of time. (this will use more system memory, but it should be very minimal).

Currently, I have set

`DebugMode = true;`

This will output some useful information to the 'Monitor' Window if you open it.
Once you have viewed this information, and are happy with the functioning of the code, change DebugMode to = false

• @jhoepffner

There is no lack of Globals in Javascript.
Declaring a variable without using the Var keyword makes it a global..
Using the Var keyword binds it to the scope of the current function.
In the case of the code I wrote, the array [] is set using the Var keyword, but its defined outside the Main functions scope, so it behaves as a global.

• @dusx

Thanks Ryan for the JS lesson and for the brillant demo.

• @DusX Thousand thanks for your help!!! This is a great solution!

However, I noticed one flaw with it: if the value does not change, then it is not registered as a new value, it registers a value when it differs from the previous one.

For example, if I measure the average of performance fps (see the attached image), the output value of fps tends to stay at the value set in the preferences. And in this case the JS actor registers only values deviating from that (and the set value, when it reaches that again). So, lets say the fps cannot keep the set value all the time, but instead goes (time to time) below more than above, then the received value is not really an average.

Another thing is that the JS actor cannot be reset, unless you exit the stage and return into it. But this is a minor thing, and I can manage without that feature.

I don't know Java, so I cannot really propose a solution. My apologies for that.

• @lauri
You are absolutely correct, submitting the same value doesn't work.
Luckily there is a very easy fix.
Increase the number of Inputs from 2 to 3. Mutate the 3rd input to a trigger (connect a trigger to it.. maybe from a trigger delay)
And then connect your data value source to the 3rd input as well.
This will cause the JS actor to see a new trigger each time a value is entered, even when the value doesn't change. Running the code as expected.

And a fast fix to add a reset, enter this code:

`if (arguments[3]){ dataSet = []; return; }`

after

`// main function for LOOP **************************************************    main = function(){`

So that it looks like:

`// main function for LOOP ************************************************** main = function(){ if (arguments[3]){ dataSet = []; return; } if (dataSet.length >= MaxValues){`

Then add another (4th) input, and mutate it to a trigger.
That trigger (input 4) will reset the array to empty.

• Thousand thanks! Works perfectly!

Mutating the 3rd input to a trigger was a bit difficult – in what order to connect the input – but in the end, yes, works flawlessly.

Thanks!!!

• @lauri

I wrote the attached user actor  a few months  ago to provide an ongoing average FPS calculated from the most recent 100 samples, but it can be used for any stream of numbers. You are welcome to use it.

Don

average-FPS.izz.zip