Files
2026-07-13 13:22:52 +08:00

96 lines
2.8 KiB
Python

"""Unit tests for the ActionOptimizer."""
import numpy as np
import pandas as pd
import pytest
import shap
from shap.utils._exceptions import ConvergenceError, InvalidAction
def create_basic_scenario():
X = pd.DataFrame({"feature1": np.ones(5), "feature2": np.ones(5), "feature3": np.ones(5)})
class IncreaseFeature1(shap.actions.Action):
"""Sample action."""
def __init__(self, amount):
self.amount = amount
self.cost = 5 * amount
def __call__(self, X):
X["feature1"] += self.amount
def __str__(self):
return f"Improve feature1 by {self.amount}."
class IncreaseFeature2(shap.actions.Action):
"""Sample action."""
def __init__(self, amount):
self.amount = amount
self.cost = 3 * amount
def __call__(self, X):
X["feature2"] += self.amount
def __str__(self):
return f"Improve feature2 by {self.amount}."
class IncreaseFeature3(shap.actions.Action):
"""Sample action."""
def __init__(self, amount):
self.amount = amount
self.cost = 4 * amount
def __call__(self, X):
X["feature3"] += self.amount
def __str__(self):
return f"Improve feature3 by {self.amount}."
def passed(x):
return np.sum(x) > 10
return X, IncreaseFeature1, IncreaseFeature2, IncreaseFeature3, passed
def test_basic_run():
X, IncreaseFeature1, IncreaseFeature2, IncreaseFeature3, passed = create_basic_scenario()
possible_actions = [
[IncreaseFeature1(i) for i in range(1, 10)],
IncreaseFeature2(5),
[IncreaseFeature3(i) for i in range(1, 20)],
]
optimizer = shap.ActionOptimizer(passed, possible_actions)
actions = optimizer(X.iloc[0])
assert len(actions) == 2
assert sum(a.cost for a in actions) == 27 # ensure we got the optimal answer
def test_too_few_evals():
X, IncreaseFeature1, IncreaseFeature2, IncreaseFeature3, passed = create_basic_scenario()
possible_actions = [
[IncreaseFeature1(i) for i in range(1, 10)],
IncreaseFeature2(5),
[IncreaseFeature3(i) for i in range(1, 20)],
]
optimizer = shap.ActionOptimizer(passed, possible_actions)
with pytest.raises(ConvergenceError):
optimizer(X.iloc[0], max_evals=3)
def test_run_out_of_group():
X, IncreaseFeature1, IncreaseFeature2, IncreaseFeature3, passed = create_basic_scenario()
possible_actions = [[IncreaseFeature1(i) for i in range(1, 10)], IncreaseFeature2(5), [IncreaseFeature3(1)]]
optimizer = shap.ActionOptimizer(passed, possible_actions)
actions = optimizer(X.iloc[0])
print(actions)
assert len(actions) == 3
def test_bad_action():
with pytest.raises(InvalidAction):
shap.ActionOptimizer(None, [None]) # type: ignore