/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.math.misc;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Fitting {
/**
* Performs a linear least-squares fit.
*
* @param points
* a list of 2-dimensional points (x, y)
* @return [m, b] such that the least-fit line is y = m*x + b
*/
public static float[] linearLeastSquares2f(List<float[]> points) {
float A0 = 0, A1 = 0, A2 = 0, A3 = 0;
float B0 = 0, B1 = 0;
for (float[] point : points) {
if (Float.isNaN(point[0]) || Float.isNaN(point[1]) ||
Float.isInfinite(point[0]) || Float.isNaN(point[1])) {
continue;
}
B0 += 2f * point[1];
B1 += 2f * point[1] * point[0];
A0 += 2f;
A1 += 2f * point[0];
A2 += 2f * point[0];
A3 += 2f * point[0] * point[0];
}
float det = A0 * A3 - A1 * A2;
if (det == 0f) {
return new float[] { Float.NaN, Float.NaN };
}
float rdet = 1f / det;
float b = rdet * (A3 * B0 - A2 * B1);
float m = rdet * (-A1 * B0 + A0 * B1);
return new float[] { m, b };
}
/**
* @param points
* a list of 2-element points
* @return a 2-element [slope, intercept] array
*/
public static float[] theilSen(List<float[]> points) {
List<Float> slopes = new ArrayList<>();
for (int i = 0; i < points.size(); i++) {
float[] p1 = points.get(i);
for (int j = i + 1; j < points.size(); j++) {
float[] p2 = points.get(j);
slopes.add((p2[1] - p1[1]) / (p2[0] - p1[0]));
}
}
Collections.sort(slopes);
float[] result = new float[2];
result[0] = slopes.get(slopes.size() / 2);
if ((slopes.size() & 0x1) == 0) {
result[0] = 0.5f * (result[0] + slopes.get(slopes.size() / 2 - 1));
}
float[] intercepts = new float[points.size()];
for (int i = 0; i < points.size(); i++) {
float[] point = points.get(i);
intercepts[i] = point[1] - result[0] * point[0];
}
Arrays.sort(intercepts);
result[1] = intercepts[points.size() / 2];
if ((points.size() & 0x1) == 0) {
result[1] = 0.5f * (result[1] + intercepts[points.size() / 2 - 1]);
}
return result;
}
}