Metadata-Version: 2.3
Name: mpml_lib
Version: 1.0.3
Summary: Multi-Perspective Machine Learning Library
Requires-Dist: numpy
Requires-Dist: pandas
Requires-Dist: scikit-learn
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# MPML — Multi-Perspective Machine Learning

**A Python package for multi-perspective ensemble classification and feature-impact analysis.**

MPML is a Python library for constructing machine learning ensembles in which different models can examine **different subsets of features** from the same dataset. Built around scikit-learn-compatible classifiers, MPML provides a unified interface for defining *perspectives*, training ensemble models, generating predictions, and examining the contributions of individual features and perspectives.

Rather than requiring every classifier to learn from the same input variables, MPML allows each perspective to combine its own estimator, feature set, and voting weight. These perspectives can then be combined using one of several ensemble-learning strategies.

The package is intended for developers, researchers, and students interested in ensemble learning, heterogeneous feature representations, and model interpretability.

> **Scope:** The supplied implementation supports **classification**. Regression and unsupervised-learning APIs are not documented as supported.

## Key features

- **Perspective-based modelling:** Assign each classifier a named, validated subset of DataFrame columns.
- **Flexible estimators:** Use estimators compatible with the scikit-learn API.
- **Six ensemble strategies:** Majority voting, weighted combination, blending, stacking, bagging, and boosting.
- **Consistent training and prediction:** Use `fit(X_train, y_train)` and `predict(X_test)` across supported combination methods.
- **Feature-level impact analysis:** Compare prediction scores before and after changing or removing a selected feature.
- **Perspective-level impact analysis:** Estimate the influence of a complete perspective on an ensemble prediction.
- **Local and global analysis:** Evaluate a single observation or average impacts over an evaluation dataset.

## Installation

Install the published package using pip:

```bash
pip install mpml_lib
```

