# 5. Preprocessor

Estimators in metric-learn all have a `preprocessor`

option at instantiation.
Filling this argument allows them to take more compact input representation
when fitting, predicting etc…

If `preprocessor=None`

, no preprocessor will be used and the user must
provide the classical representation to the fit/predict/score/etc… methods of
the estimators (see the documentation of the particular estimator to know the
type of input it accepts). Otherwise, two types of objects can be put in this
argument:

## 5.1. Array-like

You can specify `preprocessor=X`

where `X`

is an array-like containing the
dataset of points. In this case, the fit/predict/score/etc… methods of the
estimator will be able to take as inputs an array-like of indices, replacing
under the hood each index by the corresponding sample.

Example with a supervised metric learner:

```
>>> from metric_learn import NCA
>>>
>>> X = np.array([[-0.7 , -0.23],
>>> [-0.43, -0.49],
>>> [ 0.14, -0.37]]) # array of 3 samples of 2 features
>>> points_indices = np.array([2, 0, 1, 0])
>>> y = np.array([1, 0, 1, 1])
>>>
>>> nca = NCA(preprocessor=X)
>>> nca.fit(points_indices, y)
>>> # under the hood the algorithm will create
>>> # points = np.array([[ 0.14, -0.37],
>>> # [-0.7 , -0.23],
>>> # [-0.43, -0.49],
>>> # [ 0.14, -0.37]]) and fit on it
```

Example with a weakly supervised metric learner:

```
>>> from metric_learn import MMC
>>> X = np.array([[-0.7 , -0.23],
>>> [-0.43, -0.49],
>>> [ 0.14, -0.37]]) # array of 3 samples of 2 features
>>> pairs_indices = np.array([[2, 0], [1, 0]])
>>> y_pairs = np.array([1, -1])
>>>
>>> mmc = MMC(preprocessor=X)
>>> mmc.fit(pairs_indices, y_pairs)
>>> # under the hood the algorithm will create
>>> # pairs = np.array([[[ 0.14, -0.37], [-0.7 , -0.23]],
>>> # [[-0.43, -0.49], [-0.7 , -0.23]]]) and fit on it
```

## 5.2. Callable

Alternatively, you can provide a callable as `preprocessor`

. Then the
estimator will accept indicators of points instead of points. Under the hood,
the estimator will call this callable on the indicators you provide as input
when fitting, predicting etc… Using a callable can be really useful to
represent lazily a dataset of images stored on the file system for instance.
The callable should take as an input a 1D array-like, and return a 2D
array-like. For supervised learners it will be applied on the whole 1D array of
indicators at once, and for weakly supervised learners it will be applied on
each column of the 2D array of tuples.

Example with a supervised metric learner:

```
>>> def find_images(file_paths):
>>> # each file contains a small image to use as an input datapoint
>>> return np.row_stack([imread(f).ravel() for f in file_paths])
>>>
>>> nca = NCA(preprocessor=find_images)
>>> nca.fit(['img01.png', 'img00.png', 'img02.png'], [1, 0, 1])
>>> # under the hood preprocessor(indicators) will be called
```

Example with a weakly supervised metric learner:

```
>>> pairs_images_paths = [['img02.png', 'img00.png'],
>>> ['img01.png', 'img00.png']]
>>> y_pairs = np.array([1, -1])
>>>
>>> mmc = NCA(preprocessor=find_images)
>>> mmc.fit(pairs_images_paths, y_pairs)
>>> # under the hood preprocessor(pairs_indicators[i]) will be called for each
>>> # i in [0, 1]
```

Note

Note that when you fill the `preprocessor`

option, it allows you
to give more compact inputs, but the classical way of providing inputs
stays valid (2D array-like for supervised learners and 3D array-like of
tuples for weakly supervised learners). If a classical input
is provided, the metric learner will not use the preprocessor.

Example: This will work:

```
>>> from metric_learn import MMC
>>> def preprocessor_wip(array):
>>> raise NotImplementedError("This preprocessor does nothing yet.")
>>>
>>> pairs = np.array([[[ 0.14, -0.37], [-0.7 , -0.23]],
>>> [[-0.43, -0.49], [-0.7 , -0.23]]])
>>> y_pairs = np.array([1, -1])
>>>
>>> mmc = MMC(preprocessor=preprocessor_wip)
>>> mmc.fit(pairs, y_pairs) # preprocessor_wip will not be called here
```