| 90 | | What is a good approximation to the inverse of the cumulative normal |
|---|
| 91 | | distribution function? |
|---|
| 92 | | |
|---|
| 93 | | Mathematically we can write this as: Find X such that Q(X) = p for |
|---|
| 94 | | any 0 < p < 1. (Note: this computes an upper tail probability.) |
|---|
| 95 | | |
|---|
| 96 | | Again, it is not possible to write this as a closed form expression, |
|---|
| 97 | | so we resort to approximations. Because of the symmetry of the normal |
|---|
| 98 | | distribution, we need only consider 0 < p < 0.5. If you have p > 0.5, |
|---|
| 99 | | then apply the algorithm below to q = 1-p, and then negate the value |
|---|
| 100 | | for X obtained. (This approximation is also from Abramowitz and |
|---|
| 101 | | Stegun.) |
|---|
| 102 | | |
|---|
| 103 | | t = sqrt[ ln(1/p^2) ] |
|---|
| 104 | | |
|---|
| 105 | | c_0 + c_1*t + c_2*t^2 |
|---|
| 106 | | X = t - ------------------------------ |
|---|
| 107 | | 1 + d_1*t + d_2*t^2 + d_3*t^3 |
|---|
| 108 | | |
|---|
| 109 | | c_0 = 2.515517 |
|---|
| 110 | | c_1 = 0.802853 |
|---|
| 111 | | c_2 = 0.010328 |
|---|
| 112 | | |
|---|
| 113 | | d_1 = 1.432788 |
|---|
| 114 | | d_2 = 0.189269 |
|---|
| 115 | | d_3 = 0.001308 |
|---|
| 116 | | |
|---|
| 117 | | See Abramowitz and Stegun; Press, et al. |
|---|
| | 90 | I run an array of values through a random walk within a zero-mean, unit |
|---|
| | 91 | variance normal distribution. Instantiate me with the dimensions of the |
|---|
| | 92 | array as one or more arguments. |
|---|
| 126 | | # TODO: Write C-weave code to do this lightning-fast with the above |
|---|
| 127 | | # approximation. |
|---|
| 128 | | if not hasattr(self, 'distObj'): |
|---|
| 129 | | self.distObj = dists.norm() |
|---|
| | 106 | # TODO: Write C-weave code to do this lightning-fast with the following |
|---|
| | 107 | # approximation. Because of the symmetry of the normal distribution, we |
|---|
| | 108 | # need only consider 0 < p < 0.5. If you have p > 0.5, then apply the |
|---|
| | 109 | # algorithm below to q = 1-p, and then negate the value for X obtained. |
|---|
| | 110 | # |
|---|
| | 111 | # t = sqrt[ ln(1/p^2) ] |
|---|
| | 112 | # |
|---|
| | 113 | # c_0 + c_1*t + c_2*t^2 |
|---|
| | 114 | # X = t - ------------------------------ |
|---|
| | 115 | # 1 + d_1*t + d_2*t^2 + d_3*t^3 |
|---|
| | 116 | # |
|---|
| | 117 | # c_0 = 2.515517 |
|---|
| | 118 | # c_1 = 0.802853 |
|---|
| | 119 | # c_2 = 0.010328 |
|---|
| | 120 | # |
|---|
| | 121 | # d_1 = 1.432788 |
|---|
| | 122 | # d_2 = 0.189269 |
|---|
| | 123 | # d_3 = 0.001308 |
|---|
| | 124 | # |
|---|
| | 125 | # See Abramowitz and Stegun; Press, et al. |
|---|
| 134 | | For each current random variate value in I{x}, returns the transition |
|---|
| 135 | | kernel... |
|---|
| 136 | | """ |
|---|
| | 138 | if s.shape(W) != self._dims: |
|---|
| | 139 | raise ValueError( |
|---|
| | 140 | "Wiggle array must have same dimensions as walker array") |
|---|
| | 141 | # TODO: Make work with 2-D array |
|---|
| | 142 | P = s.empty_like(W) |
|---|
| | 143 | for k, dx in enumerate(0.5*W): |
|---|
| | 144 | distObj = dists.truncnorm(self.X[k]-dx, self.X[k]+dx) |
|---|
| | 145 | self.X[k] = distObj.rvs(1)[0] |
|---|
| | 146 | P[k] = distObj.pdf(self.X[k]) |
|---|
| | 147 | return self.X, P |
|---|
| | 148 | |
|---|
| | 149 | |
|---|
| | 150 | |
|---|