← back to Watches

services/ml/linear-regression.js

59 lines

/**
 * Linear Regression Model for Price Prediction
 */

export class LinearRegression {
  constructor() {
    this.slope = 0;
    this.intercept = 0;
    this.rSquared = 0;
    this.rmse = 0;
  }

  fit(X, y) {
    const n = X.length;
    const sumX = X.reduce((a, b) => a + b, 0);
    const sumY = y.reduce((a, b) => a + b, 0);
    const sumXY = X.reduce((acc, x, i) => acc + x * y[i], 0);
    const sumXX = X.reduce((acc, x) => acc + x * x, 0);

    this.slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
    this.intercept = (sumY - this.slope * sumX) / n;

    // Calculate R-squared
    const yMean = sumY / n;
    const ssTotal = y.reduce((acc, yi) => acc + Math.pow(yi - yMean, 2), 0);
    const ssRes = y.reduce((acc, yi, i) => acc + Math.pow(yi - this.predict(X[i]), 2), 0);
    this.rSquared = 1 - (ssRes / ssTotal);
    this.rmse = Math.sqrt(ssRes / n);

    return { slope: this.slope, intercept: this.intercept, rSquared: this.rSquared, rmse: this.rmse };
  }

  predict(x) {
    return this.slope * x + this.intercept;
  }

  predictFuture(currentYear, yearsAhead) {
    return Array.from({ length: yearsAhead }, (_, i) => ({
      year: currentYear + i + 1,
      price: Math.round(this.predict(currentYear + i + 1))
    }));
  }

  toJSON() {
    return { type: 'linear', slope: this.slope, intercept: this.intercept, rSquared: this.rSquared, rmse: this.rmse };
  }

  static fromJSON(data) {
    const model = new LinearRegression();
    model.slope = data.slope;
    model.intercept = data.intercept;
    model.rSquared = data.rSquared;
    model.rmse = data.rmse;
    return model;
  }
}

export default LinearRegression;