Or, if you manage your environment with [uv](https://docs.astral.sh/uv/):

```bash
uv add mpml_lib
```

**Note:** The PyPI distribution name is `mpml_lib`. The Python import name may differ from the distribution name; the examples below use `mpml` as the import path and should be checked against the published package.

### Dependencies

The provided source files directly import:

- Python
- [NumPy](https://numpy.org/)
- [pandas](https://pandas.pydata.org/)
- [scikit-learn](https://scikit-learn.org/)

The exact supported Python and dependency versions are determined by the published package's `pyproject.toml`; those version constraints were not included in the supplied files.

## Concepts

### What is a perspective?

A **perspective** is a machine learning estimator trained on a selected subset of the available dataset features. Each perspective contains:

| Component | Meaning |
| --- | --- |
| Model | A scikit-learn-compatible estimator used for classification |
| Feature list | The names of the dataset columns available to that estimator |
| Weight | A non-negative integer assigned to the perspective, used by weighted combination |

For example, a dataset containing demographic, behavioural, and clinical features could be represented by separate models that each focus on one group of variables. The same feature may also appear in multiple perspectives.

MPML assigns perspective identifiers automatically, beginning with `model1`, `model2`, and so on. The `Perspectives` class validates the model, selected columns, and weight before registering a perspective.

### How the components work together

```text
                    Input dataset (pandas DataFrame)
                                  |
                  Define perspectives and feature sets
                                  |
                  +---------------+---------------+
                  |               |               |
             Perspective 1   Perspective 2   Perspective 3
             Estimator A     Estimator B     Estimator C
             Features A      Features B      Features C
                  |               |               |
                  +---------------+---------------+
                                  |
                     Ensemble combination method
                                  |
                        Final class prediction
                                  |
                      Feature/perspective impact
```

## Quick start

The following example uses the built-in Iris classification dataset, two feature subsets, and a majority-voting ensemble.

```python
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score

from mpml import MPML, Perspectives

# 1. Load the dataset as a DataFrame.
iris = load_iris(as_frame=True)
X = iris.data
y = iris.target

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 2. Define different perspectives over the training features.
perspectives = Perspectives(X_train)

model1 = perspectives.add(
    DecisionTreeClassifier(random_state=42),
    ["sepal length (cm)", "sepal width (cm)"],
    weight=1,
)

model2 = perspectives.add(
    GaussianNB(),
    ["petal length (cm)", "petal width (cm)"],
    weight=2,
)

# 3. Initialize the ensemble.
# The current constructor requires meta_model even though
# majority voting does not train or use a meta-model.
ensemble = MPML(
    perspectives=perspectives,
    combination_method="majority_vote",
    meta_model=LogisticRegression(max_iter=1000),
)

# 4. Train the ensemble and generate predictions.
ensemble.fit(X_train, y_train)
y_pred = ensemble.predict(X_test)

print("Predictions:", y_pred)
print("Accuracy:", accuracy_score(y_test, y_pred))
```

> **Import-path note:** This example assumes your package's `__init__.py` exports `MPML` and `Perspectives` at the top level. The uploaded source files do not include `__init__.py`; adjust the imports if your installed package exposes them through submodules instead.

The training data and prediction data should be pandas DataFrames containing all feature columns referenced by the registered perspectives. `predict()` returns a NumPy array of final class predictions.

## Ensemble methods

Choose the method using the `combination_method` argument when creating an `MPML` instance.

| Method | Value | Description |
| --- | --- | --- |
| Majority voting | `"majority_vote"` | Combines class decisions from the trained perspective models. |
| Weighted combination | `"weighted_sum"` | Combines perspective outputs using their assigned weights. |
| Blending | `"blending"` | Trains base models and a meta-model using an internal holdout split. |
| Stacking | `"stacking"` | Uses cross-validated base-model predictions to train a meta-model. |
| Bagging | `"bagging"` | Trains bootstrap-based ensembles for individual perspectives. |
| Boosting | `"boosting"` | Trains AdaBoost-based ensembles for individual perspectives. |

The `MPML.fit()` and `MPML.predict()` methods dispatch to the selected strategy, so the basic workflow is consistent across methods.

### Majority voting

In majority voting, each perspective is trained with its assigned features and contributes a predicted class to the combined decision.

```python
ensemble = MPML(
    perspectives,
    combination_method="majority_vote",
    meta_model=LogisticRegression(max_iter=1000),
)
ensemble.fit(X_train, y_train)
predictions = ensemble.predict(X_test)
```

### Weighted combination

Weighted combination takes account of the integer weights supplied when perspectives were registered. The `weighted_sum` impact-scoring implementation uses a normalized weighted average of class probabilities. For the final prediction, the exact combination behaviour is defined in the package's `combination/voting.py` module, which was not provided for this README review.

```python
ensemble = MPML(
    perspectives,
    combination_method="weighted_sum",
    meta_model=LogisticRegression(max_iter=1000),
)
ensemble.fit(X_train, y_train)
predictions = ensemble.predict(X_test)
```

### Blending and stacking

Blending and stacking add a second-level **meta-model** that learns from the outputs of the perspective models.

```python
ensemble = MPML(
    perspectives,
    combination_method="stacking",  # Or "blending"
    meta_model=LogisticRegression(max_iter=1000),
)
ensemble.fit(X_train, y_train)
predictions = ensemble.predict(X_test)
```

In the supplied implementation, stacking requests **10-fold cross-validation** and blending uses an internal base-training subset described in the source as **80%** of the training dataset. These settings are currently defined by the implementation rather than exposed in the `MPML` constructor.

For impact analysis using stacking or blending, the selected meta-model must implement `predict_proba()`.

### Bagging and boosting

MPML also supports per-perspective bagging and boosting ensembles:

```python
bagged = MPML(perspectives, "bagging", LogisticRegression())
bagged.fit(X_train, y_train)
bagged_predictions = bagged.predict(X_test)

boosted = MPML(perspectives, "boosting", LogisticRegression())
boosted.fit(X_train, y_train)
boosted_predictions = boosted.predict(X_test)
```

The dedicated training methods also expose parameters for the underlying ensembles:

```python
# Dedicated training controls, when needed:
bagged.train_bagging_model(X_train, y_train, n_estimators=5, random_state=42)
boosted.train_boosting_model(
    X_train, y_train, n_estimators=50,
    learning_rate=1.0, random_state=42,
)
```

**Important:** The top-level `fit()` method invokes these methods with their defaults. Calling a dedicated trainer separately does not replace the full `fit()` workflow or its `fit_results` initialization.

## Understanding model impact

Beyond prediction, MPML provides tools for investigating how individual input features and whole perspectives affect the ensemble's score for a given class.

Impact is computed as a difference between an original score and a modified score:

\[
\text{Impact} = \text{Original score} - \text{Modified score}
\]

- **Positive impact:** The original feature or perspective increased the score for the evaluated class relative to the modified case.
- **Negative impact:** The modified case received a higher score for that class.
- **Zero impact:** The modification did not change the measured score.

These are **score-change measures**, not causal effects or guarantees of predictive importance. For voting-based methods, the ensemble score used for impact analysis can be a **vote share**, not a calibrated probability.

### Local feature impact

Use `get_feature_impact_score()` to estimate the impact of a specific feature in one observation. The observation must be a **single-row DataFrame** with the actual target label stored in a column named `class` (the default `target_column`).

```python
instance = X_test.iloc[[0]].copy()
instance["class"] = y_test.iloc[0]

impact = ensemble.get_feature_impact_score(
    feature_name="sepal length (cm)",
    model_name="model1",
    instance=instance,
    method="ablation",
    scope="ensemble",
)
print("Local feature impact:", impact)
```

The `scope` parameter selects the level at which the change is evaluated:

| Scope | Interpretation |
| --- | --- |
| `"model"` | Measures the score change in the selected perspective model. |
| `"ensemble"` | Measures the effect of modifying that perspective's feature on the combined ensemble score. |

#### Feature impact methods

| Method | How it modifies the input/model |
| --- | --- |
| `"ablation"` | Replaces the selected feature with a baseline value: the median for continuous numeric features or the mode otherwise. |
| `"background_ablation"` | Repeatedly replaces the feature with values sampled from the training data and averages the resulting score changes. |
| `"retrain"` | Removes the feature from the selected perspective, retrains a copy of that model, and compares scores. |

Example using background ablation:

```python
impact = ensemble.get_feature_impact_score(
    feature_name="sepal length (cm)",
    model_name="model1",
    instance=instance,
    method="background_ablation",
    scope="ensemble",
    n_samples=100,
    random_state=42,
)
```

Some estimators cannot provide class probabilities; model-level feature impact requires `predict_proba()`. Retraining is not supported when removing the selected feature would leave its perspective with no features.

### Local perspective impact

Use `get_perspective_impact()` to measure how the *entire perspective* contributes to the ensemble score for one observation:

```python
perspective_impact = ensemble.get_perspective_impact(
    model_name="model1",
    instance=instance,
)
print("Local perspective impact:", perspective_impact)
```

For voting, bagging, and boosting combinations, the impact procedure excludes the selected perspective and recomputes the score. For stacking and blending, it replaces that perspective's meta-model inputs with uniform class probabilities. Consequently, perspective impacts from different combination methods are not necessarily directly comparable.

At least two perspectives are required for whole-perspective impact analysis.

### Global feature and perspective impact

MPML computes global impact by **averaging local impact scores** across an evaluation dataset. First register the evaluation data:

```python
ensemble.set_evaluation_data(X_test, y_test)
```

To measure the average contribution of a specific feature within one perspective to the *ensemble*:

```python
global_feature_impact = ensemble.get_global_impact(
    model_name="model1",
    feature="sepal length (cm)",
    method="ablation",
)
print("Global feature impact:", global_feature_impact)
```

To measure the average contribution of the entire perspective, omit the `feature` argument:

```python
global_perspective_impact = ensemble.get_global_impact(
    model_name="model1"
)
print("Global perspective impact:", global_perspective_impact)
```

Global scores are **signed averages**, not averages of absolute values. Positive and negative instance-level impacts can cancel one another. Calculating global impact may be expensive, especially with background ablation (multiple samples per observation) or feature removal and retraining (model fitting for each observation).

## API overview

### `Perspectives`

Create a collection of model perspectives with `Perspectives(dataFrame)`.

| Method | Description |
| --- | --- |
| `add(model, feature_list, weight)` | Register a classifier, feature list and non-negative integer weight; returns its generated name. |
| `get_dict()` | Return the dictionary of registered perspectives. |
| `get_model(model_name)` | Return the original estimator for a perspective. |
| `get_features(model_name)` | Return the perspective's feature-column names. |
| `get_weight(model_name)` | Return its assigned voting weight. |
| `get_model_count()` | Return the number of registered perspectives. |

### `MPML`

Initialize with `MPML(perspectives, combination_method, meta_model)`.

| Method | Description |
| --- | --- |
| `fit(X_train, y_train)` | Train the chosen ensemble and return the `MPML` instance. |
| `predict(X_test)` | Predict class labels; returns a NumPy array. |
| `set_evaluation_data(X_test, y_test)` | Save an evaluation dataset for global impact calculations. |
| `get_feature_impact_score(...)` | Calculate local impact for a selected feature and perspective. |
| `get_background_ablation_score(...)` | Calculate sampled feature-ablation impact directly. |
| `get_perspective_impact(model_name, instance)` | Calculate the local impact of an entire perspective. |
| `get_global_impact(model_name, feature=None, ...)` | Average feature or perspective impacts over the evaluation data. |
| `train_bagging_model(...)` | Train per-perspective bagging ensembles with configurable parameters. |
| `train_boosting_model(...)` | Train per-perspective boosting ensembles with configurable parameters. |

After fitting, `ensemble.fit_results` contains the selected method, meta-model information where applicable, and the trained estimator and feature list for each perspective.

## Data and estimator requirements

- **DataFrames:** Provide pandas DataFrames with the named feature columns used by the perspectives. `predict()` explicitly checks that its input is a DataFrame.
- **Classification estimators:** Perspectives must contain scikit-learn `BaseEstimator` instances with `fit()` and `predict()` methods.
- **Probability support for impact analysis:** Many feature-impact paths call `predict_proba()`; select compatible base estimators when using these methods.
- **Weights:** Perspective weights must be non-negative Python integers. Choose weights with a positive total when using weighted combination.
- **Actual labels:** Local impact inputs require a `class` column containing the true class for each evaluated observation (unless the instance's `target_column` has been customized).
- **Preprocessing:** Encode categorical features, handle missing values, and perform scaling when required by the selected estimators. Each perspective is responsible for receiving data compatible with its model.
- **Reproducibility:** Set estimator `random_state` values where supported and use the impact methods' `random_state` argument for reproducible background sampling.

## Development

The supplied source is divided into a perspective registry, a top-level ensemble controller, and supporting combination modules:

```text
mpml/
├── __init__.py               # Public package exports (verify in your repository)
├── mpml.py                   # MPML class
├── perspective.py            # Perspectives class
└── combination/
    ├── voting.py             # Majority and weighted combination
    ├── blending.py           # Blending training and prediction
    ├── stacking.py           # Stacking training and prediction
    ├── bagging.py            # Bagging training and prediction
    └── boosting.py           # Boosting training and prediction
```

The combination-module names are taken from the imports in `mpml.py`. The full repository structure and contents of those modules were not supplied for this review.

### Local development with uv

If you have a local checkout with a valid `pyproject.toml`, you can install its project dependencies with:

```bash
uv sync
```

Build distributions with:

```bash
uv build
```

Run tests with `pytest` if your repository includes a test suite and has pytest installed:

```bash
uv run pytest
```

### Contributing

Contributions that improve documentation, testing, estimator compatibility, performance, and interpretability methods are welcome in principle. Consult the project's repository for its actual contribution policy and pull-request workflow.

## Project information

- **Package:** MPML — Multi-Perspective Machine Learning
- **Purpose:** Multi-perspective ensemble classification and feature-impact analysis
- **Core ecosystem:** Python, scikit-learn, pandas, NumPy
- **PyPI:** https://pypi.org/project/mpml_lib/
- **Source repository:** *Add the official GitHub or other repository URL.*
- **Issues and support:** *Add the project's issue-tracker or support URL.*
- **License:** *Add the actual license declared by the project.*
- **Release notes:** *Link a CHANGELOG.md or repository release page if available.*

## Citation

If you use MPML in academic work, cite the package's repository or published release using the author, version, year, and permanent URL supplied by the project. A formal citation or DOI was not provided with the source files, so no bibliographic entry is invented here.

## Acknowledgements

MPML is built to work with the scikit-learn estimator ecosystem and uses pandas and NumPy for dataset handling and numerical operations.

---

**MPML** helps researchers and developers combine multiple views of a dataset and examine how those views contribute to classification decisions.
