Search code examples
javajava-7meanmedianmode

Mean, Median and Mode


Hi I'm having problem with my codes on the Modal value. Here is my code:

public class basicStatistics {
 public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    int n = in.nextInt();
    int[] arr = new int[n];
    int[] arrCount = new int[n];
    int maxCount = 0;
    double median = 0.0, mean = 0.0;
    for(int i = 0; i < n; i++) {
        arr[i] = in.nextInt();
        mean = mean + arr[i];
    }
    mean = mean/n;
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++){
            if(arr[i] == arr[j]){
                arrCount[i]++;
            }
        if(maxCount < arrCount[i])
            maxCount = i;
        }
    }
    Arrays.sort(arr);
    if(n%2 == 0) {
        median = arr[n/2] + arr[n/2-1]; 
        median = median/2;
    } else {
        median = arr[n/2];
    }
    System.out.printf("%.1f\n", mean);
    System.out.printf("%.1f\n", median);
    System.out.println(arr[maxCount]);
 }
}

The issue is I can't get the Modal value right when the input is this: http://pastebin.com/HCqTrimY I used pastebin as it inputs 2500 numbers. My code outputs the array value 259 instead of 2184 for Mode however if the input is n = 10 and the numbers = 64630 11735 14216 99233 14470 4978 73429 38120 51135 67060 I get the mode value correct or for other scenario.

I also wanted to transform the code to a more efficient one utilizing the Java8 structure. Thank you in advanced!


Solution

  • If you want to use Java 8, you can make that code a lot shorter. First, the mean: Here you can create a stream from your int-array arr and then just call the average method:

    double mean = IntStream.of(arr).average().getAsDouble();
    

    The median is pretty much the same as in your code; I just put it on fewer lines by using a ternary expression (... ? ... : ...) instead of a multi-line if/else:

    Arrays.sort(arr);
    int len = arr.length;
    double median = len % 2 == 0 ? (arr[len/2-1] + arr[len/2]) / 2. : arr[len/2];
    

    The mode is a bit more difficult, last but not least since you seem to need the smallest mode in case there are multiple numbers appearing most often. While your double-for-loop approach would work, too, it has quadratic complexity, which could become a problem for larger data sets. Instead, you should use a Map to count the different numbers. In Java 8, this can be done very easily using Collectors.groupingBy and Collectors.counting. Then, just determine the max of the counts, filter the numbers that have that count, and get the min of those.

    Map<Integer, Long> counts = IntStream.of(arr).boxed()
            .collect(Collectors.groupingBy(x -> x, Collectors.counting()));
    Long max = counts.values().stream().max(Comparator.naturalOrder()).get();
    int mode = counts.entrySet().stream()
            .filter(e -> e.getValue().equals(max))
            .min(Comparator.comparing(Entry::getKey)).get().getKey();