There are two ways to implement this that I use commonly. I am always working with realtime data, so this assumes continuous input. Here's some pseudo-code:
Using a trainable minmax:
define function peak:
// keeps the highest value it has received
define function trough:
// keeps the lowest value it has received
define function calibrate:
// toggles whether peak() and trough() are receiving values or not
define function scale:
// maps input range [trough.value() to peak.value()] to [0.0 to 1.0]
This function requires that you either perform an initial training phase (by using calibrate()) or that you re-train either at certain intervals or according to certain conditions. For instance, imagine a function like this:
define function outBounds (val, thresh):
if val > (thresh*peak.value()) || val < (trough.value() / thresh):
calibrate()
// peak and trough are normally not receiving values, but if outBounds() receives a value that is more than 1.5 times the current peak or less than the current trough divided by 1.5, then calibrate() is called which allows the function to recalibrate automatically.
Using an historical minmax:
var arrayLength = 1000
var histArray[arrayLength]
define historyArray(f):
histArray.pushFront(f) //adds f to the beginning of the array
define max(array):
// finds maximum element in histArray[]
return max
define min(array):
// finds minimum element in histArray[]
return min
define function scale:
// maps input range [min(histArray) to max(histArray)] to [0.0 to 1.0]
main()
historyArray(histArray)
scale(min(histArray), max(histArray), histArray[0])
// histArray[0] is the current element