001 /**
002 * Copyright (c) 2010, SIB. All rights reserved.
003 *
004 * SIB (Swiss Institute of Bioinformatics) - http://www.isb-sib.ch Host -
005 * https://sourceforge.net/projects/javaprotlib/
006 *
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions are met:
009 * Redistributions of source code must retain the above copyright notice, this
010 * list of conditions and the following disclaimer. Redistributions in binary
011 * form must reproduce the above copyright notice, this list of conditions and
012 * the following disclaimer in the documentation and/or other materials provided
013 * with the distribution. Neither the name of the SIB/GENEBIO nor the names of
014 * its contributors may be used to endorse or promote products derived from this
015 * software without specific prior written permission.
016 *
017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020 * ARE DISCLAIMED. IN NO EVENT SHALL SIB/GENEBIO BE LIABLE FOR ANY DIRECT,
021 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
024 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 */
028 package org.expasy.jpl.commons.base.math;
029
030
031 import java.util.ArrayList;
032
033
034 /**
035 * Class to fit straight line to a set of 2D data points. The methods
036 * implemented in Press, Teukolsky, Vetterling, Flannery, Numerical Recipies,
037 * second edition are used.
038 *
039 * @author mueller
040 *
041 * @version 1.0
042 *
043 */
044 public final class StraightLineFitter {
045
046 /** x values of data points */
047 private ArrayList<Double> xValues;
048 /** y values of data points */
049 private ArrayList<Double> yValues;
050 /** Variance associated with y-values of data points */
051 private ArrayList<Double> yVariance;
052 /** Offset of fitted straight line */
053 private double offset;
054 /** Slope of fitted straight line */
055 private double slope;
056 /** Boolean specifying whether fit is up to date */
057 boolean fitIsUpToDate;
058
059 /**
060 * Constructor : y-variance is set to 1 for all data points
061 *
062 * @param xValues : x-values of the data points
063 * @param yValues : y-values of the data points
064 */
065 public StraightLineFitter(final ArrayList<Double> xValues,
066 final ArrayList<Double> yValues) throws IllegalArgumentException {
067 setXYValues(xValues, yValues);
068 }
069
070 /**
071 * Constructor
072 *
073 * @param xValues : x-values of the data points
074 * @param yValues : y-values of the data points
075 * @param yVariance : variance associated with y-values of data points
076 */
077 public StraightLineFitter(final ArrayList<Double> xValues,
078 final ArrayList<Double> yValues, final ArrayList<Double> yVariance)
079 throws IllegalArgumentException {
080 setXYValues(xValues, yValues, yVariance);
081 }
082
083 /**
084 * Sets x-,y-values. y-variance is set to 1 for all data points
085 *
086 * @param xValues : x-values of the data points
087 * @param yValues : y-values of the data points
088 */
089 public void setXYValues(final ArrayList<Double> xValues,
090 final ArrayList<Double> yValues) throws IllegalArgumentException {
091 this.xValues = xValues;
092 this.yValues = yValues;
093
094 yVariance = new ArrayList<Double>();
095 for (int i = 0; i < yValues.size(); i++) {
096 yVariance.add(1.0);
097 }
098
099 fitIsUpToDate = false;
100
101 if (xValues.size() != yValues.size()) {
102 throw new IllegalArgumentException(
103 "Incomaptible lengths of xValues and yValues");
104 }
105 }
106
107 /**
108 * Sets x-,y- and variance values
109 *
110 * @param xValues : x-values of the data points
111 * @param yValues : y-values of the data points
112 * @param yVariance : variance associated with y-values of data points
113 */
114 public void setXYValues(final ArrayList<Double> xValues,
115 final ArrayList<Double> yValues, final ArrayList<Double> yVariance)
116 throws IllegalArgumentException {
117 this.xValues = xValues;
118 this.yValues = yValues;
119 this.yVariance = yVariance;
120
121 fitIsUpToDate = false;
122
123 if ((xValues.size() != yValues.size())
124 || (xValues.size() != yVariance.size())
125 || (yValues.size() != yVariance.size())) {
126 throw new IllegalArgumentException(
127 "Incomaptible lengths of xValues, yValues or yVariance");
128 }
129 }
130
131 /**
132 * Get the x-values of the data points
133 *
134 * @return x-values of the data points
135 */
136 public ArrayList<Double> getXValues() {
137 return xValues;
138 }
139
140 /**
141 * Get the y-values of the data points
142 *
143 * @return y-values of the data points
144 */
145 public ArrayList<Double> getYValues() {
146 return yValues;
147 }
148
149 /**
150 * Get y-variance of data points
151 *
152 * @return y-variance of data points
153 */
154 public ArrayList<Double> getYVariance() {
155 return yVariance;
156 }
157
158 /**
159 * Gets offset of fitted straight line
160 *
161 * @return offset
162 */
163 public double getFitOffset() {
164 if (!fitIsUpToDate) {
165 calcFit();
166 }
167 return offset;
168 }
169
170 /**
171 * Gets slope of fitted straight line
172 *
173 * @return slope
174 */
175 public double getFitSlope() {
176 if (!fitIsUpToDate) {
177 calcFit();
178 }
179 return slope;
180 }
181
182 /**
183 * Returns the values of the fitted straight line at xValues
184 *
185 * @return values of the fitted straight line at xValues
186 */
187 public ArrayList<Double> getFittedValues() {
188 if (!fitIsUpToDate) {
189 calcFit();
190 }
191
192 final ArrayList<Double> fittedValues = new ArrayList<Double>();
193 for (int i = 0; i < yValues.size(); i++) {
194 fittedValues.add(i, slope * xValues.get(i) + offset);
195 }
196
197 return fittedValues;
198 }
199
200 /**
201 * Calculates straight line fit
202 */
203 private void calcFit() {
204 int i, nr;
205 double s, sx, sy, stt, t, r, sig;
206
207 s = sx = sy = stt = 0.0;
208 nr = xValues.size();
209
210 slope = 0.0;
211 offset = 0.0;
212
213 if (nr == 0) {
214 return;
215 }
216
217 // only one point : do nothing
218 if (nr == 1) {
219 slope = 1.0;
220 offset = yValues.get(0) - xValues.get(0);
221 return;
222 }
223
224 // only two points : straight calculation of slope and offset
225 if (nr == 2) {
226 slope =
227 (yValues.get(0) - yValues.get(1))
228 / (xValues.get(0) - xValues.get(1));
229 offset = yValues.get(0) - slope * xValues.get(0);
230 return;
231 }
232
233 // more than two points ..
234 for (i = 0; i < nr; i++) {
235 sig = 1.0 / yVariance.get(i);
236 s += sig;
237 sx += xValues.get(i) * sig;
238 sy += yValues.get(i) * sig;
239 }
240
241 r = sx / s;
242 for (i = 0; i < nr; i++) {
243 sig = Math.sqrt(1.0 / yVariance.get(i));
244 t = (xValues.get(i) - r) * sig;
245 stt += t * t;
246 slope += t * yValues.get(i) * sig;
247 }
248
249 slope /= stt;
250 offset = (sy - sx * slope) / s;
251
252 fitIsUpToDate = true;
253 }
254
255 }