Skip to content

Commit

Permalink
Up to v0.5.4 -- Added Collection<Number>.average()
Browse files Browse the repository at this point in the history
  • Loading branch information
Tim Yates committed Nov 25, 2013
1 parent fb7131a commit 5132526
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 2 deletions.
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Obviously requires at least Groovy 2.0.5 (so that the extension system exists)

Usage:

@Grab( 'com.bloidonia:groovy-common-extensions:0.5.3' )
@Grab( 'com.bloidonia:groovy-common-extensions:0.5.4' )

and the following methods will be available to you:

Expand Down Expand Up @@ -276,3 +276,20 @@ And (passing a list of amounts):

// Note we run out of list 2 after the 'e', so just get the last 4 from list 1
assert c = [ 1, 'a', 'b', 2, 'c', 'd', 3, 'e', 4 ]

## Averages for collection of Numbers

static <V extends Number> AverageStats<V> average( Collection<V> collection )

Given a colelction of Numbers, ie:

def a = 1..10

We can get the `mean`, `median`, `variance` and `stdDev` wrapped in an Immutable `AverageStats` class by simply calling:

def stats = a.average()

assert stats.mean == 5.5
assert stats.median == 5.5
assert stats.variance == 8.25
assert String.format( '%.5g', avg.stdDev ) == '2.8723'
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ apply plugin: 'signing'
sourceCompatibility=1.6
targetCompatibility=1.6

version = '0.5.3'
version = '0.5.4'

repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -271,4 +271,51 @@ class CollectionExtensionMethods {
static <T> Iterator<T> transposedIterator( List<List<T>> lists, List<Integer> amounts ) {
new TransposingIterator( lists, amounts )
}

@groovy.transform.Immutable( knownImmutables=[ 'median' ])
public static class AverageStats<V extends Number> {
Double mean
V median
Double variance
Double stdDev

String toString() {
"AverageStats( mean:$mean, median:$median, variance:$variance, stdDev:$stdDev )"
}
}

/**
* Calculate the mean, median, variance and standard deviation of a Collection of
* Numbers
*
* <pre class="groovyTestCase">
* def avg = (1..10).average()
*
* assert avg.mean == 5.5
* assert avg.median == 5.5
* assert avg.variance == 8.25
* assert String.format( '%.5g', avg.stdDev ) == '2.8723'
* </pre>
*
* @param collection The {@code Collection} of {@code Number} elements to generate stats for
* @return A populated {@code AverageStats} object
*/
static <V extends Number> AverageStats<V> average( Collection<V> collection ) {
int size = collection.size()
V sum = collection.sum()
int zsize = size - 1
V median = size % 2 == 1 ?
collection.sort( false )[ ( zsize / 2 ).toInteger() ] :
collection.sort( false )[ [ Math.floor( zsize / 2 ), Math.ceil( zsize / 2.0 ) ]*.toInteger() ].sum() / 2
double mean = sum / size
double variance = 0

for( V num : collection ) {
variance += ( mean - num ) * ( mean - num )
}

variance /= size

new AverageStats( mean: mean, median: median, variance: variance, stdDev: Math.sqrt( variance ) )
}
}
39 changes: 39 additions & 0 deletions src/test/groovy/tests/CollectionTests.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,43 @@ class CollectionTests extends Specification {
rand.unique()
}

def 'check integer averages'() {
given:
def avg = [ 332, 42, 100 ].average()
expect:
avg.mean == 158
avg.median == 100
String.format( '%.7g', avg.variance ) == '15698.67'
String.format( '%.6g', avg.stdDev ) == '125.294'
}

def 'check integer averages with even number of elements'() {
given:
def avg = [ 332, 998, 42, 100 ].average()
expect:
avg.mean == 368
avg.median == 216
String.format( '%.7g', avg.variance ) == '144074.0'
String.format( '%.6g', avg.stdDev ) == '379.571'
}

def 'check BigInteger averages'() {
given:
def avg = [ 332G, 42G, 100G ].average()
expect:
avg.mean == 158
avg.median == 100G
String.format( '%.7g', avg.variance ) == '15698.67'
String.format( '%.6g', avg.stdDev ) == '125.294'
}

def 'check BigDecimal averages with even number of elements'() {
given:
def avg = [ 332.0G, 998.0G, 42.0G, 100.0G ].average()
expect:
avg.mean == 368
avg.median == 216.0G
String.format( '%.7g', avg.variance ) == '144074.0'
String.format( '%.6g', avg.stdDev ) == '379.571'
}
}

0 comments on commit 5132526

Please sign in to comment.