# Acf

[1]: https://dl.acm.org/doi/10.1109/ICDM.2008.22

*A lightweight recommender engine for implicit feedback datasets*

![](https://github.com/jancervenka/acf/workflows/CI/badge.svg)

The package implements an algorithm described in
[Collaborative Filtering for Implicit Feedback Datasets][1] paper. 
The algorithm is based on the following ideas:

* using collaborative filtering with latent factors
* transforming feedback observations into binary preferences
  with associated confidence levels
* using alternating least sqaures to compute the matrix factorization

## Install

The package requires Python `3.7` or newer, the only dependencies are
`numpy` and `pandas`. To install it, run

```bash
python setup.py install
```

## Usage

The following example shows how to train a model and compute predictions.

```python
import acf
import pandas as pd

# assuming the data are in the following format:
# | user_id | item_id | feedback |
# |---------|---------|----------|
# | 2491    | 129     | 2        |

interactions = pd.read_csv('interactions.csv')

engine = acf.Engine(reg_lambda=1, alpha=35, n_factors=2, random_state=0)

engine.fit(interactions,
           user_column='user_id',
           item_column='item_id',
           feedback_column='feedback',
           n_iter=20,
           n_jobs=4)

# get the best 20 recommendations
prediction = engine.predict(user=2491, top_n=20)

# to print training loss value at every iteration
print(engine.loss)
```

### Model Evaluation

For performance evaluation, the package offers `metrics.mean_rank`
function that implements "mean rank" metric as defined by equation
8 in the [paper][1].

The metric is a weighted mean of percentile-ranked recommendations
(`rank_ui = 0` says that item `i` is the first to be recommended for
user `u` and item `j` with `rank_uj = 1` is the last to be recommended)
where the weights are the actual feedback values from `R` user-item matrix.

```python
interactions_test = pd.read_csv('intercations_test.csv')

print(acf.metrics.mean_rank(interactions=interactions_test,
                            user_column='user_id',
                            item_column='item_id'
                            feedback_column='feedback',
                            engine=engine))
```

### Model Persistence

Trained model can be serialized and stored using `joblib` or `pickle`.

To store a model:

```python
with open('engine.joblib', 'wb') as f:
    joblib.dump(engine, f)
```

To load a model:

```python
with open('engine.joblib', 'rb') as f:
    engine = joblib.load(f)
```

## Public API

### `acf.Engine`

```python
acf.core.computation.Engine(reg_lambda=0.1, alpha=40,
                            n_factors=10, random_state=None):
```

Class exposing the recommender.

* `reg_lambda`: regularization strength
* `alpha`: gain parameter in feedback-confidence transformation 
           `c_ui = 1 + alpha * r_ui`
* `n_factors`: number of latent factors
* `random_state`: initial RNG state

__Properties:__

* `user_factors`: user factor matrix
* `item_factors`: item factor matrix
* `loss`: training loss history

__Methods:__

```python
Engine.fit(interactions, user_column, item_column,
           feedback_column, n_iter=20, n_jobs=1)
```

Trains the model.

* `interactions`: dataframe containing user-item feedbacks
* `user_column`: name of the column containing user ids
* `item_column`: name of the column containing item ids
* `feedback_column`: name of the column containing feedback values
* `n_iter`: number of alternating least squares iteration
* `n_jobs`: number of parallel jobs

```python
Engine.predict(user, top_n=None)
```

Predicts the recommendation.

* `user`: user identification for whom the prediction is computed
* `top_n`: if not `None`, only the besr n items are included in the result

__Returns:__ predicted recommendation score for each item as `pandas.Series`

### `acf.metrics.mean_rank`

```python
acf.core.metrics.mean_rank(interactions, user_column, item_column,
                           feedback_column, engine)
```

Computes mean rank evaluation.

* `interactions`: dataframe containing user-item feedbacks
* `user_column`: name of the column containing user ids
* `item_column`: name of the column containing item ids
* `feedback_column`: name of the column containing feedback values
* `engine`: trained `acf.Engine` instance

__Returns:__ computed value

## Tests

Tests can be executed by `pytest` as

```bash
python -m pytest acf/tests
```
