3

I'm a design teacher trying to help a student with a programming challenge, so I code for fun, but I'm no expert.

She needs to find the mode (most frequent value) in a dataset built using data from sensors coupled to an Arduino, and then activate some functions based on the result.

We've got most of it figured out, except how to calculate the mode in Arduino. I found the post Get the element with the highest occurrence in an array that solves the problem in JavaScript, but I haven't been able to "port" it.

Community
  • 1
  • 1
Sergio Majluf
  • 1,355
  • 13
  • 18
  • What form does the 'dataset' you refer to take? Is it an (Arduino) array of integers? – Matthew Murdoch Oct 26 '11 at 21:28
  • Hi Matthew. We haven't coded it yet, but yes, it will probably be an array of integers, although we might take the float way too. She (the student) will measure an object's speed, and act upon that. For the scale prototype, the speed will be also "scaled down", so we will have to test if integers are fine, or if we should take decimal numbers for the evaluation. – Sergio Majluf Oct 27 '11 at 02:55
  • If you're using a floating point number I'd be concerned that there may be no single 'mode' (i.e. every result could be different). Is this likely to be a problem or can we assume that that can't happen in this case? – Matthew Murdoch Oct 27 '11 at 13:07

2 Answers2

3

I've used a HashMap to replace the js {} dynamic object instance and floats, but @weberik's port looks more straightforward.

void setup() {
    int numValues = 10;
    float[] values = new float[numValues]; //Create an empty sample array
    for(int i = 0 ; i < numValues ; i++) values[i] = random(0.0,100.0); //Populate it with random values.
    println("mode: " + mode(values));
}

float mode(float[] source) {
    if (source.length == 0)
        return -1;
    HashMap<Float,Integer> modeMap = new HashMap<Float,Integer>();
    float result = source[0];
    int maxCount = 1;
    for (int i = 0; i < source.length; i++) {
        float el = source[i];
        if (modeMap.get(el) == null)
            modeMap.put(el,1);
        else
            modeMap.put(el,modeMap.get(el)+1);
        if (modeMap.get(el) > maxCount) {
            result = el;
            maxCount = modeMap.get(el);
        }
    }
    return result;
}

You've mentioned sensor input, so I presume data will be sampled continuously, so values could be stored at a certain interval, then sent to Processing for the mode. Just a wild guess, but isn't she looking to average/smooth out sensor readings a bit? If so, she could cache a few values (say 10) in an array in Arduino and get the average everytime a new values is added:

int vals[10]; //Array to store caches values.

void setup() {
    Serial.begin(9600);
    for (int i=0 ; i < 10 ; i++) 
        vals[i] = 0; //Init with zeroes
}

void loop() {
    delay(100);
    int currentVal = average(analogRead(0));
    //Serial.print(currentVal,BYTE);
    Serial.println(currentVal);
}

int average(int newVal) {
    int total = 0; //Used to store the addition of all currently cached values
    for(int i = 9; i > 0; i--) { //Loop backwards from the one before last to 0
        vals[i] = vals[i-1]; //Overwrite the prev. value with the current(shift values in array by 1)
        total += vals[i]; //Add to total
    }
    vals[0] = newVal; //Add the newest value at the start of the array
    total += vals[0]; //Add that to the total as well
    return total *= .1; //Get the average (for 10 elemnts) same as total /= 10, but multiplication is faster than division.
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • The thought of using a template on a micrcontroller makes my mind bleed. The inefficiencies are painful. On that note, you did show how to port the code over, if it will compile on arduino, I am not sure. – Kortuk Oct 26 '11 at 00:49
  • Thanks George, very thoughtful response. Indeed she would have to average the readings a bit, and your solution is clear and made me learn about HashMaps! Do you think it might be done in Arduino alone (excluding the Processing part)? Don't show me code, but the answer :) – Sergio Majluf Oct 26 '11 at 10:31
  • Kortuk, you are right about efficiency, but that's not the target for Arduino...it's supposed to make it simple/less scary for anyone with little or no programming experience to use a microcontroller. Regarding the ported code, that is for Processing/Java, not Arduino, so it will no run on Arduino obviosuly, but values can be sent from Arduino to Processing via the built-in Serial library. – George Profenza Oct 26 '11 at 20:27
  • Thanks George. In the end I took an aproach based on the previous response, however yours led me to learn about Hash maps! – Sergio Majluf Nov 22 '11 at 00:57
2

I ported the code from your linked post to Processing, but it's limited to int arrays. I hope that helps.

void setup()
{
    int[] numbers = {1, 2, 3, 2, 1, 1, 1, 3, 4, 5, 2};
    println(mode(numbers));
}


int mode(int[] array) {
    int[] modeMap = new int [array.length];
    int maxEl = array[0];
    int maxCount = 1;

    for (int i = 0; i < array.length; i++) {
        int el = array[i];
        if (modeMap[el] == 0) {
            modeMap[el] = 1;
        }
        else {
            modeMap[el]++;
        }

        if (modeMap[el] > maxCount) {
            maxEl = el;
            maxCount = modeMap[el];
        }
    }
    return maxEl;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
weberik
  • 2,636
  • 1
  • 18
  • 11
  • Thank you weberik, your solution is clear and easy tu follow! – Sergio Majluf Oct 26 '11 at 10:24
  • 1
    Am I correct in saying that this code requires that the elements in the original array be smaller than the length of the original array? – j_v_wow_d Aug 29 '15 at 14:36
  • This solution is both wrong and not a port from the original JS code. In the original JS code `modeMap` is a map (a JavaScript Object) and a new entry (key) for the current mode will be automatically added if the key is not already present. As already stated by @j_v_wow_d, this code will only work *by chance* and only if the values of the elements of the array are always smaller than the length of the array. – mancho Sep 04 '20 at 08:41