Metadata-Version: 2.4
Name: steer-learn
Version: 0.1.1
Summary: SKLearn extention for Markov models
Author-email: Miguel <miguel.melo@driverevel.com>
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: scikit-learn
Requires-Dist: numpy
Dynamic: license-file

# steer_learn

`steer_learn` is a compact, scikit-learn-style toolkit for learning from **state transitions**, **absorbing processes**, and **sequence-like data**.

It currently includes:

- absorbing Markov-chain estimators
- a Bayesian model for path-to-outcome prediction
- hidden Markov models with discrete and Gaussian emissions
- lightweight preprocessing transformers for path data

The project is designed for users who want familiar estimator APIs while working on problems such as funnels, journeys, workflows, process states, and sequence-aware classification.

## Core ideas

Many datasets can be represented as transitions between states:

- a user moves from landing page to signup to purchase
- a workflow moves from pending to reviewed to approved
- a machine process moves through operational stages until termination
- a sequence contains observations whose meaning depends on the previous state

`steer_learn` provides small, readable building blocks for these settings with interfaces that feel close to scikit-learn.

## Main components

### Estimators

#### `MarkovAbsorbingModel`

Learns a transition matrix from one-step transitions encoded as one-hot source and target states.  
After fitting, it estimates the probability that a starting state will end in each absorbing state.

Use it when your training data is naturally represented as:

- current state -> next state
- one sample per transition
- terminal outcomes represented as absorbing states in the chain

#### `BayesianAbsorbingModel`

Predicts an absorbing outcome directly from a full path of visited states.

Instead of estimating a classical absorbing-chain transition system from one-step transitions, this model learns how the presence of states in a path relates to the final absorbing class. It is useful when each sample is a full sequence or path and the target is the terminal outcome.

Use it when your training data looks like:

- one sample per path
- `X` contains sequences of visited state indices
- `y` contains the final absorbing outcome encoded as one-hot labels

#### `HiddenMarkovModel`

A discrete-emission hidden Markov model classifier.

It combines:

- transition probabilities from the previous hidden state
- per-feature categorical emission probabilities
- sklearn-style `fit`, `predict`, and `predict_proba` methods

Use it when observations are discrete symbols and the previous state is available as part of the input.

#### `GaussianHiddenMarkovModel`

A continuous-emission hidden Markov model classifier.

It is intended for cases where:

- the first column identifies the previous state
- the remaining columns are continuous features
- the target is the current hidden state

### Transformers

#### `PathSplitter`

Splits raw path strings into token sequences.

#### `PathFlanker`

Adds a configurable start token and appends the aligned target label at the end of each sequence.

#### `TransitionRoller`

Converts sequences into consecutive transition pairs.


#### `PaddingTransformer`

Pads sequences to a uniform length using a special `<PADDING>` token.

- `fit(X)`: computes the maximum sequence length
- `transform(X)`: left-pads each sequence to `max_len`

Example:

```python
from steer_learn.transformer import PaddingTransformer

X = [
    ["A", "B"],
    ["A", "B", "C", "D"],
]

padder = PaddingTransformer()
padder.fit(X)

X_padded = [padder.transform(x) for x in X]
```


## Installation

```bash
pip install steer_learn
```

## Top-level imports

The following objects are exposed from `steer_learn.__init__`:

```python
from steer_learn import (
    MarkovAbsorbingModel,
    BayesianAbsorbingModel,
    HiddenMarkovModel,
    GaussianHiddenMarkovModel,
)
```

Transformer utilities are available from the transformer module:

```python
from steer_learn.transformer import (
    PathSplitter,
    PathFlanker,
    TransitionRoller,
)
```

## Quick start

### 1. Absorbing-state prediction from transitions

Use `MarkovAbsorbingModel` when each row represents a transition from a source state to a target state.

```python
import numpy as np
from steer_learn import MarkovAbsorbingModel

# States: A, B, C
# X = source state (one-hot)
# y = target state (one-hot)
X = np.array([
    [1, 0, 0],  # A -> B
    [1, 0, 0],  # A -> B
    [0, 1, 0],  # B -> C
    [0, 1, 0],  # B -> C
])

y = np.array([
    [0, 1, 0],
    [0, 1, 0],
    [0, 0, 1],
    [0, 0, 1],
])

model = MarkovAbsorbingModel().fit(X, y)

pred = model.predict(np.array([
    [1, 0, 0],
    [0, 1, 0],
]))

proba = model.predict_proba(np.array([
    [1, 0, 0],
    [0, 1, 0],
]))
```

### 2. Absorbing-state prediction from full paths

Use `BayesianAbsorbingModel` when each sample is a path and the target is the terminal absorbing class.

