import numpy as np
import pytest
import scirs2
class TestDeciles:
def test_deciles_basic(self):
data = np.array([float(i) for i in range(1, 101)]) result = scirs2.deciles_py(data)
assert len(result) == 9
expected = [10.9, 20.8, 30.7, 40.6, 50.5, 60.4, 70.3, 80.2, 90.1]
for i, exp in enumerate(expected):
assert result[i] == pytest.approx(exp, abs=0.1)
def test_deciles_ordering(self):
data = np.random.normal(0, 1, 1000)
result = scirs2.deciles_py(data)
assert len(result) == 9
for i in range(len(result) - 1):
assert result[i] < result[i + 1]
def test_deciles_uniform_data(self):
data = np.linspace(0, 100, 1000)
result = scirs2.deciles_py(data)
expected = [10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0]
for i, exp in enumerate(expected):
assert result[i] == pytest.approx(exp, abs=1.0)
def test_deciles_small_dataset(self):
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])
result = scirs2.deciles_py(data)
assert len(result) == 9
assert np.all(result >= data.min())
assert np.all(result <= data.max())
def test_deciles_vs_quintiles(self):
data = np.random.normal(50, 10, 500)
deciles = scirs2.deciles_py(data)
quintiles = scirs2.quintiles_py(data)
assert deciles[1] == pytest.approx(quintiles[0], abs=0.1)
assert deciles[3] == pytest.approx(quintiles[1], abs=0.1)
assert deciles[5] == pytest.approx(quintiles[2], abs=0.1)
assert deciles[7] == pytest.approx(quintiles[3], abs=0.1)
def test_deciles_finer_granularity(self):
data = np.random.normal(100, 15, 800)
deciles = scirs2.deciles_py(data)
assert deciles[0] < deciles[1] < deciles[2]
assert deciles[6] < deciles[7] < deciles[8]
class TestStandardErrorMean:
def test_sem_basic(self):
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
result = scirs2.sem_py(data, ddof=1)
expected = np.std(data, ddof=1) / np.sqrt(len(data))
assert result == pytest.approx(expected, abs=0.001)
def test_sem_ddof_zero(self):
data = np.array([10.0, 20.0, 30.0, 40.0, 50.0])
result = scirs2.sem_py(data, ddof=0)
expected = np.std(data, ddof=0) / np.sqrt(len(data))
assert result == pytest.approx(expected, abs=0.001)
def test_sem_ddof_effect(self):
data = np.array([5.0, 10.0, 15.0, 20.0, 25.0, 30.0])
sem_ddof0 = scirs2.sem_py(data, ddof=0)
sem_ddof1 = scirs2.sem_py(data, ddof=1)
assert sem_ddof1 > sem_ddof0
def test_sem_large_dataset(self):
np.random.seed(42)
data_small = np.random.normal(100, 15, 10)
data_large = np.random.normal(100, 15, 1000)
sem_small = scirs2.sem_py(data_small, ddof=1)
sem_large = scirs2.sem_py(data_large, ddof=1)
assert sem_large < sem_small
def test_sem_constant_data(self):
data = np.array([5.0, 5.0, 5.0, 5.0, 5.0])
result = scirs2.sem_py(data, ddof=1)
assert result == pytest.approx(0.0, abs=1e-10)
def test_sem_formula_verification(self):
data = np.array([12.5, 15.3, 18.7, 21.2, 24.8, 27.1, 30.5])
std_dev = np.std(data, ddof=1)
n = len(data)
expected_sem = std_dev / np.sqrt(n)
result = scirs2.sem_py(data, ddof=1)
assert result == pytest.approx(expected_sem, abs=1e-10)
def test_sem_normal_distribution(self):
np.random.seed(123)
data = np.random.normal(50, 10, 100)
result = scirs2.sem_py(data, ddof=1)
assert result > 0
assert result < np.std(data, ddof=1)
class TestPercentileRange:
def test_percentile_range_basic(self):
data = np.array([float(i) for i in range(1, 101)])
result = scirs2.percentile_range_py(data, 25.0, 75.0)
assert result == pytest.approx(50.0, abs=1.0)
@pytest.mark.skip(reason="Overflow bug in scirs2-stats quantile_simd.rs:59 with normal distribution")
def test_percentile_range_iqr_comparison(self):
data = np.random.normal(100, 15, 500)
pct_range = scirs2.percentile_range_py(data, 25.0, 75.0)
iqr = scirs2.iqr_py(data)
assert pct_range == pytest.approx(iqr, abs=0.5)
def test_percentile_range_custom_percentiles(self):
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0])
result = scirs2.percentile_range_py(data, 10.0, 90.0)
assert result > 0
assert result < (data.max() - data.min())
@pytest.mark.skip(reason="Overflow bug in scirs2-stats quantile_simd.rs:59 with normal distribution")
def test_percentile_range_narrow(self):
data = np.random.normal(50, 10, 1000)
narrow_range = scirs2.percentile_range_py(data, 45.0, 55.0)
wide_range = scirs2.percentile_range_py(data, 10.0, 90.0)
assert narrow_range < wide_range / 2
@pytest.mark.skip(reason="Overflow bug in scirs2-stats quantile_simd.rs:59 with normal distribution")
def test_percentile_range_symmetric_distribution(self):
np.random.seed(42)
data = np.random.normal(0, 1, 1000)
lower_range = scirs2.percentile_range_py(data, 5.0, 50.0)
upper_range = scirs2.percentile_range_py(data, 50.0, 95.0)
assert lower_range == pytest.approx(upper_range, abs=0.3)
def test_percentile_range_extreme(self):
data = np.linspace(0, 100, 1000)
result = scirs2.percentile_range_py(data, 1.0, 99.0)
full_range = data.max() - data.min()
assert result == pytest.approx(0.98 * full_range, abs=2.0)
def test_percentile_range_identical_values(self):
data = np.array([42.0, 42.0, 42.0, 42.0, 42.0])
result = scirs2.percentile_range_py(data, 25.0, 75.0)
assert result == pytest.approx(0.0, abs=1e-10)
def test_percentile_range_interpolation(self):
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
result = scirs2.percentile_range_py(data, 20.0, 80.0, interpolation="linear")
assert result > 0
assert result <= (data.max() - data.min())
class TestRealWorldScenarios:
def test_income_deciles(self):
np.random.seed(42)
incomes = np.random.lognormal(mean=10, sigma=0.8, size=1000)
deciles = scirs2.deciles_py(incomes)
for i in range(len(deciles) - 1):
assert deciles[i] < deciles[i + 1]
assert deciles[8] > 3 * deciles[0]
def test_test_scores_sem(self):
test_scores = np.array([85.0, 88.0, 92.0, 79.0, 95.0, 87.0, 90.0, 84.0, 91.0, 86.0])
mean_score = np.mean(test_scores)
sem = scirs2.sem_py(test_scores, ddof=1)
ci_lower = mean_score - 1.96 * sem
ci_upper = mean_score + 1.96 * sem
assert ci_lower < mean_score < ci_upper
assert ci_upper - ci_lower > 0
@pytest.mark.skip(reason="Overflow bug in scirs2-stats quantile_simd.rs:59 with normal distribution")
def test_quality_control_percentile_range(self):
np.random.seed(42)
measurements = np.random.normal(100, 2, 500)
middle_range = scirs2.percentile_range_py(measurements, 10.0, 90.0)
full_range = measurements.max() - measurements.min()
assert middle_range < full_range
assert middle_range < 15
def test_performance_metrics_deciles(self):
np.random.seed(123)
performance = np.random.beta(5, 2, 1000) * 100
deciles = scirs2.deciles_py(performance)
assert deciles[0] > 0 assert deciles[8] < 100 assert deciles[4] > deciles[0]
class TestEdgeCases:
def test_deciles_minimum_size(self):
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
result = scirs2.deciles_py(data)
assert len(result) == 9
def test_sem_minimum_size_ddof1(self):
data = np.array([1.0, 2.0])
result = scirs2.sem_py(data, ddof=1)
assert result > 0
def test_percentile_range_full_range(self):
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
result = scirs2.percentile_range_py(data, 0.0, 100.0)
assert result == pytest.approx(4.0, abs=0.1)
def test_deciles_all_equal(self):
data = np.array([7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0, 7.0])
result = scirs2.deciles_py(data)
assert np.all(np.abs(result - 7.0) < 1e-10)
def test_sem_large_values(self):
data = np.array([1000000.0, 2000000.0, 3000000.0, 4000000.0, 5000000.0])
result = scirs2.sem_py(data, ddof=1)
assert result > 0
assert result < np.mean(data)
def test_percentile_range_negative_values(self):
data = np.array([-10.0, -5.0, 0.0, 5.0, 10.0, 15.0, 20.0])
result = scirs2.percentile_range_py(data, 25.0, 75.0)
assert result > 0
class TestNumericalStability:
def test_deciles_very_small_values(self):
data = np.array([0.001, 0.002, 0.003, 0.004, 0.005, 0.006, 0.007, 0.008, 0.009, 0.010])
result = scirs2.deciles_py(data)
assert len(result) == 9
assert np.all(result >= data.min())
assert np.all(result <= data.max())
def test_sem_high_precision(self):
data = np.array([1.23456789, 2.34567890, 3.45678901, 4.56789012, 5.67890123])
result = scirs2.sem_py(data, ddof=1)
expected = np.std(data, ddof=1) / np.sqrt(len(data))
assert result == pytest.approx(expected, rel=1e-6)
def test_percentile_range_wide_range(self):
data = np.array([0.001, 1.0, 100.0, 10000.0, 1000000.0])
result = scirs2.percentile_range_py(data, 20.0, 80.0)
assert result > 0
if __name__ == "__main__":
pytest.main([__file__, "-v"])