diff --git a/.gitignore b/.gitignore index d9005f2..b96d430 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ __pycache__/ *.so # Distribution / packaging +test.py .Python build/ develop-eggs/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f54cb58 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +## [0.0.8] - 2023-09-26 +### Added +- **Vector Mean Calculation**: Added a new method to calculate the mean (average) of the vector's values. You can now use the `mean` method to obtain the mean value of a Vector. + +- **Vector Variance Calculation**: Implemented a method to compute the variance of the vector's values. You can now use the `variance` method to calculate the variance of a Vector. + +- **Vector Standard Deviation Calculation**: Introduced a method for calculating the standard deviation of the vector's values. You can now use the `std_deviation` method to find the standard deviation of a Vector. + +- **Linear Regression**: Added support for linear regression with the `LinearRegression` class. You can use the `fit` method to train a linear regression model and the `predict` method to make predictions based on the trained model. + +- **Time Series Analysis**: Introduced a new class, `TimeSeriesAnalysis`, for analyzing time series data. + - `moving_average(self, window_size: int) -> Vector`: Added a method for calculating the moving average of a time series with a specified window size. + - `exponential_smoothing(self, alpha: float) -> Vector`: Added a method for applying exponential smoothing to a time series with a given smoothing parameter. + +These new features provide additional statistical analysis capabilities for Vector objects, as well as the ability to perform linear regression on your data using the `LinearRegression` class. +These new features enable users to perform time series analysis, including moving average and exponential smoothing, using the `TimeSeriesAnalysis` class. This enhances the capabilities of the library for working with time series data. diff --git a/UNIMATHLIB/MathFunc/matrix.py b/UNIMATHLIB/MathFunc/matrix.py index e1c0515..0bdf83e 100644 --- a/UNIMATHLIB/MathFunc/matrix.py +++ b/UNIMATHLIB/MathFunc/matrix.py @@ -87,7 +87,8 @@ class Matrix(): QR_eigen(max_iterations: int = 100): Compute eigenvalues and eigenvectors using QR algorithm - + Properties: + - shape: Return the shape (dimension) of the Matrix. Attributes: rows (int): The number of rows in the Matrix. diff --git a/UNIMATHLIB/MathFunc/vector.py b/UNIMATHLIB/MathFunc/vector.py index 8176aaa..7779f5d 100644 --- a/UNIMATHLIB/MathFunc/vector.py +++ b/UNIMATHLIB/MathFunc/vector.py @@ -35,7 +35,10 @@ class Vector: - cross(self, y): Calculate the cross product of two 3D Vectors. - size(self): Return the size (dimension) of the Vector. - _AllNum(vector): Check if all elements in the Vector are of type int or float. - + - mean(self): Calculate the mean (average) of the vector's values. + - variance(self): Calculate the variance of the vector's values. + - std_deviation(self): Calculate the standard deviation of the vector's values. + Properties: - size: Return the size (dimension) of the Vector. @@ -408,7 +411,7 @@ def clear(self): - def cross(self,y): + def cross(self,other): """Calculate the cross product of two 3-dimensional vectors. Args: @@ -423,12 +426,12 @@ def cross(self,y): if isinstance(other, (list, tuple, set, dict, str)): other = Vector(other) if isinstance(other, Vector): - if len(self) != 3 or len(y) != 3: + if len(self) != 3 or len(other) != 3: raise ValueError("Vector must be 3 dimensional") result = Vector([ - self[1] * y[2] - self[2] * y[1], - self[2] * y[0] - self[0] * y[2], - self[0] * y[1] - self[1] * y[0] + self[1] * other[2] - self[2] * other[1], + self[2] * other[0] - self[0] * other[2], + self[0] * other[1] - self[1] * other[0] ]) return result @@ -459,6 +462,49 @@ def dist(self,y,p = 1): else: raise ValueError(f"Error in vector format, please check {(self,y)}") + def mean(self): + """ + Calculate the mean (average) of the vector's values. + + Raises: + ValueError: If the vector is empty. + + Returns: + float: The mean value of the vector's elements. + """ + if len(self) == 0: + raise ValueError("Empty Vector") + + total = sum(self.vector) + return total / len(self.vector) + + def variance(self): + """ + Calculate the variance of the vector's values. + + Raises: + ValueError: If there are fewer than 2 values in the vector. + + Returns: + float: The variance of the vector's elements. + """ + if len(self) < 2: + raise ValueError("Need min 2 values for variance") + + mean_value = self.mean() + squared_diff = [(x - mean_value) ** 2 for x in self.vector] + return sum(squared_diff) / (len(self.vector) - 1) + + def std_deviation(self): + """ + Calculate the standard deviation of the vector's values. + + Returns: + float: The standard deviation of the vector's elements. + """ + return (self.variance())**0.5 + + def _euclidean(self, y): """Calculate the Euclidean norm of two vectors. @@ -470,6 +516,7 @@ def _euclidean(self, y): """ return sum(abs(x-y)**2 for x,y in zip(self.vector,y.vector))**(1/2) + def _manhattan(self, y): """Calculate the Manhattan norm of two vectors. diff --git a/UNIMATHLIB/__init__.py b/UNIMATHLIB/__init__.py index 32035a5..bc78afe 100644 --- a/UNIMATHLIB/__init__.py +++ b/UNIMATHLIB/__init__.py @@ -1,2 +1,4 @@ from .MathFunc import * - +from .Linear import * +from .timeSeriesAnalys import * +from .fin import * \ No newline at end of file diff --git a/setup.py b/setup.py index f587fe2..a3cf782 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, find_packages setup(name='UniMathLib', - version='0.0.7', + version='0.0.8', license = "GNU GPLv3", description="""UniMathLib is a Python library that provides essential tools for working with mathematical vectors and matrices. It simplifies tasks like vector @@ -44,7 +44,7 @@ Identity and Diagonal Matrices: Create identity matrices of any size and diagonal matrices with specified diagonal values. - + and others. UniMathLib simplifies complex mathematical tasks, making it a valuable tool for professionals and students alike. Whether you need to perform basic vector operations or solve intricate linear systems, UniMathLib provides an efficient and user-friendly solution. Start using UniMathLib today to enhance your mathematical diff --git a/unimathlib/Linear/__init__.py b/unimathlib/Linear/__init__.py new file mode 100644 index 0000000..e84baed --- /dev/null +++ b/unimathlib/Linear/__init__.py @@ -0,0 +1 @@ +from .linreg import * \ No newline at end of file diff --git a/unimathlib/Linear/linreg.py b/unimathlib/Linear/linreg.py new file mode 100644 index 0000000..e1dab3c --- /dev/null +++ b/unimathlib/Linear/linreg.py @@ -0,0 +1,95 @@ +from ..MathFunc import Matrix, Vector +import sys +import io +class LinearRegression: + """ + Linear Regression Model. + + This class implements a simple linear regression model using the method of least squares. + + Attributes: + coefficients (Vector): The coefficients of the linear regression model, including the intercept. + + Methods: + - __init__(self): Initialize an instance of the LinearRegression class. + - fit(self, X: Matrix, y: Matrix): Fit the linear regression model to the training data. + - predict(self, X: Matrix): Predict target values for new data. + + Example: + # Create a LinearRegression instance + model = LinearRegression() + + # Fit the model to training data + X_train = Matrix([[1], [2], [3], [4]]) + y_train = Matrix([2, 4, 6, 8]) + model.fit(X_train, y_train) + + # Make predictions + X_test = Matrix([[5], [6]]) + predictions = model.predict(X_test) + """ + def __init__(self): + """ + Initialize an instance of the LinearRegression class. + + The coefficients attribute is set to None initially. + """ + self.coefficients = None + + def fit(self, X:Matrix, y:Matrix): + """ + Fit the linear regression model to the training data. + + Args: + X (Matrix): The feature matrix. Each row represents an observation. + y (Matrix): The target values. + + Raises: + ValueError: If the lengths of X and y do not match. + + Returns: + None + """ + if not isinstance(X, Matrix) or not isinstance(y, Matrix): + X = Matrix(X) + y = Matrix(y) + original_stdout = sys.stdout + sys.stdout = io.StringIO() + if len(X) != len(y): + raise ValueError("len(X) must be equal len(y)") + + X_reg = Matrix(X.shape[0],X.shape[1]+1) + for i in range(len(X)): + n_r = Vector(X.shape[1]+1) + n_r[0], n_r[1:] = 1, X[i] + X_reg[i] = n_r + X_transpose = X_reg.transpose() + XTX = X_transpose*X_reg + XTY = X_transpose*y + self.coefficients = XTX.LU_solver(XTY) + sys.stdout = original_stdout + + def predict(self, X): + """ + Predict target values for new data. + + Args: + X (Matrix): The feature matrix for prediction. Each row represents an observation. + + Raises: + ValueError: If the model has not been trained (fit) yet. + + Returns: + Vector: Predicted target values. + """ + if self.coefficients is None: + raise ValueError("Use fit for model teach.") + if not isinstance(X, Matrix): + X = Matrix(X) + X_reg = Matrix(X.shape[0],X.shape[1]+1) + for i in range(len(X)): + n_r = Vector(X.shape[1]+1) + n_r[0], n_r[1:] = 1, X[i] + X_reg[i] = n_r + return Vector([Vector(x) * Vector([el[0] for el in self.coefficients.matrix]) for x in X_reg]) + diff --git a/unimathlib/fin/__init__.py b/unimathlib/fin/__init__.py new file mode 100644 index 0000000..1a56ff5 --- /dev/null +++ b/unimathlib/fin/__init__.py @@ -0,0 +1 @@ +from .finance import * \ No newline at end of file diff --git a/unimathlib/fin/finance.py b/unimathlib/fin/finance.py new file mode 100644 index 0000000..466bdbb --- /dev/null +++ b/unimathlib/fin/finance.py @@ -0,0 +1,83 @@ +from ..MathFunc import Vector + +class FinancialAnalysis: + """ + Financial Analysis Class. + + This class provides methods for financial analysis, including NPV and IRR calculations. + + Methods: + - __init__(self): Initialize an instance of the FinancialAnalysis class. + - npv(self, cash_flows: Vector), discount_rate: float) -> float: Calculate the Net Present Value (NPV) of cash flows. + - irr(self, cash_flows: Vector) -> float: Calculate the Internal Rate of Return (IRR) of cash flows. + + Example: + # Create a FinancialAnalysis instance + analyzer = FinancialAnalysis() + + # Calculate NPV for cash flows with a discount rate of 0.1 + cash_flows = [-100, 50, 60, 70] + discount_rate = 0.1 + npv_result = analyzer.npv(cash_flows, discount_rate) + + # Calculate IRR for cash flows + irr_result = analyzer.irr(cash_flows) + """ + + def __init__(self): + """ + Initialize an instance of the FinancialAnalysis class. + """ + pass + + def npv(self, cash_flows, discount_rate: float) -> float: + """ + Calculate the Net Present Value (NPV) of cash flows. + + Args: + cash_flows (List[float]): List of cash flows over time. + discount_rate (float): The discount rate. + + Returns: + float: The NPV value. + """ + if not isinstance(cash_flows, Vector): + cash_flows = Vector(cash_flows) + npv_value = 0 + for i, cash_flow in enumerate(cash_flows): + npv_value += cash_flow / (1 + discount_rate) ** i + return npv_value + + def irr(self, cash_flows, max_iterations:int=1000, irr_guess:float = 0.1, tolerance:float=1e-6) -> float: + """ + Calculate the Internal Rate of Return (IRR) of cash flows. + + Args: + cash_flows (Vector): List of cash flows over time. + max_iterations (int, optional): Maximum number of iterations for IRR calculation. Defaults to 1000. + irr_guess (float, optional): Initial guess for IRR. Defaults to 0.1. + tolerance (float, optional): Tolerance level for convergence. Defaults to 1e-6. + + Raises: + ValueError: Raised if the IRR calculation does not converge within the specified parameters. + + Returns: + float: The calculated IRR value. + """ + if not isinstance(cash_flows, Vector): + cash_flows = Vector(cash_flows) + for _ in range(max_iterations): + npv = self.npv(cash_flows, irr_guess) + npv_derivative = 0 + for t, cash_flow in enumerate(cash_flows): + npv_derivative -= t * cash_flow / ((1 + irr_guess) ** (t + 1)) + + # Newton-Raphson step + irr_guess -= npv / npv_derivative + + # Check for convergence + if abs(npv) < tolerance: + return irr_guess + + # If no convergence is reached, return None or raise an exception + raise ValueError("IRR calculation did not converge") \ No newline at end of file diff --git a/unimathlib/timeSeriesAnalys/__init__.py b/unimathlib/timeSeriesAnalys/__init__.py new file mode 100644 index 0000000..bdfac59 --- /dev/null +++ b/unimathlib/timeSeriesAnalys/__init__.py @@ -0,0 +1 @@ +from .tsy import * \ No newline at end of file diff --git a/unimathlib/timeSeriesAnalys/tsy.py b/unimathlib/timeSeriesAnalys/tsy.py new file mode 100644 index 0000000..7799f78 --- /dev/null +++ b/unimathlib/timeSeriesAnalys/tsy.py @@ -0,0 +1,71 @@ +from ..MathFunc import Vector +class TimeSeriesAnalysis: + """ + Time Series Analysis Class. + + This class provides methods for analyzing time series data, including moving average and exponential smoothing. + + Attributes: + data Vector: The time series data. + + Methods: + - __init__(self, data: Vector): Initialize an instance of the TimeSeriesAnalysis class with time series data. + - moving_average(self, window_size: int) -> Vector: Calculate the moving average of the time series. + - exponential_smoothing(self, alpha: float) -> Vector: Apply exponential smoothing to the time series. + + Example: + # Create a TimeSeriesAnalysis instance with time series data + data = [10, 15, 12, 18, 20, 22, 25] + analyzer = TimeSeriesAnalysis(data) + + # Calculate a 3-day moving average + moving_avg = analyzer.moving_average(window_size=3) + + # Apply exponential smoothing with alpha = 0.2 + smoothed_data = analyzer.exponential_smoothing(alpha=0.2) + + """ + + def __init__(self, data): + """ + Initialize an instance of the TimeSeriesAnalysis class with time series data. + + Args: + data (Vector): The time series data. + """ + if not isinstance(data, Vector): + data = Vector(data) + self.data = data + + def moving_average(self, window_size: int) -> Vector: + """ + Calculate the moving average of the time series. + + Args: + window_size (int): The size of the moving average window. + + Returns: + Vector: A list of moving average values. + """ + moving_averages = Vector() + for i in range(len(self.data) - window_size + 1): + window = self.data[i:i + window_size] + average = sum(window) / window_size + moving_averages.append(average) + return moving_averages + + def exponential_smoothing(self, alpha: float) -> Vector: + """ + Apply exponential smoothing to the time series. + + Args: + alpha (float): Smoothing parameter (0 < alpha < 1). + + Returns: + Vector: A list of smoothed values. + """ + smoothed_data = Vector([self.data[0]]) # Initial value + for i in range(1, len(self.data)): + smoothed_value = alpha * self.data[i] + (1 - alpha) * smoothed_data[-1] + smoothed_data.append(smoothed_value) + return smoothed_data