Reputation: 129
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
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
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
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:
e
, obtain its count as you're currently doing.e
and set the "unique" flag to true
.false
.At the end, just return the mode if "unique" is true
, otherwise return NaN
.
Upvotes: 0