```python
import numpy as np
from steer_learn import BayesianAbsorbingModel

# Example paths encoded as state indices
X = np.array([
    [0, 1, 2],
    [0, 1, 1],
    [3, 4, 4],
    [3, 4, 5],
])

# Two possible absorbing outcomes encoded as one-hot vectors
y = np.array([
    [1, 0],
    [1, 0],
    [0, 1],
    [0, 1],
])

model = BayesianAbsorbingModel().fit(X, y)

pred = model.predict(X)
log_proba = model.predict_log_proba(X)
```

### 3. Discrete hidden Markov model

Use `HiddenMarkovModel` when observations are discrete.

```python
import numpy as np
from steer_learn import HiddenMarkovModel

# X columns: [previous_state, symbol_1, symbol_2]
X = np.array([
    [0, 1, 2],
    [0, 1, 1],
    [1, 0, 2],
    [1, 0, 1],
])

y = np.array([0, 0, 1, 1])

model = HiddenMarkovModel().fit(X, y)

pred = model.predict(X)
proba = model.predict_proba(X)
```

### 4. Gaussian hidden Markov model

Use `GaussianHiddenMarkovModel` when observations are continuous.

```python
import numpy as np
from steer_learn import GaussianHiddenMarkovModel

# X columns: [previous_state, feature_1, feature_2]
X = np.array([
    [0, 0.2, 1.1],
    [0, 0.1, 0.9],
    [1, 2.2, 3.0],
    [1, 2.0, 2.8],
])

y = np.array([0, 0, 1, 1])

model = GaussianHiddenMarkovModel().fit(X, y)

pred = model.predict(X)
proba = model.predict_proba(X)
```

## Input conventions

### `MarkovAbsorbingModel`

Expected training data:

- `X`: 2D array of one-hot encoded source states
- `y`: 2D array of one-hot encoded target states

Prediction behavior:

- `predict(X)`: returns the most likely absorbing state index
- `predict_proba(X)`: returns probabilities over absorbing states

### `BayesianAbsorbingModel`

Expected training data:

- `X`: 2D array where each row is a path represented as state indices
- `y`: 2D one-hot array for the absorbing target class

Prediction behavior:

- `predict(X)`: returns the most likely absorbing class index
- `predict_log_proba(X)`: returns per-class log-scores for each path

This estimator is useful when the full visited path matters more than local one-step transition structure.

### `HiddenMarkovModel`

Expected training data:

- `X[:, 0]`: previous state index
- `X[:, 1:]`: discrete observed symbols
- `y`: target state index

Prediction behavior:

- `predict(X)`: returns the most likely hidden state
- `predict_proba(X)`: returns state scores based on transition and emission terms

### `GaussianHiddenMarkovModel`

Expected training data:

- `X[:, 0]`: previous state index
- `X[:, 1:]`: continuous observation vector
- `y`: target state index

Prediction behavior:

- `predict(X)`: returns the most likely hidden state
- `predict_proba(X)`: returns transition-weighted Gaussian scores

### `PathSplitter`

- input: iterable of strings
- output: tokenized path sequences split using `sep`

### `PathFlanker`

- input: iterable of tokenized sequences plus aligned labels `y`
- output: `[start_state] + sequence + [target_label]`

Example:

```python
from steer_learn.transformer import PathFlanker

X = [["Landing", "Signup"], ["Landing", "Pricing"]]
y = ["Activated", "Churned"]

flanker = PathFlanker(start_state="<START>")
X_flanked = flanker.fit_transform(X, y)
```

### `TransitionRoller`

- input: iterable of sequences
- output: 2-column consecutive transitions

## Typical use cases

`steer_learn` is a good fit for:

- predicting terminal outcomes in funnels and workflows
- modeling absorbing states in process data
- mapping full paths to final outcomes
- sequence-aware classification with previous-state context
- converting raw path strings into supervised transition datasets
- building small, readable transition-based ML pipelines

## Example path-processing workflow

```python
from steer_learn.transformer import PathSplitter, PathFlanker, TransitionRoller

paths = [
    "Landing -> Signup",
    "Landing -> Pricing",
]
labels = ["Activated", "Churned"]

splitter = PathSplitter(sep="->")
flanker = PathFlanker(start_state="<START>")
roller = TransitionRoller()

X = splitter.fit_transform(paths)
X = flanker.fit_transform(X, labels)
transitions = roller.transform(X)
```

## Design goals

This project currently emphasizes:

- readable implementations
- sklearn-like APIs
- compact transition-oriented models
- simple NumPy-based usage patterns
- explicit input conventions for each estimator

## Contributing

Contributions are welcome. Good additions usually preserve the current philosophy:

- keep APIs simple and explicit
- document estimator assumptions clearly
- prefer readability over unnecessary abstraction
- maintain consistent import and naming patterns
