Skip to content

Commit fc5be1f

Browse files
authored
Merge pull request #48 from epics-base/Issue_41
Issue 41
2 parents 2807b4f + 15e39ec commit fc5be1f

26 files changed

Lines changed: 2013 additions & 308 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (C) 2010-18 diirt developers. See COPYRIGHT.TXT
3+
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
4+
*/
5+
package org.epics.util.stats;
6+
7+
/**
8+
* The statistics of a given set of numbers.
9+
* <p>
10+
* For the purpose of statistics calculation, NaNs should be skipped. That is,
11+
* they should not appear as minimum, maximum, average or stdDev, and shouldn't
12+
* even be included in the count. The number of elements (including NaNs)
13+
* will be available from the number set used to create the statistics. This
14+
* can be useful to determine whether the set actually contained any valid
15+
* values and therefore if there is anything to do.
16+
* <p>
17+
* The appropriate Statistics instance for
18+
* an unknown set, or for a set of NaN values, is null.
19+
*
20+
* @author carcassi
21+
*/
22+
public abstract class Statistics {
23+
24+
/**
25+
* The range of the values.
26+
*
27+
* @return the range
28+
*/
29+
public abstract Range getRange();
30+
31+
/**
32+
* The number of values (excluding NaN) included in the set.
33+
*
34+
* @return the number of values
35+
*/
36+
public abstract int getCount();
37+
38+
/**
39+
* The average value.
40+
*
41+
* @return the average value
42+
*/
43+
public abstract double getAverage();
44+
45+
/**
46+
* The standard deviation.
47+
*
48+
* @return the standard deviation
49+
*/
50+
public abstract double getStdDev();
51+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/**
2+
* Copyright (C) 2010-18 diirt developers. See COPYRIGHT.TXT
3+
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
4+
*/
5+
package org.epics.util.stats;
6+
7+
import java.util.Iterator;
8+
import java.util.List;
9+
import org.epics.util.array.CollectionNumber;
10+
import org.epics.util.array.IteratorNumber;
11+
12+
/**
13+
* Utility class to calculate statistical information.
14+
*
15+
* @author carcassi
16+
*/
17+
public class StatisticsUtil {
18+
19+
private static class StatisticsImpl extends Statistics {
20+
21+
private final int count;
22+
private final Range range;
23+
private final double average;
24+
private final double stdDev;
25+
26+
public StatisticsImpl(Range range, int count, double average, double stdDev) {
27+
this.count = count;
28+
this.range = range;
29+
this.average = average;
30+
this.stdDev = stdDev;
31+
}
32+
33+
@Override
34+
public int getCount() {
35+
return count;
36+
}
37+
38+
@Override
39+
public Range getRange() {
40+
return range;
41+
}
42+
43+
@Override
44+
public double getAverage() {
45+
return average;
46+
}
47+
48+
@Override
49+
public double getStdDev() {
50+
return stdDev;
51+
}
52+
53+
}
54+
55+
/**
56+
* Calculates data statistics, excluding NaN values.
57+
*
58+
* @param data the data
59+
* @return the calculated statistics
60+
*/
61+
public static Statistics statisticsOf(CollectionNumber data) {
62+
IteratorNumber iterator = data.iterator();
63+
if (!iterator.hasNext()) {
64+
return null;
65+
}
66+
int count = 0;
67+
double min = iterator.nextDouble();
68+
while (Double.isNaN(min)) {
69+
if (!iterator.hasNext()) {
70+
return null;
71+
} else {
72+
min = iterator.nextDouble();
73+
}
74+
}
75+
double max = min;
76+
double total = min;
77+
double totalSquare = min*min;
78+
count++;
79+
80+
while (iterator.hasNext()) {
81+
double value = iterator.nextDouble();
82+
if (!Double.isNaN(value)) {
83+
if (value > max)
84+
max = value;
85+
if (value < min)
86+
min = value;
87+
total += value;
88+
totalSquare += value*value;
89+
count++;
90+
}
91+
}
92+
93+
double average = total/count;
94+
double stdDev = Math.sqrt(totalSquare / count - average * average);
95+
96+
return new StatisticsImpl(Range.of(min, max), count, average, stdDev);
97+
}
98+
99+
/**
100+
* Aggregates statistical information.
101+
*
102+
* @param data a list of statistical information
103+
* @return the aggregate of all
104+
*/
105+
public static Statistics statisticsOf(List<Statistics> data) {
106+
if (data.isEmpty())
107+
return null;
108+
109+
Iterator<Statistics> iterator = data.iterator();
110+
if (!iterator.hasNext()) {
111+
return null;
112+
}
113+
Statistics first = null;
114+
while (first == null && iterator.hasNext()) {
115+
first = iterator.next();
116+
}
117+
if (first == null)
118+
return null;
119+
120+
int count = first.getCount();
121+
Range range = first.getRange();
122+
double total = first.getAverage() * first.getCount();
123+
double totalSquare = (first.getStdDev() * first.getStdDev() + first.getAverage() * first.getAverage()) * first.getCount();
124+
125+
while (iterator.hasNext()) {
126+
Statistics stats = iterator.next();
127+
range = range.combine(stats.getRange());
128+
total += stats.getAverage() * stats.getCount();
129+
totalSquare += ( stats.getStdDev() * stats.getStdDev() + stats.getAverage() * stats.getAverage() ) * stats.getCount();
130+
count += stats.getCount();
131+
}
132+
133+
double average = total/count;
134+
double stdDev = Math.sqrt(totalSquare / count - average * average);
135+
136+
return new StatisticsImpl(range, count, average, stdDev);
137+
}
138+
139+
/**
140+
* Creates the statistics, excluding NaN values, but the values
141+
* are actually calculated when requested.
142+
*
143+
* @param data the data
144+
* @return the calculated statistics
145+
*/
146+
public static Statistics lazyStatisticsOf(CollectionNumber data) {
147+
return new LazyStatistics(data);
148+
}
149+
150+
private static class LazyStatistics extends Statistics {
151+
152+
private Statistics stats;
153+
private CollectionNumber data;
154+
155+
public LazyStatistics(CollectionNumber data) {
156+
this.data = data;
157+
}
158+
159+
private void calculateStats() {
160+
if (stats == null) {
161+
stats = statisticsOf(data);
162+
data = null;
163+
}
164+
}
165+
166+
@Override
167+
public int getCount() {
168+
calculateStats();
169+
return stats.getCount();
170+
}
171+
172+
@Override
173+
public double getAverage() {
174+
calculateStats();
175+
return stats.getAverage();
176+
}
177+
178+
@Override
179+
public double getStdDev() {
180+
calculateStats();
181+
return stats.getStdDev();
182+
}
183+
184+
@Override
185+
public Range getRange() {
186+
calculateStats();
187+
return stats.getRange();
188+
}
189+
190+
}
191+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright (C) 2010-18 diirt developers. See COPYRIGHT.TXT
3+
* All rights reserved. Use is subject to license terms. See LICENSE.TXT
4+
*/
5+
package org.epics.util.stats;
6+
7+
import java.util.ArrayList;
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import org.epics.util.array.ArrayDouble;
11+
import org.junit.Test;
12+
import static org.junit.Assert.*;
13+
import static org.hamcrest.CoreMatchers.*;
14+
15+
16+
/**
17+
*
18+
* @author carcassi
19+
*/
20+
public class StatisticsUtilTest {
21+
22+
@Test
23+
public void statisticsOf1() {
24+
Statistics stats = StatisticsUtil.statisticsOf(ArrayDouble.of(1.0));
25+
assertThat(stats.getAverage(), equalTo(1.0));
26+
assertThat(stats.getStdDev(), equalTo(0.0));
27+
assertThat(stats.getRange().getMinimum(), equalTo(1.0));
28+
assertThat(stats.getRange().getMaximum(), equalTo(1.0));
29+
assertThat(stats.getCount(), equalTo(1));
30+
}
31+
32+
@Test
33+
public void statisticsOf2() {
34+
Statistics stats = StatisticsUtil.statisticsOf(ArrayDouble.of(1, 3, 5, -1, 7));
35+
assertThat(stats.getAverage(), equalTo(3.0));
36+
assertThat(stats.getStdDev(), equalTo(2.8284271247461903));
37+
assertThat(stats.getRange().getMinimum(), equalTo(-1.0));
38+
assertThat(stats.getRange().getMaximum(), equalTo(7.0));
39+
assertThat(stats.getCount(), equalTo(5));
40+
}
41+
42+
@Test
43+
public void statisticsOf3() {
44+
List<Statistics> list = new ArrayList<Statistics>();
45+
for (int i = 0; i < 10; i++) {
46+
list.add(StatisticsUtil.statisticsOf(ArrayDouble.of(i)));
47+
}
48+
Statistics stats = StatisticsUtil.statisticsOf(list);
49+
assertThat(stats.getAverage(), equalTo(4.5));
50+
assertThat(stats.getStdDev(), equalTo(2.8722813232690143));
51+
assertThat(stats.getRange().getMinimum(), equalTo(0.0));
52+
assertThat(stats.getRange().getMaximum(), equalTo(9.0));
53+
assertThat(stats.getCount(), equalTo(10));
54+
}
55+
56+
@Test
57+
public void statisticsOf4() {
58+
Statistics stats = StatisticsUtil.statisticsOf(ArrayDouble.of(1, 3, 5, Double.NaN, -1, 7));
59+
assertThat(stats.getAverage(), equalTo(3.0));
60+
assertThat(stats.getStdDev(), equalTo(2.8284271247461903));
61+
assertThat(stats.getRange().getMinimum(), equalTo(-1.0));
62+
assertThat(stats.getRange().getMaximum(), equalTo(7.0));
63+
assertThat(stats.getCount(), equalTo(5));
64+
}
65+
66+
@Test
67+
public void statisticsOf5() {
68+
Statistics stats = StatisticsUtil.statisticsOf(ArrayDouble.of(1, 3, 5, -1, 7));
69+
stats = StatisticsUtil.statisticsOf(Arrays.asList(stats));
70+
assertThat(stats.getAverage(), equalTo(3.0));
71+
assertThat(stats.getStdDev(), equalTo(2.8284271247461903));
72+
assertThat(stats.getRange().getMinimum(), equalTo(-1.0));
73+
assertThat(stats.getRange().getMaximum(), equalTo(7.0));
74+
assertThat(stats.getCount(), equalTo(5));
75+
}
76+
}
Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
/**
2-
* Copyright information and license terms for this software can be
3-
* found in the file LICENSE.TXT included with the distribution.
4-
*/
5-
package org.epics.vtype;
6-
7-
/**
8-
* A value that provides an alarm.
9-
*
10-
* @author carcassi
11-
*/
12-
interface AlarmProvider {
13-
14-
/**
15-
* The alarm associated with this value.
16-
*
17-
* @return the alarm; not null
18-
*/
19-
public Alarm getAlarm();
20-
}
1+
/**
2+
* Copyright information and license terms for this software can be
3+
* found in the file LICENSE.TXT included with the distribution.
4+
*/
5+
package org.epics.vtype;
6+
7+
/**
8+
* A value that provides an alarm.
9+
*
10+
* @author carcassi
11+
*/
12+
public interface AlarmProvider {
13+
14+
/**
15+
* The alarm associated with this value.
16+
*
17+
* @return the alarm; not null
18+
*/
19+
public Alarm getAlarm();
20+
}

0 commit comments

Comments
 (0)