1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tradefed.util;
17 
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.LinkedList;
21 import java.util.List;
22 
23 /**
24  * A small utility class that calculates a few statistical measures given a numerical dataset.  The
25  * values are stored internally as {@link Double}s.
26  */
27 public class SimpleStats {
28     private List<Double> mData = new LinkedList<Double>();
29 
30     // cached values
31     private double mSum = 0;
32 
33     /**
34      * Add a number of measurements to the dataset.
35      *
36      * @throws NullPointerException if the collection contains any {@code null} elements
37      */
addAll(Collection<? extends Double> c)38     public void addAll(Collection<? extends Double> c) {
39         for (Double meas : c) {
40             if (meas == null) {
41                 throw new NullPointerException();
42             }
43             add(meas);
44         }
45     }
46 
47     /**
48      * Add a measurement to the dataset.
49      */
add(double meas)50     public void add(double meas) {
51         mData.add(meas);
52         mSum += meas;
53     }
54 
55     /**
56      * Retrieve the dataset.
57      */
getData()58     public List<Double> getData() {
59         return mData;
60     }
61 
62     /**
63      * Check if the dataset is empty.
64      */
isEmpty()65     public boolean isEmpty() {
66         return mData.isEmpty();
67     }
68 
69     /**
70      * Check how many elements are in the dataset.
71      */
size()72     public int size() {
73         return mData.size();
74     }
75 
76     /**
77      * Calculate and return the mean of the dataset, or {@code null} if the dataset is empty.
78      */
mean()79     public Double mean() {
80         if (isEmpty()) {
81             return null;
82         }
83 
84         return mSum / size();
85     }
86 
87     /**
88      * Calculate and return the median of the dataset, or {@code null} if the dataset is empty.
89      */
median()90     public Double median() {
91         if (isEmpty()) {
92             return null;
93         }
94 
95         Collections.sort(mData);
96         if ((mData.size() & 0x1) == 1) {
97             // odd count of items, pick the middle element.  Note that we don't +1 since indices
98             // are zero-based rather than one-based
99             int idx = size() / 2;
100             return mData.get(idx);
101         } else {
102             // even count of items, average the two middle elements
103             int idx = size() / 2;
104             return (mData.get(idx - 1) + mData.get(idx)) / 2;
105         }
106     }
107 
108     /**
109      * Return the minimum value in the dataset, or {@code null} if the dataset is empty.
110      */
min()111     public Double min() {
112         if (isEmpty()) {
113             return null;
114         }
115 
116         Collections.sort(mData);
117         return mData.get(0);
118     }
119 
120     /**
121      * Return the maximum value in the dataset, or {@code null} if the dataset is empty.
122      */
max()123     public Double max() {
124         if (isEmpty()) {
125             return null;
126         }
127 
128         Collections.sort(mData);
129         return mData.get(size() - 1);
130     }
131 
132     /**
133      * Return the standard deviation of the dataset, or {@code null} if the dataset is empty.
134      * <p />
135      * Note that this method calculates the population standard deviation, not the sample standard
136      * deviation.  That is, it assumes that the dataset is entirely contained in the
137      * {@link SimpleStats} instance.
138      */
stdev()139     public Double stdev() {
140         if (isEmpty()) {
141             return null;
142         }
143 
144         Double avg = mean();
145         Double ssd = 0.0;  // sum of squared differences
146         for (Double meas : mData) {
147             Double diff = meas - avg;
148             ssd += diff * diff;
149         }
150 
151         return Math.sqrt(ssd / size());
152     }
153 
154     /**
155      * return the average value of the samples that are within one stdev
156      * e.g
157      * 2.55 50.3 50.4 48.5 50.1 29.8 30 46 48 49
158      * average: 40.45, stdev: 15.54
159      * average of the values within one stdev is: 44.67
160      */
meanOverOneStandardDeviationRange()161     public Double meanOverOneStandardDeviationRange() {
162         if (isEmpty()) {
163             return null;
164         }
165 
166         Double avg = mean();
167         Double std = stdev();
168         Double upper = avg + std;
169         Double lower = avg - std;
170         Double sum = 0.0;
171         int count = 0;
172         for (Double meas : mData) {
173             if (meas > lower && meas < upper) {
174                 sum += meas;
175                 count++;
176             }
177         }
178         return sum / count;
179     }
180 }
181 
182