Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions linear_regression.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Python Object Oriented Programming by Joe Marini course example
# Simple linear regression using OOP concepts

from dataclasses import dataclass


@dataclass
class DataPoint:
"""Represents a single (x, y) data observation."""
x: float
y: float

def __str__(self):
return f"({self.x}, {self.y})"


class SimpleLinearRegression:
"""Performs simple linear regression (y = mx + b) using least squares."""

def __init__(self, data_points):
if len(data_points) < 2:
raise ValueError("At least 2 data points are required")

self._x_data = [p.x for p in data_points]
self._y_data = [p.y for p in data_points]
self._slope = None
self._intercept = None
self._fit()

def _fit(self):
"""Compute slope and intercept using the least-squares method."""
n = len(self._x_data)
sum_x = sum(self._x_data)
sum_y = sum(self._y_data)
sum_xy = sum(x * y for x, y in zip(self._x_data, self._y_data))
sum_x2 = sum(x ** 2 for x in self._x_data)

denominator = n * sum_x2 - sum_x ** 2
if denominator == 0:
raise ValueError("All x values are identical; cannot compute regression")

self._slope = (n * sum_xy - sum_x * sum_y) / denominator
self._intercept = (sum_y - self._slope * sum_x) / n

def predict(self, x):
"""Predict y for a given x value."""
return self._slope * x + self._intercept

def get_r_squared(self):
"""Compute the R-squared (coefficient of determination)."""
y_mean = sum(self._y_data) / len(self._y_data)
ss_tot = sum((y - y_mean) ** 2 for y in self._y_data)
if ss_tot == 0:
return 1.0
ss_res = sum(
(y - self.predict(x)) ** 2
for x, y in zip(self._x_data, self._y_data)
)
return 1 - (ss_res / ss_tot)

def __str__(self):
return (f"LinearRegression: y = {self._slope:.4f}x + "
f"{self._intercept:.4f} (R²={self.get_r_squared():.4f})")

def __repr__(self):
return (f"SimpleLinearRegression(n={len(self._x_data)}, "
f"slope={self._slope:.4f}, intercept={self._intercept:.4f})")

def __call__(self, x):
"""Make the model callable for predictions."""
return self.predict(x)


# ~~~~~~~~~ TEST CODE ~~~~~~~~~
# Sample data: years of experience vs. salary (in thousands)
data = [
DataPoint(1.0, 45.0),
DataPoint(2.0, 50.0),
DataPoint(3.0, 55.0),
DataPoint(4.0, 60.0),
DataPoint(5.0, 68.0),
DataPoint(6.0, 72.0),
DataPoint(7.0, 80.0),
DataPoint(8.0, 85.0),
DataPoint(9.0, 90.0),
DataPoint(10.0, 95.0),
]

# Create the regression model
model = SimpleLinearRegression(data)

# Display the model equation
print(model)

# Make predictions using the predict method
print(f"Predicted salary for 5 years exp: {model.predict(5.0):.2f}k")
print(f"Predicted salary for 15 years exp: {model.predict(15.0):.2f}k")

# Use the model as a callable (demonstrates __call__)
print(f"Predicted salary for 12 years exp: {model(12.0):.2f}k")

# Display model details using repr
print(repr(model))

# Show R-squared value
print(f"R-squared: {model.get_r_squared():.4f}")