125 lines
3.7 KiB
Python
125 lines
3.7 KiB
Python
import numpy as np
|
|
import pandas as pd
|
|
import pytest
|
|
import scipy.sparse as ssp
|
|
|
|
import shap
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"arr",
|
|
[
|
|
np.arange(100),
|
|
["zz"] * 100,
|
|
pd.Series(range(100), name="test"),
|
|
pd.DataFrame(np.random.RandomState(0).randn(100, 2), columns=["a", "b"]),
|
|
],
|
|
)
|
|
def test_sample_basic(arr):
|
|
"""Tests the basic functionality of `sample()` on a variety of array-like objects."""
|
|
new_arr = shap.utils.sample(arr, 30, random_state=42)
|
|
assert len(new_arr) == 30
|
|
|
|
|
|
def test_sample_basic_sparse():
|
|
"""Tests the basic functionality of `sample()` on sparse objects."""
|
|
arr = ssp.csr_matrix((100, 3), dtype=np.int8)
|
|
new_arr = shap.utils.sample(arr, 30, random_state=42)
|
|
assert new_arr.shape[0] == 30
|
|
|
|
|
|
def test_sample_no_op():
|
|
"""Ensures that `sample()` is a no-op when numsamples is larger
|
|
than the size of X.
|
|
"""
|
|
arr = np.arange(50)
|
|
new_arr = shap.utils.sample(arr, 100, random_state=42)
|
|
|
|
assert len(arr) == len(new_arr)
|
|
|
|
|
|
def test_sample_sampling_without_replacement():
|
|
"""Ensures that `sample()` is performing sampling without replacement.
|
|
|
|
See GH dsgibbons#36.
|
|
"""
|
|
arr = np.arange(100)
|
|
new_arr = shap.utils.sample(arr, 99, random_state=0)
|
|
|
|
assert len(new_arr) == 99
|
|
assert len(np.unique(new_arr)) == 99
|
|
|
|
|
|
def test_sample_can_be_zipped():
|
|
"""Ensures that the sampling is done via indexing.
|
|
|
|
That is, sampling X and y separately would give the same result as sampling
|
|
concat(X, y), up to a random state. Our `datasets` module relies on
|
|
this behaviour.
|
|
"""
|
|
arr1 = pd.Series(np.arange(100))
|
|
arr2 = pd.Series(np.repeat(np.arange(25), 4))
|
|
combined = pd.DataFrame(
|
|
{
|
|
"arr1": arr1,
|
|
"arr2": arr2,
|
|
}
|
|
)
|
|
|
|
new_arr1 = shap.utils.sample(arr1, 75, random_state=42)
|
|
new_arr2 = shap.utils.sample(arr2, 75, random_state=42)
|
|
new_combined = shap.utils.sample(combined, 75, random_state=42)
|
|
|
|
assert (new_arr1 == new_combined["arr1"]).all()
|
|
assert (new_arr2 == new_combined["arr2"]).all()
|
|
|
|
|
|
def test_opchain_repr():
|
|
"""Ensures OpChain repr is working properly"""
|
|
opchain = (
|
|
shap.utils.OpChain("shap.DummyExplanation")
|
|
.foo.foo(0, "big_blue_bear")
|
|
.foo(0, v1=10)
|
|
.foo(k1="alpha", k2="beta")
|
|
.baz
|
|
)
|
|
expected_repr = "shap.DummyExplanation.foo.foo(0, 'big_blue_bear').foo(0, v1=10).foo(k1='alpha', k2='beta').baz"
|
|
|
|
assert repr(opchain) == expected_repr
|
|
|
|
|
|
def test_format_value_empty_string():
|
|
"""Tests that format_value() handles empty strings without raising IndexError."""
|
|
# Test with empty string
|
|
result = shap.utils._general.format_value("", "%0.03f")
|
|
assert result == ""
|
|
|
|
|
|
def test_format_value_negative_number():
|
|
"""Tests that format_value() correctly formats negative numbers with unicode minus sign."""
|
|
result = shap.utils._general.format_value(-1.5, "%0.03f")
|
|
assert result == "\u2212" + "1.5"
|
|
|
|
|
|
def test_format_value_positive_number():
|
|
"""Tests that format_value() correctly formats positive numbers."""
|
|
result = shap.utils._general.format_value(1.5, "%0.03f")
|
|
assert result == "1.5"
|
|
|
|
|
|
def test_format_value_trailing_zeros():
|
|
"""Tests that format_value() removes trailing zeros."""
|
|
result = shap.utils._general.format_value(1.5000, "%0.03f")
|
|
assert result == "1.5"
|
|
|
|
|
|
def test_format_value_string_input():
|
|
"""Tests that format_value() handles string inputs correctly."""
|
|
# Test with non-empty string
|
|
result = shap.utils._general.format_value("test_string", "%0.03f")
|
|
assert result == "test_string"
|
|
|
|
# Test with string that starts with minus
|
|
result = shap.utils._general.format_value("-123", "%0.03f")
|
|
assert result == "\u2212" + "123"
|