1 /*
2  * Copyright (C) 2014 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 
17 package com.android.compatibility.common.util;
18 
19 import java.util.Arrays;
20 
21 /**
22  * Utilities for doing statistics
23  */
24 public class Stat {
25     /**
26      * Private constructor for static class.
27      */
Stat()28     private Stat() {}
29 
30     /**
31      * Collection of statistical propertirs like average, max, min, and stddev
32      */
33     public static class StatResult {
34         public double mAverage;
35         public double mMin;
36         public double mMax;
37         public double mStddev;
38         public int mDataCount;
StatResult(double average, double min, double max, double stddev, int dataCount)39         public StatResult(double average, double min, double max, double stddev, int dataCount) {
40             mAverage = average;
41             mMin = min;
42             mMax = max;
43             mStddev = stddev;
44             mDataCount = dataCount;
45         }
46     }
47 
48     /**
49      * Calculate statistics properties likes average, min, max, and stddev for the given array
50      */
getStat(double[] data)51     public static StatResult getStat(double[] data) {
52         double average = data[0];
53         double min = data[0];
54         double max = data[0];
55         for (int i = 1; i < data.length; i++) {
56             average += data[i];
57             if (data[i] > max) {
58                 max = data[i];
59             }
60             if (data[i] < min) {
61                 min = data[i];
62             }
63         }
64         average /= data.length;
65         double sumOfSquares = 0.0;
66         for (int i = 0; i < data.length; i++) {
67             double diff = average - data[i];
68             sumOfSquares += diff * diff;
69         }
70         double variance = sumOfSquares / (data.length - 1);
71         double stddev = Math.sqrt(variance);
72         return new StatResult(average, min, max, stddev, data.length);
73     }
74 
75     /**
76      * Calculate statistics properties likes average, min, max, and stddev for the given array
77      * while rejecting outlier +/- median * rejectionThreshold.
78      * rejectionThreshold should be bigger than 0.0 and be lowerthan 1.0
79      */
getStatWithOutlierRejection(double[] data, double rejectionThreshold)80     public static StatResult getStatWithOutlierRejection(double[] data, double rejectionThreshold) {
81         double[] dataCopied = Arrays.copyOf(data, data.length);
82         Arrays.sort(dataCopied);
83         int medianIndex = dataCopied.length / 2;
84         double median;
85         if (dataCopied.length % 2 == 1) {
86             median = dataCopied[medianIndex];
87         } else {
88             median = (dataCopied[medianIndex - 1] + dataCopied[medianIndex]) / 2.0;
89         }
90         double thresholdMin = median * (1.0 - rejectionThreshold);
91         double thresholdMax = median * (1.0 + rejectionThreshold);
92 
93         double[] validData = new double[data.length];
94         int index = 0;
95         for (int i = 0; i < data.length; i++) {
96             if ((data[i] > thresholdMin) && (data[i] < thresholdMax)) {
97                 validData[index] = data[i];
98                 index++;
99             }
100             // TODO report rejected data
101         }
102         return getStat(Arrays.copyOf(validData, index));
103     }
104 
105     /**
106      * return the average value of the passed array
107      */
getAverage(double[] data)108     public static double getAverage(double[] data) {
109         double sum = data[0];
110         for (int i = 1; i < data.length; i++) {
111             sum += data[i];
112         }
113         return sum / data.length;
114     }
115 
116     /**
117      * return the minimum value of the passed array
118      */
getMin(double[] data)119     public static double getMin(double[] data) {
120         double min = data[0];
121         for (int i = 1; i < data.length; i++) {
122             if (data[i] < min) {
123                 min = data[i];
124             }
125         }
126         return min;
127     }
128 
129     /**
130      * return the maximum value of the passed array
131      */
getMax(double[] data)132     public static double getMax(double[] data) {
133         double max = data[0];
134         for (int i = 1; i < data.length; i++) {
135             if (data[i] > max) {
136                 max = data[i];
137             }
138         }
139         return max;
140     }
141 
142     /**
143      * Calculate rate per sec for given change happened during given timeInMSec.
144      * timeInSec with 0 value will be changed to small value to prevent divide by zero.
145      * @param change total change of quality for the given duration timeInMSec.
146      * @param timeInMSec
147      */
calcRatePerSec(double change, double timeInMSec)148     public static double calcRatePerSec(double change, double timeInMSec) {
149         if (timeInMSec == 0) {
150             return change * 1000.0 / 0.001; // do not allow zero
151         } else {
152             return change * 1000.0 / timeInMSec;
153         }
154     }
155 
156     /**
157      * array version of calcRatePerSecArray
158      */
calcRatePerSecArray(double change, double[] timeInMSec)159     public static double[] calcRatePerSecArray(double change, double[] timeInMSec) {
160         double[] result = new double[timeInMSec.length];
161         change *= 1000.0;
162         for (int i = 0; i < timeInMSec.length; i++) {
163             if (timeInMSec[i] == 0) {
164                 result[i] = change / 0.001;
165             } else {
166                 result[i] = change / timeInMSec[i];
167             }
168         }
169         return result;
170     }
171 
172     /**
173      * Get the value of the 95th percentile using nearest rank algorithm.
174      */
get95PercentileValue(double[] values)175     public static double get95PercentileValue(double[] values) {
176         Arrays.sort(values);
177         // zero-based array index
178         int index = (int) Math.round(values.length * 0.95 + .5) - 1;
179         return values[index];
180     }
181 
182 }
183