Reduction by Java Stream

For business analysis, it is often useful to reduce a list of numbers to a single value.

"What is the sum of product A sold among all the invoices?"
"What is the median price among all the houses sold in Toronto last month?"
"What is the average annual inflation over the last 10 years?"

This type of queries is reduction operation that can be handled by the Stream class.  Below are some examples to help memorizing how the reduce method is used.

Here are three different functions of calculating the sum of a list of integers. The first function is a typical for-loop.  The second and third produce the same result by using stream's reduce. Compare the functions for mnemonic. 

List<Integer> list = Arrays.asList(2, 4, 6, 8, 10, 12, 14, 16);	

Function<List<Integer>, Integer> sumMethod1 = (numbers) -> {
	int sum = 0;
	for (Integer x : numbers) sum += x;
	return sum;
};		
System.out.println ("sum = " + sumMethod1.apply(list));
		
Function<List<Integer>, Integer> sumMethod2 = (numbers) -> 
	numbers.stream().reduce(0, (a, b) -> a + b);
	
System.out.println ("sum = " + sumMethod2.apply(list));

Function<List<Integer>, Integer> sumMethod3 = (numbers) ->
	numbers.stream().reduce(0, Integer::sum);
	
System.out.println ("sum = " + sumMethod3.apply(list));

The output is:
sum = 72
sum = 72
sum = 72

Here is the fourth function of calculating the same. Notice that the reduce method used in this function does not require an initial value.  In this case, the return value will be an Optional.  Given the absence of initial value, there may not be a valid sum available. This is due to the scenario of an empty list of numbers.

//list = Arrays.asList();
Function<List<Integer>, Optional<Integer>> sumMethod4 = (numbers) ->
        numbers.stream().reduce(Integer::sum);	// No initial value is required

System.out.print("sum= ");
sumMethod4.apply(list)
        .ifPresentOrElse(System.out::println, ()->System.out.println("not available"));

For finding the maximum value of a list of numbers, we can use the max function; conversely use min. For this type of operations, there is no initial value required.

Function<List<Integer>, Optional<Integer>> findMax = (numbers) ->
        numbers.stream().reduce(Integer::max);

System.out.print("max= ");
findMax.apply(list)
        .ifPresentOrElse(System.out::println, ()->System.out.println("not available"));

(For median, refer to the blog post: Finding Median by Stream)

Here are a couple of functions of calculating geometric mean.  The first one is a typical for-loop; and the other one stream reduction. To make this example interesting, I included the annual inflation rates (2011 - 2020) of Canada and U.S. respectively.

List<Double> benchmark;

// Dummy percentage numbers
// benchmark = Arrays.asList(10d, 20d, 30d);

// Annual inflation of Canada from 2011 to 2020
benchmark = Arrays.asList(2.30, 0.83, 1.24, 1.47, 1.61, 1.50, 1.87, 1.99, 2.25, 0.73);

// Annual inflation of U.S. from 2011 to 2020
// benchmark = Arrays.asList(2.96, 1.74, 1.50, 0.76, 0.73, 2.07, 2.11, 1.91, 2.29, 1.36);

Function<List<Double>, Double> findGeometricMeanMethod1 = (percentageNumbers) -> {
	double multiplier = 1;
	for (Double x : percentageNumbers)
		multiplier *= (1 + x / 100d);			
	return (Math.pow(multiplier, 1d / percentageNumbers.size()) - 1) * 100;
};
System.out.printf("geometric mean = %.2f%n", findGeometricMeanMethod1.apply(benchmark));		

Function<List<Double>, Double> findGeometricMeanMethod2 = (percentageNumbers) -> {
	double multiplier = percentageNumbers.stream().reduce(1d, (a, b) -> (a * ( 1 + b / 100d) ));
	return (Math.pow(multiplier, 1d / percentageNumbers.size()) - 1) * 100;							 			
};
System.out.printf("geometric mean = %.2f%n", findGeometricMeanMethod2.apply(benchmark));

For the list of inflation rates of Canada, the output is:
geometric mean = 1.58
geometric mean = 1.58

There are other methods in the Stream class that also carry out reduction-type of operations.  Those methods include count, min, max, and collect.


Comments

Popular posts from this blog

Finding Median by Stream

Factorial by Different Styles in Java