# Usage and Examples¶

Some examples are also available in Jupyter Notebook format in the `Examples/` directory of the repository.

## Basic Usage¶

```from pymcr.mcr import McrAR
mcrar = McrAR()

# MCR assumes a system of the form: D = CS^T
#
# Data that you will provide (hyperspectral context):
# D [n_pixels, n_frequencies]  # Hyperspectral image unraveled in space (2D)
#
# initial_spectra [n_components, n_frequencies]  ## S^T in the literature
# OR
# initial_conc [n_pixels, n_components]   ## C in the literature

# If you have an initial estimate of the spectra
mcrar.fit(D, ST=initial_spectra)

# Otherwise, if you have an initial estimate of the concentrations
mcrar.fit(D, C=initial_conc)
```

## Default: Ordinary Least-Squares (OLS) with Non-Negativity Constraints¶

Using OLS and post-iterative non-negativity constraints is a classic way of performing MCR-ALS. OLS algorithms are often faster in practice, but NNLS method may converge MCR faster.

```from pymcr.mcr import McrAR
mcrar = McrAR()

# Equivalent to

mcrar = McrAR(c_regr='OLS', st_regr='OLS')

# Equivalent to

from pymcr.mcr import McrAR
from pymcr.regressors import OLS
from pymcr.constraints import ConstraintNonneg

mcrar = McrAR(c_regr=OLS(), st_regr=OLS(), c_constraints=[ConstraintNonneg()],
st_constraints=[ConstraintNonneg()])

# Then use the fit method with initial guesses of ST or C.
```

## Non-Negative Least-Squares (NNLS), No Constraints¶

Using OLS and post-iterative non-negativity constraints is a classic way of performing MCR-ALS. OLS algorithms are often faster in practice, but NNLS method may converge MCR faster.

```from pymcr.mcr import McrAR
from pymcr.regressors import NNLS

mcrar = McrAR(c_regr=NNLS(), st_regr=NNLS(), c_constraints=[], st_constraints=[])

# Then use the fit method with initial guesses of ST or C.
```

## Mix NNLS and OLS Regressors, Non-Negativity and Sum-to-1 (i.e., Norm) Constraints¶

In this example, NNLS is used to regress the signature/spectrum and OLS is used for abundance/concentration. Additionally, the ConstraintNorm imposes that after each iteration the summation of all concentrations for each data point sums-to-one. This is common and physical for many use-cases such as spectroscopy (as long as all analytes have a signature [i.e., no 0-intensity signatures])

```from pymcr.mcr import McrAR
from pymcr.regressors import NNLS, OLS
from pymcr.constraints import ConstraintNonneg, ConstraintNorm

# Note constraint order matters
mcrar = McrAR(max_iter=100, st_regr='NNLS', c_regr='OLS',
c_constraints=[ConstraintNonneg(), ConstraintNorm()])

# Equivalent to

# Note constraint order matters
mcrar = McrAR(max_iter=100, st_regr=NNLS(), c_regr=OLS(),
c_constraints=[ConstraintNonneg(), ConstraintNorm()])

# Then use the fit method with initial guesses of ST or C.
```

## Ridge-Regression from Scikit-Learn¶

In this example, NNLS is used to regress the signatures/spectra but a ridge regression regressor is imported from scikit-learn

```from pymcr.mcr import McrAR
from pymcr.regressors import NNLS
from pymcr.constraints import ConstraintNonneg, ConstraintNorm

from sklearn.linear_model.ridge import Ridge

# Note that an instance of Ridge can be instantiated (with hyperparameters)
# within the instantiation of McrAR.
mcrar = McrAR(max_iter=100, tol_increase=2, tol_err_change=1e-10,
c_regr=Ridge(alpha=1), st_regr=NNLS(),
c_constraints=[ConstraintNonneg(), ConstraintNorm()])

# Then use the fit method with initial guesses of ST or C.
```

## Single-Gaussian Distribution Spectral Fitting with LMFIT¶

In this example, the LMFIT Library is used to perform Gaussian distribution fitting on each spectral component. A longer example of this can be found in the `Examples/NIST_JRes_Paper` Jupyter Notebook.

```import numpy as np

from lmfit.models import GaussianModel

from pymcr.mcr import McrAR
from pymcr.constraints import Constraint, ConstraintNorm

class ConstraintSingleGauss(Constraint):
"""
Perform a nonlinear least-squares fitting to enforce a Gaussian.

Parameters
----------
copy : bool
Make copy of input data, A; otherwise, overwrite (if mutable)

axis : int
Axis to perform fitting over

"""
def __init__(self, copy=False, axis=-1):
""" A must be non-negative"""
self.copy = copy
self.axis = axis

def transform(self, A):
""" Fit """
n_components = list(A.shape)
x = np.arange(n_components[self.axis])
n_components.pop(self.axis)
assert len(n_components)==1, "Input must be 2D"
n_components = n_components[0]

A_fit = 0*A

for num in range(n_components):
if (self.axis == -1) | (self.axis == 1):
y = A[num, :]
else:
y = A[:, num]

mod = GaussianModel()
pars = mod.guess(y, x=x)
out = mod.fit(y, pars, x=x)

if (self.axis == -1) | (self.axis == 1):
A_fit[num,:] = 1*out.best_fit
else:
A_fit[:, num] = 1*out.best_fit

if self.copy:
return A_fit
else:
A *= 0
A += A_fit
return A

mcrar = McrAR(max_iter=1000, st_regr='NNLS', c_regr='NNLS',
c_constraints=[ConstraintNorm()],
st_constraints=[ConstraintSingleGauss()])
```

## Semi-Learned Fitting¶

In semi-learned fitting, one has a priori knowledge about some or all of the spectra and/or concentration components. To use this information, the known entities are put into the initial guesses and the “fix” parameter is used to tell the fit method the indices. Effectively the fit method will not alter these values during iterations.

```# Example 1: 2 known concentration maps
# D: Mixture Spectra
# C_guess: Concentration guesses, but the 0 and 1 indices are known ahead of time

mcrar.fit(D, C=C_guess, c_fix=[0,1])

# Example 1: 2 known concentration maps, 1 known spectrum
# D: Mixture Spectra
# C_guess: Concentration guesses, but the 0 and 1 indices are known ahead of time
# St_guess: Spectra guesses, but the 0-index is known ahead of time
# c_first: In first iteration, solve for C first (fixed St)?

mcrar.fit(D_known, C=C_guess, ST=St_guess, c_fix=[0,1], st_fix=[0], c_first=True)
```