Metadata-Version: 2.4
Name: gri-fitter
Version: 0.2.1
Project-URL: repository, https://gitlab.com/geosol-foss/python/gri-fitter
Project-URL: homepage, https://geosolresearch.com
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: numpy
Requires-Dist: scipy
Description-Content-Type: text/markdown

[![GeoSol Research Logo](https://geosolresearch.com/logos/foss_logo.png "GeoSol Research")](https://geosolresearch.com)

# Fitter (Equation Fitting)

Structured wrapper around `scipy.optimize.curve_fit` that pairs equations with their fitted results. Provides automatic coefficient extraction from function signatures, printable equation representations with value substitution, and RMSE reporting.

## Overview

gri-fitter separates equation definition from curve fitting into two composable classes. The `Equation` class wraps any Python function and introspects its signature to identify the independent variable (first parameter) and coefficients (remaining parameters). The `Fit` class takes data and an `Equation`, runs `scipy.optimize.curve_fit`, and stores the results -- coefficients, fitted y-values, and RMSE -- for downstream use.

Requires Python 3.12+.

## Installation

```bash
pip install gri-fitter
```

For development:

```bash
git clone https://gitlab.com/geosol-foss/python/gri-fitter.git
cd gri-fitter
. .init_venv.sh
```

## Quick Start

```python
from gri_fitter import Equation, Fit

# Define the equation: first param is independent variable, rest are coefficients
equ = Equation(lambda x, m, b: m * x + b, "mx + b")

# Fit to data
fit = Fit((0, 1, 2, 3), (0, 1.1, 1.9, 3), equ, "LineFit")
coeffs, rmse = fit.fit()

# Print results
print(fit)          # LineFit RMSE [0.00900] ::: 0.99x + 0.05
print(fit.y_fit)    # Fitted y values at the input x points
```

## Equation Class

`Equation` wraps a function (lambda or `def`) and extracts variable names from its signature. The first parameter is treated as the independent variable; all others are coefficients.

```python
# Using a lambda
equ = Equation(lambda x, a, b, c: a * x**2 + b * x + c, "ax^2 + bx + c")

print(equ.independent)   # "x"
print(equ.coefficients)  # ["a", "b", "c"]

# Using a defined function
def exponential(x, a, k):
    return a * np.exp(k * x)

equ = Equation(exponential, "a * exp(kx)")

# Without a string representation (replace() will not be available)
equ = Equation(lambda x, m: m * x)
```

The `replace()` method substitutes coefficient values into the equation string for display. The `p` parameter controls decimal precision (truncation, not rounding):

```python
equ = Equation(lambda x, m, b: m * x + b, "mx + b")
print(equ.replace((4.9876, 1.0)))         # 4.987x + 1.0
print(equ.replace((4.9876, 1.0), p=1))    # 4.9x + 1.0
```

## Fit Class

`Fit` takes x data, y data, an `Equation`, and an optional title. Call `fit()` to run `scipy.optimize.curve_fit`.

```python
equ = Equation(lambda x, m, b: m * x + b, "mx + b")
fit = Fit(x_data, y_data, equ, "MyFit")

# Returns (coefficients, rmse) on success, ([], None) on failure
coeffs, rmse = fit.fit()

# After fitting, results are stored as attributes
print(fit.coefficients)  # [0.99, 0.05]
print(fit.rmse)          # 0.009
print(fit.y_fit)         # Fitted y values at each input x
```

When the fit fails (e.g., data does not match the equation form), `fit()` returns `([], None)` and `str(fit)` displays the failure:

```python
print(fit)  # MyFit ***NO FIT*** ::: mx + b
```

## Dependencies

- **numpy**: Array operations
- **scipy**: `scipy.optimize.curve_fit` for nonlinear least squares fitting


## Other Projects

Current list of other [GRI FOSS Projects](https://gitlab.com/geosol-foss/python/gri-fitter/-/blob/main/.docs_other_projects.md) we are building and maintaining.

## License

MIT License. See [LICENSE](https://gitlab.com/geosol-foss/python/gri-fitter/-/blob/main/LICENSE) for details.
