Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/scipy/stats/_tukeylambda_stats.py: 25%

53 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-12-12 06:31 +0000

1import numpy as np 

2from numpy import poly1d 

3from scipy.special import beta 

4 

5 

6# The following code was used to generate the Pade coefficients for the 

7# Tukey Lambda variance function. Version 0.17 of mpmath was used. 

8#--------------------------------------------------------------------------- 

9# import mpmath as mp 

10# 

11# mp.mp.dps = 60 

12# 

13# one = mp.mpf(1) 

14# two = mp.mpf(2) 

15# 

16# def mpvar(lam): 

17# if lam == 0: 

18# v = mp.pi**2 / three 

19# else: 

20# v = (two / lam**2) * (one / (one + two*lam) - 

21# mp.beta(lam + one, lam + one)) 

22# return v 

23# 

24# t = mp.taylor(mpvar, 0, 8) 

25# p, q = mp.pade(t, 4, 4) 

26# print("p =", [mp.fp.mpf(c) for c in p]) 

27# print("q =", [mp.fp.mpf(c) for c in q]) 

28#--------------------------------------------------------------------------- 

29 

30# Pade coefficients for the Tukey Lambda variance function. 

31_tukeylambda_var_pc = [3.289868133696453, 0.7306125098871127, 

32 -0.5370742306855439, 0.17292046290190008, 

33 -0.02371146284628187] 

34_tukeylambda_var_qc = [1.0, 3.683605511659861, 4.184152498888124, 

35 1.7660926747377275, 0.2643989311168465] 

36 

37# numpy.poly1d instances for the numerator and denominator of the 

38# Pade approximation to the Tukey Lambda variance. 

39_tukeylambda_var_p = poly1d(_tukeylambda_var_pc[::-1]) 

40_tukeylambda_var_q = poly1d(_tukeylambda_var_qc[::-1]) 

41 

42 

43def tukeylambda_variance(lam): 

44 """Variance of the Tukey Lambda distribution. 

45 

46 Parameters 

47 ---------- 

48 lam : array_like 

49 The lambda values at which to compute the variance. 

50 

51 Returns 

52 ------- 

53 v : ndarray 

54 The variance. For lam < -0.5, the variance is not defined, so 

55 np.nan is returned. For lam = 0.5, np.inf is returned. 

56 

57 Notes 

58 ----- 

59 In an interval around lambda=0, this function uses the [4,4] Pade 

60 approximation to compute the variance. Otherwise it uses the standard 

61 formula (https://en.wikipedia.org/wiki/Tukey_lambda_distribution). The 

62 Pade approximation is used because the standard formula has a removable 

63 discontinuity at lambda = 0, and does not produce accurate numerical 

64 results near lambda = 0. 

65 """ 

66 lam = np.asarray(lam) 

67 shp = lam.shape 

68 lam = np.atleast_1d(lam).astype(np.float64) 

69 

70 # For absolute values of lam less than threshold, use the Pade 

71 # approximation. 

72 threshold = 0.075 

73 

74 # Play games with masks to implement the conditional evaluation of 

75 # the distribution. 

76 # lambda < -0.5: var = nan 

77 low_mask = lam < -0.5 

78 # lambda == -0.5: var = inf 

79 neghalf_mask = lam == -0.5 

80 # abs(lambda) < threshold: use Pade approximation 

81 small_mask = np.abs(lam) < threshold 

82 # else the "regular" case: use the explicit formula. 

83 reg_mask = ~(low_mask | neghalf_mask | small_mask) 

84 

85 # Get the 'lam' values for the cases where they are needed. 

86 small = lam[small_mask] 

87 reg = lam[reg_mask] 

88 

89 # Compute the function for each case. 

90 v = np.empty_like(lam) 

91 v[low_mask] = np.nan 

92 v[neghalf_mask] = np.inf 

93 if small.size > 0: 

94 # Use the Pade approximation near lambda = 0. 

95 v[small_mask] = _tukeylambda_var_p(small) / _tukeylambda_var_q(small) 

96 if reg.size > 0: 

97 v[reg_mask] = (2.0 / reg**2) * (1.0 / (1.0 + 2 * reg) - 

98 beta(reg + 1, reg + 1)) 

99 v.shape = shp 

100 return v 

101 

102 

103# The following code was used to generate the Pade coefficients for the 

104# Tukey Lambda kurtosis function. Version 0.17 of mpmath was used. 

