153 lines
6.8 KiB
Python
153 lines
6.8 KiB
Python
# Tests for the CensoredData class.
|
|
|
|
import pytest
|
|
import numpy as np
|
|
from numpy.testing import assert_equal, assert_array_equal
|
|
from scipy.stats import CensoredData
|
|
|
|
|
|
class TestCensoredData:
|
|
|
|
def test_basic(self):
|
|
uncensored = [1]
|
|
left = [0]
|
|
right = [2, 5]
|
|
interval = [[2, 3]]
|
|
data = CensoredData(uncensored, left=left, right=right,
|
|
interval=interval)
|
|
assert_equal(data._uncensored, uncensored)
|
|
assert_equal(data._left, left)
|
|
assert_equal(data._right, right)
|
|
assert_equal(data._interval, interval)
|
|
|
|
udata = data._uncensor()
|
|
assert_equal(udata, np.concatenate((uncensored, left, right,
|
|
np.mean(interval, axis=1))))
|
|
|
|
def test_right_censored(self):
|
|
x = np.array([0, 3, 2.5])
|
|
is_censored = np.array([0, 1, 0], dtype=bool)
|
|
data = CensoredData.right_censored(x, is_censored)
|
|
assert_equal(data._uncensored, x[~is_censored])
|
|
assert_equal(data._right, x[is_censored])
|
|
assert_equal(data._left, [])
|
|
assert_equal(data._interval, np.empty((0, 2)))
|
|
|
|
def test_left_censored(self):
|
|
x = np.array([0, 3, 2.5])
|
|
is_censored = np.array([0, 1, 0], dtype=bool)
|
|
data = CensoredData.left_censored(x, is_censored)
|
|
assert_equal(data._uncensored, x[~is_censored])
|
|
assert_equal(data._left, x[is_censored])
|
|
assert_equal(data._right, [])
|
|
assert_equal(data._interval, np.empty((0, 2)))
|
|
|
|
def test_interval_censored_basic(self):
|
|
a = [0.5, 2.0, 3.0, 5.5]
|
|
b = [1.0, 2.5, 3.5, 7.0]
|
|
data = CensoredData.interval_censored(low=a, high=b)
|
|
assert_array_equal(data._interval, np.array(list(zip(a, b))))
|
|
assert data._uncensored.shape == (0,)
|
|
assert data._left.shape == (0,)
|
|
assert data._right.shape == (0,)
|
|
|
|
def test_interval_censored_mixed(self):
|
|
# This is actually a mix of uncensored, left-censored, right-censored
|
|
# and interval-censored data. Check that when the `interval_censored`
|
|
# class method is used, the data is correctly separated into the
|
|
# appropriate arrays.
|
|
a = [0.5, -np.inf, -13.0, 2.0, 1.0, 10.0, -1.0]
|
|
b = [0.5, 2500.0, np.inf, 3.0, 1.0, 11.0, np.inf]
|
|
data = CensoredData.interval_censored(low=a, high=b)
|
|
assert_array_equal(data._interval, [[2.0, 3.0], [10.0, 11.0]])
|
|
assert_array_equal(data._uncensored, [0.5, 1.0])
|
|
assert_array_equal(data._left, [2500.0])
|
|
assert_array_equal(data._right, [-13.0, -1.0])
|
|
|
|
def test_interval_to_other_types(self):
|
|
# The interval parameter can represent uncensored and
|
|
# left- or right-censored data. Test the conversion of such
|
|
# an example to the canonical form in which the different
|
|
# types have been split into the separate arrays.
|
|
interval = np.array([[0, 1], # interval-censored
|
|
[2, 2], # not censored
|
|
[3, 3], # not censored
|
|
[9, np.inf], # right-censored
|
|
[8, np.inf], # right-censored
|
|
[-np.inf, 0], # left-censored
|
|
[1, 2]]) # interval-censored
|
|
data = CensoredData(interval=interval)
|
|
assert_equal(data._uncensored, [2, 3])
|
|
assert_equal(data._left, [0])
|
|
assert_equal(data._right, [9, 8])
|
|
assert_equal(data._interval, [[0, 1], [1, 2]])
|
|
|
|
def test_empty_arrays(self):
|
|
data = CensoredData(uncensored=[], left=[], right=[], interval=[])
|
|
assert data._uncensored.shape == (0,)
|
|
assert data._left.shape == (0,)
|
|
assert data._right.shape == (0,)
|
|
assert data._interval.shape == (0, 2)
|
|
assert len(data) == 0
|
|
|
|
def test_invalid_constructor_args(self):
|
|
with pytest.raises(ValueError, match='must be a one-dimensional'):
|
|
CensoredData(uncensored=[[1, 2, 3]])
|
|
with pytest.raises(ValueError, match='must be a one-dimensional'):
|
|
CensoredData(left=[[1, 2, 3]])
|
|
with pytest.raises(ValueError, match='must be a one-dimensional'):
|
|
CensoredData(right=[[1, 2, 3]])
|
|
with pytest.raises(ValueError, match='must be a two-dimensional'):
|
|
CensoredData(interval=[[1, 2, 3]])
|
|
|
|
with pytest.raises(ValueError, match='must not contain nan'):
|
|
CensoredData(uncensored=[1, np.nan, 2])
|
|
with pytest.raises(ValueError, match='must not contain nan'):
|
|
CensoredData(left=[1, np.nan, 2])
|
|
with pytest.raises(ValueError, match='must not contain nan'):
|
|
CensoredData(right=[1, np.nan, 2])
|
|
with pytest.raises(ValueError, match='must not contain nan'):
|
|
CensoredData(interval=[[1, np.nan], [2, 3]])
|
|
|
|
with pytest.raises(ValueError,
|
|
match='both values must not be infinite'):
|
|
CensoredData(interval=[[1, 3], [2, 9], [np.inf, np.inf]])
|
|
|
|
with pytest.raises(ValueError,
|
|
match='left value must not exceed the right'):
|
|
CensoredData(interval=[[1, 0], [2, 2]])
|
|
|
|
@pytest.mark.parametrize('func', [CensoredData.left_censored,
|
|
CensoredData.right_censored])
|
|
def test_invalid_left_right_censored_args(self, func):
|
|
with pytest.raises(ValueError,
|
|
match='`x` must be one-dimensional'):
|
|
func([[1, 2, 3]], [0, 1, 1])
|
|
with pytest.raises(ValueError,
|
|
match='`censored` must be one-dimensional'):
|
|
func([1, 2, 3], [[0, 1, 1]])
|
|
with pytest.raises(ValueError, match='`x` must not contain'):
|
|
func([1, 2, np.nan], [0, 1, 1])
|
|
with pytest.raises(ValueError, match='must have the same length'):
|
|
func([1, 2, 3], [0, 0, 1, 1])
|
|
|
|
def test_invalid_censored_args(self):
|
|
with pytest.raises(ValueError,
|
|
match='`low` must be a one-dimensional'):
|
|
CensoredData.interval_censored(low=[[3]], high=[4, 5])
|
|
with pytest.raises(ValueError,
|
|
match='`high` must be a one-dimensional'):
|
|
CensoredData.interval_censored(low=[3], high=[[4, 5]])
|
|
with pytest.raises(ValueError, match='`low` must not contain'):
|
|
CensoredData.interval_censored([1, 2, np.nan], [0, 1, 1])
|
|
with pytest.raises(ValueError, match='must have the same length'):
|
|
CensoredData.interval_censored([1, 2, 3], [0, 0, 1, 1])
|
|
|
|
def test_count_censored(self):
|
|
x = [1, 2, 3]
|
|
# data1 has no censored data.
|
|
data1 = CensoredData(x)
|
|
assert data1.num_censored() == 0
|
|
data2 = CensoredData(uncensored=[2.5], left=[10], interval=[[0, 1]])
|
|
assert data2.num_censored() == 2
|