Tfarcraw IIV
Tfarcraw IIV

Reputation: 129

Finding the mode of an array Java

I've got to find the mode of an array. I am a bit embarrassed to admit that I've been stuck on this for a day. I think I've overthought it a bit - my method just gets longer and longer. The real issue that I keep running into is that when there isn't one mode (two numbers appear with the same frequency) I need to return Double.NaN.

Here's what I've tried:

private double[] data = {1, 1, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9};

if(data.length != 0){

        double maxValue = -1;
        int maxCount = 0;
        for(int i = 0; i < data.length; i++) {
            int count = 0;
            for(int j = 0; j < data.length; j++) {
                if(data[j] == data[i]) {
                    count++;
                }
            }

            if(count > maxCount) {
                maxValue = (int) data[i];
                maxCount = count;
            }
        }
        return maxValue;

    }else{

        return Double.NaN;

    }

This actually returns the mode, but it can't deal with two modes. Here's my most recent attempt, but it's only half complete:

private double[] data = {1, 1, 2, 2, 2, 3, 4, 5, 5, 5, 5, 5, 6, 7, 7, 8, 8, 8, 8, 8, 9, 9, 9};

public void mode(){

    int[] frequency = new int[data.length];

    double[] vals = new double[data.length];

    for(int i = 0; i < data.length; i++){
        frequency[i] = occursNumberOfTimes(data[i]);
    }

    boolean uniform = false;

    for(int g = 0; g < frequency.length && !uniform; g++){
        if(frequency[0] != frequency[g]){
            uniform = false;
        }

    int[] arr = new int[frequency.length-1];

    for(int j = 1; j < frequency.length; j++){

        if(frequency[j] > frequency[j-1]){

            int mod = 0;

            for(int k = 0; k < arr.length; k++){
                if(k == j){
                    mod += 1;
                    arr[k] = frequency[k + mod];
                }else{
                    arr[k] = frequency[k + mod];    
                }
            }
        }
    }
    frequency = arr;
    }
}

private int occursNumberOfTimes(double value){

    int count = 0;

    for(int i = 0; i < data.length; i++){
        if(data[i] == value){
            count++;
        }
    }
    return count;
}

I sorta got lost in the second try, I just can't sort out how to deal with multiple modes. I've written out my thoughts, but I just don't know how. I can't use anything from the Arrays class, which is why I'm lost.

Upvotes: 2

Views: 8036

Answers (3)

Perdi Estaquel
Perdi Estaquel

Reputation: 846

Does it have to be efficient? If not:

double maxValue = -1.0d;
int maxCount = 0;
for (int i = 0; i < data.length; ++i) {
    double currentValue = data[i];
    int currentCount = 1;
    for (int j = i + 1; j < data.length; ++j) {
        if (Math.abs(data[j] - currentValue) < epsilon) {
            ++currentCount;
        } 
    }
    if (currentCount > maxCount) {
        maxCount = currentCount;
        maxValue = currentValue;
    } else if (currentCount == maxCount) {
        maxValue = Double.NaN;
    }
}
System.out.println("mode: " + maxValue);

Upvotes: 2

Tfarcraw IIV
Tfarcraw IIV

Reputation: 129

Here's my long, dumb solution. It works! It's a very roundabout way of getting the mode, but I'm really happy it works. I used the advice I got from some comments and looked at it differently. It was a frustrating few hours, but here it is:

public double mode2(){



    if(data.length != 0){

        int[] counts = new int[data.length];
        double[] vals = new double[data.length];

        for(int l = 0; l < data.length; l++){

            counts[l] = 1;

        }

        for(int i = 0; i < data.length; i++){

            for(int j = 0; j < data.length; j++){

                if((data[i] == data[j]) && (i != j)){

                    vals[i] = data[i];
                    counts[i] += 1;

                }

            }

        }

        for(int i = 0; i < data.length; i++){

            for(int j = 0; j < data.length; j++){

                if((vals[i] == vals[j]) && (i != j)){

                    vals[i] = 0;
                    counts[i] = 0;

                }

            }
        }

        int counter = 0;

        for(int k = 0; k < data.length; k++){

            if(counts[k] != 0){

                counts[counter] = counts[k];
                vals[counter] = vals[k];
                counter++;

            }

        }


        int[] compactCounts = new int[counter];
        double[] compactVals = new double[counter];

        for(int k = 0; k < counter; k++){

            if(counts[k] != 0){

                compactCounts[k] = counts[k];
                compactVals[k] = vals[k];

            }else{

                break;

            }

        }

        for(int g = 1; g < compactVals.length; g++){

            if(compactCounts[g] > compactCounts[g-1]){

                compactCounts[g-1] = 0;
                compactVals[g-1] = 0;

            }

        }

        for(int g = 0; g < compactVals.length-1; g++){

            if(compactCounts[g] > compactCounts[g+1]){

                compactCounts[g+1] = 0;
                compactVals[g+1] = 0;

            }

        }

        int counterTwo = 0;

        for(int k = 0; k < compactCounts.length; k++){

            if(compactCounts[k] != 0){

                compactCounts[counterTwo] = compactCounts[k];
                compactVals[counterTwo] = vals[k];
                counterTwo++;

            }

        }



        int[] compactCountsTwo = new int[counterTwo];
        double[] compactValsTwo = new double[counterTwo];

        for(int k = 0; k < counterTwo; k++){

            if(counts[k] != 0){

                compactCountsTwo[k] = compactCounts[k];
                compactValsTwo[k] = compactVals[k];

            }else{

                break;

            }
        }

        //now populated compactTwos

        //We're now setting some lesser values to 0

        for(int g = 1; g < compactValsTwo.length; g++){

            if(compactCountsTwo[g] > compactCountsTwo[g-1]){

                compactCountsTwo[g-1] = 0;
                compactValsTwo[g-1] = 0;

            }

        }


        //now setting other lesser values to 0


        for(int g = 0; g < compactValsTwo.length-1; g++){
            if(compactCountsTwo[g] > compactCountsTwo[g+1]){
                compactCountsTwo[g+1] = 0;
                compactValsTwo[g+1] = 0;

            }

        }


        //calling methods to shorten our arrays by dropping indexes populated by zeroes

        compactValsTwo = doubleTruncator(compactValsTwo);
        compactCountsTwo = intTruncator(compactCountsTwo);


        //now setting some lesser values to 0

        for(int g = 1; g < compactValsTwo.length; g++){

            if(compactCountsTwo[g] > compactCountsTwo[g-1]){

                compactCountsTwo[g-1] = 0;
                compactValsTwo[g-1] = 0;

            }

        }


        //now setting other lesser values to 0


        for(int g = 0; g < compactValsTwo.length-1; g++){
            if(compactCountsTwo[g] > compactCountsTwo[g+1]){
                compactCountsTwo[g+1] = 0;
                compactValsTwo[g+1] = 0;

            }

        }


        //calling methods to shorten our arrays by dropping indexes populated by zeroes

        compactValsTwo = doubleTruncator(compactValsTwo);
        compactCountsTwo = intTruncator(compactCountsTwo);


        if(compactValsTwo.length > 1){

            return Double.NaN;

        }else{

            return compactValsTwo[0];

        }

        }else{

            System.out.println("ISSUE");
            return Double.NaN;          
    }   
}


public double[] doubleTruncator(double[] a){

    int counter = 0;

    for(int k = 0; k < a.length; k++){

        if(a[k] != 0){

            a[counter] = a[k];
            counter++;

        }

    }

    double[] b = new double[counter];

    for(int i= 0; i < counter; i++){

        if(a[i] != 0){

            b[i] = a[i];

        }else{

            break;

        }

    }
    return b;

}


public int[] intTruncator(int[] a){

    int counter = 0;

    for(int k = 0; k < a.length; k++){

        if(a[k] != 0){

            a[counter] = a[k];
            counter++;

        }

    }

    int[] b = new int[counter];

    for(int i= 0; i < counter; i++){

        if(a[i] != 0){

            b[i] = a[i];

        }else{

            break;

        }

    }
    return b;

}

Big thanks to everybody who helped. I know it's not great (certainly not as good as the answer from @Perdi Estaquel), but I'm happy that I managed to do it.

Upvotes: 0

arshajii
arshajii

Reputation: 129547

You could track the two most common elements as was suggested in the comments, but another approach is to keep a boolean flag that indicates if the current most common element is unique. Then:

  1. For each array element e, obtain its count as you're currently doing.
  2. If that count is greater than the current max, set the the most common element (seen so far) to e and set the "unique" flag to true.
  3. Otherwise, if that count is equal to the current max, set the "unique" flag to false.

At the end, just return the mode if "unique" is true, otherwise return NaN.

Upvotes: 0

Related Questions