105#--------------------------------------------------------------------------- 

106# import mpmath as mp 

107# 

108# mp.mp.dps = 60 

109# 

110# one = mp.mpf(1) 

111# two = mp.mpf(2) 

112# three = mp.mpf(3) 

113# four = mp.mpf(4) 

114# 

115# def mpkurt(lam): 

116# if lam == 0: 

117# k = mp.mpf(6)/5 

118# else: 

119# numer = (one/(four*lam+one) - four*mp.beta(three*lam+one, lam+one) + 

120# three*mp.beta(two*lam+one, two*lam+one)) 

121# denom = two*(one/(two*lam+one) - mp.beta(lam+one,lam+one))**2 

122# k = numer / denom - three 

123# return k 

124# 

125# # There is a bug in mpmath 0.17: when we use the 'method' keyword of the 

126# # taylor function and we request a degree 9 Taylor polynomial, we actually 

127# # get degree 8. 

128# t = mp.taylor(mpkurt, 0, 9, method='quad', radius=0.01) 

129# t = [mp.chop(c, tol=1e-15) for c in t] 

130# p, q = mp.pade(t, 4, 4) 

131# print("p =", [mp.fp.mpf(c) for c in p]) 

132# print("q =", [mp.fp.mpf(c) for c in q]) 

133#--------------------------------------------------------------------------- 

134 

135# Pade coefficients for the Tukey Lambda kurtosis function. 

136_tukeylambda_kurt_pc = [1.2, -5.853465139719495, -22.653447381131077, 

137 0.20601184383406815, 4.59796302262789] 

138_tukeylambda_kurt_qc = [1.0, 7.171149192233599, 12.96663094361842, 

139 0.43075235247853005, -2.789746758009912] 

140 

141# numpy.poly1d instances for the numerator and denominator of the 

142# Pade approximation to the Tukey Lambda kurtosis. 

143_tukeylambda_kurt_p = poly1d(_tukeylambda_kurt_pc[::-1]) 

144_tukeylambda_kurt_q = poly1d(_tukeylambda_kurt_qc[::-1]) 

145 

146 

147def tukeylambda_kurtosis(lam): 

148 """Kurtosis of the Tukey Lambda distribution. 

149 

150 Parameters 

151 ---------- 

152 lam : array_like 

153 The lambda values at which to compute the variance. 

154 

155 Returns 

156 ------- 

157 v : ndarray 

158 The variance. For lam < -0.25, the variance is not defined, so 

159 np.nan is returned. For lam = 0.25, np.inf is returned. 

160 

161 """ 

162 lam = np.asarray(lam) 

163 shp = lam.shape 

164 lam = np.atleast_1d(lam).astype(np.float64) 

165 

166 # For absolute values of lam less than threshold, use the Pade 

167 # approximation. 

168 threshold = 0.055 

169 

170 # Use masks to implement the conditional evaluation of the kurtosis. 

171 # lambda < -0.25: kurtosis = nan 

172 low_mask = lam < -0.25 

173 # lambda == -0.25: kurtosis = inf 

174 negqrtr_mask = lam == -0.25 

175 # lambda near 0: use Pade approximation 

176 small_mask = np.abs(lam) < threshold 

177 # else the "regular" case: use the explicit formula. 

178 reg_mask = ~(low_mask | negqrtr_mask | small_mask) 

179 

180 # Get the 'lam' values for the cases where they are needed. 

181 small = lam[small_mask] 

182 reg = lam[reg_mask] 

183 

184 # Compute the function for each case. 

185 k = np.empty_like(lam) 

186 k[low_mask] = np.nan 

187 k[negqrtr_mask] = np.inf 

188 if small.size > 0: 

189 k[small_mask] = _tukeylambda_kurt_p(small) / _tukeylambda_kurt_q(small) 

190 if reg.size > 0: 

191 numer = (1.0 / (4 * reg + 1) - 4 * beta(3 * reg + 1, reg + 1) + 

192 3 * beta(2 * reg + 1, 2 * reg + 1)) 

193 denom = 2 * (1.0/(2 * reg + 1) - beta(reg + 1, reg + 1))**2 

194 k[reg_mask] = numer / denom - 3 

195 

196 # The return value will be a numpy array; resetting the shape ensures that 

197 # if `lam` was a scalar, the return value is a 0-d array. 

198 k.shape = shp 

199 return k