test_variation.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. import numpy as np
  2. from numpy.testing import assert_equal, assert_allclose
  3. import pytest
  4. from scipy.stats import variation
  5. class TestVariation:
  6. """
  7. Test class for scipy.stats.variation
  8. """
  9. def test_ddof(self):
  10. x = np.arange(9.0)
  11. assert_allclose(variation(x, ddof=1), np.sqrt(60/8)/4)
  12. @pytest.mark.parametrize('sgn', [1, -1])
  13. def test_sign(self, sgn):
  14. x = np.array([1, 2, 3, 4, 5])
  15. v = variation(sgn*x)
  16. expected = sgn*np.sqrt(2)/3
  17. assert_allclose(v, expected, rtol=1e-10)
  18. def test_scalar(self):
  19. # A scalar is treated like a 1-d sequence with length 1.
  20. assert_equal(variation(4.0), 0.0)
  21. @pytest.mark.parametrize('nan_policy, expected',
  22. [('propagate', np.nan),
  23. ('omit', np.sqrt(20/3)/4)])
  24. def test_variation_nan(self, nan_policy, expected):
  25. x = np.arange(10.)
  26. x[9] = np.nan
  27. assert_allclose(variation(x, nan_policy=nan_policy), expected)
  28. def test_nan_policy_raise(self):
  29. x = np.array([1.0, 2.0, np.nan, 3.0])
  30. with pytest.raises(ValueError, match='input contains nan'):
  31. variation(x, nan_policy='raise')
  32. def test_bad_nan_policy(self):
  33. with pytest.raises(ValueError, match='must be one of'):
  34. variation([1, 2, 3], nan_policy='foobar')
  35. def test_keepdims(self):
  36. x = np.arange(10).reshape(2, 5)
  37. y = variation(x, axis=1, keepdims=True)
  38. expected = np.array([[np.sqrt(2)/2],
  39. [np.sqrt(2)/7]])
  40. assert_allclose(y, expected)
  41. @pytest.mark.parametrize('axis, expected',
  42. [(0, np.empty((1, 0))),
  43. (1, np.full((5, 1), fill_value=np.nan))])
  44. def test_keepdims_size0(self, axis, expected):
  45. x = np.zeros((5, 0))
  46. y = variation(x, axis=axis, keepdims=True)
  47. assert_equal(y, expected)
  48. @pytest.mark.parametrize('incr, expected_fill', [(0, np.inf), (1, np.nan)])
  49. def test_keepdims_and_ddof_eq_len_plus_incr(self, incr, expected_fill):
  50. x = np.array([[1, 1, 2, 2], [1, 2, 3, 3]])
  51. y = variation(x, axis=1, ddof=x.shape[1] + incr, keepdims=True)
  52. assert_equal(y, np.full((2, 1), fill_value=expected_fill))
  53. def test_propagate_nan(self):
  54. # Check that the shape of the result is the same for inputs
  55. # with and without nans, cf gh-5817
  56. a = np.arange(8).reshape(2, -1).astype(float)
  57. a[1, 0] = np.nan
  58. v = variation(a, axis=1, nan_policy="propagate")
  59. assert_allclose(v, [np.sqrt(5/4)/1.5, np.nan], atol=1e-15)
  60. def test_axis_none(self):
  61. # Check that `variation` computes the result on the flattened
  62. # input when axis is None.
  63. y = variation([[0, 1], [2, 3]], axis=None)
  64. assert_allclose(y, np.sqrt(5/4)/1.5)
  65. def test_bad_axis(self):
  66. # Check that an invalid axis raises np.AxisError.
  67. x = np.array([[1, 2, 3], [4, 5, 6]])
  68. with pytest.raises(np.AxisError):
  69. variation(x, axis=10)
  70. def test_mean_zero(self):
  71. # Check that `variation` returns inf for a sequence that is not
  72. # identically zero but whose mean is zero.
  73. x = np.array([10, -3, 1, -4, -4])
  74. y = variation(x)
  75. assert_equal(y, np.inf)
  76. x2 = np.array([x, -10*x])
  77. y2 = variation(x2, axis=1)
  78. assert_equal(y2, [np.inf, np.inf])
  79. @pytest.mark.parametrize('x', [np.zeros(5), [], [1, 2, np.inf, 9]])
  80. def test_return_nan(self, x):
  81. # Test some cases where `variation` returns nan.
  82. y = variation(x)
  83. assert_equal(y, np.nan)
  84. @pytest.mark.parametrize('axis, expected',
  85. [(0, []), (1, [np.nan]*3), (None, np.nan)])
  86. def test_2d_size_zero_with_axis(self, axis, expected):
  87. x = np.empty((3, 0))
  88. y = variation(x, axis=axis)
  89. assert_equal(y, expected)
  90. def test_neg_inf(self):
  91. # Edge case that produces -inf: ddof equals the number of non-nan
  92. # values, the values are not constant, and the mean is negative.
  93. x1 = np.array([-3, -5])
  94. assert_equal(variation(x1, ddof=2), -np.inf)
  95. x2 = np.array([[np.nan, 1, -10, np.nan],
  96. [-20, -3, np.nan, np.nan]])
  97. assert_equal(variation(x2, axis=1, ddof=2, nan_policy='omit'),
  98. [-np.inf, -np.inf])
  99. @pytest.mark.parametrize("nan_policy", ['propagate', 'omit'])
  100. def test_combined_edge_cases(self, nan_policy):
  101. x = np.array([[0, 10, np.nan, 1],
  102. [0, -5, np.nan, 2],
  103. [0, -5, np.nan, 3]])
  104. y = variation(x, axis=0, nan_policy=nan_policy)
  105. assert_allclose(y, [np.nan, np.inf, np.nan, np.sqrt(2/3)/2])
  106. @pytest.mark.parametrize(
  107. 'ddof, expected',
  108. [(0, [np.sqrt(1/6), np.sqrt(5/8), np.inf, 0, np.nan, 0.0, np.nan]),
  109. (1, [0.5, np.sqrt(5/6), np.inf, 0, np.nan, 0, np.nan]),
  110. (2, [np.sqrt(0.5), np.sqrt(5/4), np.inf, np.nan, np.nan, 0, np.nan])]
  111. )
  112. def test_more_nan_policy_omit_tests(self, ddof, expected):
  113. # The slightly strange formatting in the follow array is my attempt to
  114. # maintain a clean tabular arrangement of the data while satisfying
  115. # the demands of pycodestyle. Currently, E201 and E241 are not
  116. # disabled by the `# noqa` annotation.
  117. nan = np.nan
  118. x = np.array([[1.0, 2.0, nan, 3.0],
  119. [0.0, 4.0, 3.0, 1.0],
  120. [nan, -.5, 0.5, nan],
  121. [nan, 9.0, 9.0, nan],
  122. [nan, nan, nan, nan],
  123. [3.0, 3.0, 3.0, 3.0],
  124. [0.0, 0.0, 0.0, 0.0]])
  125. v = variation(x, axis=1, ddof=ddof, nan_policy='omit')
  126. assert_allclose(v, expected)
  127. def test_variation_ddof(self):
  128. # test variation with delta degrees of freedom
  129. # regression test for gh-13341
  130. a = np.array([1, 2, 3, 4, 5])
  131. nan_a = np.array([1, 2, 3, np.nan, 4, 5, np.nan])
  132. y = variation(a, ddof=1)
  133. nan_y = variation(nan_a, nan_policy="omit", ddof=1)
  134. assert_allclose(y, np.sqrt(5/2)/3)
  135. assert y == nan_y