├── .gitignore ├── test_plots.py ├── kde.py ├── dgp_density.py └── dgp_class.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pdf 2 | *.pyc 3 | *~ -------------------------------------------------------------------------------- /test_plots.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python -tt 2 | 3 | from __future__ import division 4 | 5 | import sys 6 | import inspect 7 | import numpy as np 8 | import matplotlib.pyplot as plt 9 | import kde 10 | import dgp_class as dgp 11 | 12 | def main(Nsamp=None, Nmesh=None): 13 | """ 14 | Generates plots for the 16 test cases for both the analytical pdf and the 15 | kernel density estimate. 16 | 17 | Parameters 18 | ---------- 19 | Nsamp: int 20 | Number of samples used for the kde 21 | Nmesh: int 22 | Number of points used for the mesh 23 | """ 24 | if Nsamp is None: 25 | Nsamp = 10000 26 | classes = [(cls, name) for name, cls in inspect.getmembers(dgp) 27 | if inspect.isclass(cls) and not cls in (dgp.dgp, dgp.LogNormal)] 28 | for cls, name in classes: 29 | print 'Generating graph for', name 30 | model = cls() 31 | x = model.sample(size=Nsamp) 32 | t, mesh, kdense = kde.kde(x, N=Nmesh) 33 | f = model.pdf(mesh) 34 | 35 | fig = plt.figure() 36 | ax = fig.add_subplot(111) 37 | ax.set_title(name, size=36) 38 | 39 | plt.plot(mesh, kdense) 40 | plt.plot(mesh, f) 41 | for label in ax.get_xticklabels() + ax.get_yticklabels(): 42 | label.set_fontsize(24) 43 | 44 | fig.set_figheight(10) 45 | fig.set_figwidth(12) 46 | fig.savefig(name+'.pdf') 47 | plt.close() 48 | 49 | 50 | return None 51 | 52 | if __name__ == "__main__": 53 | answer = raw_input('Warning: This script generates 16 pdfs\n' + 54 | 'Do you wish to continue? (Y/N)') 55 | if answer in ('Y', 'y', 'yes', 'Yes', 'YES'): 56 | main() 57 | sys.exit(0) 58 | else: 59 | sys.exit(1) 60 | 61 | -------------------------------------------------------------------------------- /kde.py: -------------------------------------------------------------------------------- 1 | """ 2 | An implementation of the kde bandwidth selection method outlined in: 3 | 4 | Z. I. Botev, J. F. Grotowski, and D. P. Kroese. Kernel density 5 | estimation via diffusion. The Annals of Statistics, 38(5):2916-2957, 2010. 6 | 7 | Based on the implementation in Matlab by Zdravko Botev. 8 | 9 | Daniel B. Smith, PhD 10 | Updated 1-23-2013 11 | """ 12 | 13 | from __future__ import division 14 | 15 | import scipy as sci 16 | import scipy.optimize 17 | import scipy.fftpack 18 | 19 | def kde(data, N=None, MIN=None, MAX=None): 20 | 21 | # Parameters to set up the mesh on which to calculate 22 | N = 2**14 if N is None else int(2**sci.ceil(sci.log2(N))) 23 | if MIN is None or MAX is None: 24 | minimum = min(data) 25 | maximum = max(data) 26 | Range = maximum - minimum 27 | MIN = minimum - Range/10 if MIN is None else MIN 28 | MAX = maximum + Range/10 if MAX is None else MAX 29 | 30 | # Range of the data 31 | R = MAX-MIN 32 | 33 | # Histogram the data to get a crude first approximation of the density 34 | M = len(data) 35 | DataHist, bins = sci.histogram(data, bins=N, range=(MIN,MAX)) 36 | DataHist = DataHist/M 37 | DCTData = scipy.fftpack.dct(DataHist, norm=None) 38 | 39 | I = [iN*iN for iN in xrange(1, N)] 40 | SqDCTData = (DCTData[1:]/2)**2 41 | 42 | # The fixed point calculation finds the bandwidth = t_star 43 | guess = 0.1 44 | try: 45 | t_star = scipy.optimize.brentq(fixed_point, 0, guess, 46 | args=(M, I, SqDCTData)) 47 | except ValueError: 48 | print 'Oops!' 49 | return None 50 | 51 | # Smooth the DCTransformed data using t_star 52 | SmDCTData = DCTData*sci.exp(-sci.arange(N)**2*sci.pi**2*t_star/2) 53 | # Inverse DCT to get density 54 | density = scipy.fftpack.idct(SmDCTData, norm=None)*N/R 55 | mesh = [(bins[i]+bins[i+1])/2 for i in xrange(N)] 56 | bandwidth = sci.sqrt(t_star)*R 57 | 58 | density = density/sci.trapz(density, mesh) 59 | return bandwidth, mesh, density 60 | 61 | def fixed_point(t, M, I, a2): 62 | l=7 63 | I = sci.float128(I) 64 | M = sci.float128(M) 65 | a2 = sci.float128(a2) 66 | f = 2*sci.pi**(2*l)*sci.sum(I**l*a2*sci.exp(-I*sci.pi**2*t)) 67 | for s in range(l, 1, -1): 68 | K0 = sci.prod(xrange(1, 2*s, 2))/sci.sqrt(2*sci.pi) 69 | const = (1 + (1/2)**(s + 1/2))/3 70 | time=(2*const*K0/M/f)**(2/(3+2*s)) 71 | f=2*sci.pi**(2*s)*sci.sum(I**s*a2*sci.exp(-I*sci.pi**2*time)) 72 | return t-(2*M*sci.sqrt(sci.pi)*f)**(-2/5) 73 | -------------------------------------------------------------------------------- /dgp_density.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file creates the data generating functions necessary to reproduce Table 1 3 | from: 4 | 5 | Z. I. Botev, J. F. Grotowski, and D. P. Kroese. Kernel density 6 | estimation via diffusion. The Annals of Statistics, 38(5):2916-2957, 2010. 7 | 8 | Daniel B. Smith, PhD 9 | 1-29-2013 10 | """ 11 | 12 | from __future__ import division 13 | 14 | import numpy.random as rand 15 | from random import shuffle 16 | 17 | _doc = """Generates according to a {dist} distribution 18 | 19 | Parameters: 20 | ---------- 21 | nsamp: int 22 | Number of samples to generate 23 | 24 | {eq} 25 | """ 26 | 27 | def nsamp_opt(func): 28 | def inner(nsamp=None): 29 | if nsamp is None: 30 | nsamp = 1 31 | return func(nsamp) 32 | return inner 33 | 34 | def _generate(inputs, counts): 35 | """ 36 | Generates random samples from a sum of normals based on inputs, counts 37 | 38 | Parameters 39 | ---------- 40 | inputs : list of (mean, standard deviation) tuples 41 | counts : list of number of samples to draw from each normal defined in 42 | inputs 43 | """ 44 | out = [] 45 | for iC, count in enumerate(counts): 46 | out.extend(inputs[iC][1]*rand.randn(count)+inputs[iC][0]) 47 | shuffle(out) 48 | return out 49 | 50 | @nsamp_opt 51 | def claw(nsamp): 52 | inputs = [(0, 1)] 53 | for k in xrange(5): 54 | inputs.append((k/2-1, 1/10)) 55 | counts = rand.multinomial(nsamp, [1/2]+[1/10]*5, size=1)[0] 56 | return _generate(inputs, counts) 57 | eq = ' 1/2*N(0,1) + sum_{k=0}^4 1/10*N(k/2-1, (1/10)^2)' 58 | claw.__doc__ = _doc.format(dist='claw', eq=eq) 59 | 60 | @nsamp_opt 61 | def strongly_skewed(nsamp): 62 | inputs = [] 63 | for k in xrange(8): 64 | inputs.append((3*((2/3)**k-1), (2/3)**k)) 65 | counts = rand.multinomial(nsamp, [1/8]*8, size=1)[0] 66 | return _generate(inputs, counts) 67 | eq = 'sum_{k=0}^7 1/8*N(3*((2/3)^k-1), (2/3)^(2k))' 68 | strongly_skewed.__doc__ = _doc.format(dist='strongly skewed', eq=eq) 69 | 70 | @nsamp_opt 71 | def kurtotic_unimodal(nsamp): 72 | inputs = [(0, 1), (0, 1/10)] 73 | counts = rand.multinomial(nsamp, [2/3, 1/3], size=1)[0] 74 | return _generate(inputs, counts) 75 | kurtotic_unimodal.__doc__ = _doc.format(dist='kurtotic unimodal', 76 | eq='2/3*N(0,1) + 1/3*N(0,(1/10)^2)') 77 | 78 | @nsamp_opt 79 | def double_claw(nsamp): 80 | inputs = [(-1, 2/3), (1, 2/3)] 81 | for k in xrange(7): 82 | inputs.append(((k-3)/2, 1/100)) 83 | counts = rand.multinomial(nsamp, [49/100]*2+[1/350]*7, size=1)[0] 84 | return _generate(inputs, counts) 85 | eq = ('49/100*N(-1, (2/3)^2) + 49/100*N(1, (2/3)^2) + \n' + 86 | ' sum_{k=0}^6 1/350*N((k-3)/2, (1/100)^2)') 87 | double_claw.__doc__ = _doc.format(dist='double claw', eq=eq) 88 | 89 | @nsamp_opt 90 | def discrete_comb(nsamp): 91 | inputs = [] 92 | for k in xrange(3): 93 | inputs.append(((2*k-15)/7, 2/7)) 94 | for k in xrange(8, 11): 95 | inputs.append((2*k/7, 1/21)) 96 | counts = rand.multinomial(nsamp, [2/7]*3+[1/21]*3, size=1)[0] 97 | return _generate(inputs, counts) 98 | eq = ('2/7*sum_{k=0}^2 N((12*k-15/7), (2/7)^2) + \n' + 99 | ' 1/21*sum_{k=8}^10 N(2*k/7, (1/21)^2)') 100 | discrete_comb.__doc__ = _doc.format(dist='discrete comb', eq=eq) 101 | 102 | @nsamp_opt 103 | def asym_double_claw(nsamp): 104 | inputs = [] 105 | for k in xrange(2): 106 | inputs.append((2*k-1, 2/3)) 107 | for k in xrange(1, 4): 108 | inputs.append((-k/2, 1/100)) 109 | for k in xrange(1, 4): 110 | inputs.append((k/2, 7/100)) 111 | counts = rand.multinomial(nsamp, [46/100]*2+[1/300]*3+[7/300]*3, size=1)[0] 112 | return _generate(inputs, counts) 113 | eq = ('46/100*sum_{k=0}^1 N(2*k-1, (2/3)^2) + 1/300*sum_{k=1}^3 ' + 114 | 'N(-k/2, (1/100)^2)\n sum_{k=1}^3 N(k/2, (7/100)^2)') 115 | asym_double_claw.__doc__ = _doc.format(dist='asymmetric double claw', eq=eq) 116 | 117 | @nsamp_opt 118 | def outlier(nsamp): 119 | inputs = [(0, 1), (0, 1/10)] 120 | counts = rand.multinomial(nsamp, [1/10, 9/10], size=1)[0] 121 | return _generate(inputs, counts) 122 | outlier.__doc__ = _doc.format(dist='outlier', 123 | eq='1/10*N(0, 1)+9/10*N(0, (1/10)^2)') 124 | 125 | @nsamp_opt 126 | def sep_bimodal(nsamp): 127 | inputs = [(-12, 1/2), (12, 1/2)] 128 | counts = rand.multinomial(nsamp, [1/2, 1/2], size=1)[0] 129 | return _generate(inputs, counts) 130 | eq = '1/2*N(-12, (1/2)^2) + 1/2*N(12, (1/2)^2)' 131 | sep_bimodal.__doc__ = _doc.format(dist='separated bimodal', eq=eq) 132 | 133 | 134 | @nsamp_opt 135 | def skew_bimodal(nsamp): 136 | inputs = [(0,1), (3/2, 1/3)] 137 | counts = rand.multinomial(nsamp, [3/4, 1/4], size=1)[0] 138 | return _generate(inputs, counts) 139 | eq = '3/4*N(0, 1) + 1/4*N(3/2, (1/3)^2)' 140 | skew_bimodal.__doc__ = _doc.format(dist='skewed bimodal', eq=eq) 141 | 142 | @nsamp_opt 143 | def bimodal(nsamp): 144 | inputs = [(0, 1/10), (5, 1)] 145 | counts = rand.multinomial(nsamp, [1/2, 1/2], size=1)[0] 146 | return _generate(inputs, counts) 147 | eq = '1/2*N(0, (1/10)^2) + 1/2*N(5, 1)' 148 | bimodal.__doc__ = _doc.format(dist='bimodal', eq=eq) 149 | 150 | @nsamp_opt 151 | def log_normal(nsamp): 152 | return rand.lognormal(size=nsamp) 153 | eq = 'Wrapper for Numpy\'s log normal random generator' 154 | log_normal.__doc__ = _doc.format(dist='log normal', eq=eq) 155 | 156 | @nsamp_opt 157 | def asym_claw(nsamp): 158 | inputs = [(0, 1)] 159 | rates = [1/2] 160 | for k in xrange(-2, 3): 161 | inputs.append((k+1/2, 2**(-k)/10)) 162 | rates.append(2**(1-k)/31) 163 | counts = rand.multinomial(nsamp, rates, size=1)[0] 164 | return _generate(inputs, counts) 165 | eq = '1/2*N(0, 1) + sum_{k=-2}^2 2**(1-k)/31*N(k+1/2, (2**-k/10)^2)' 166 | asym_claw.__doc__ = _doc.format(dist='asymmetric claw', eq=eq) 167 | 168 | @nsamp_opt 169 | def trimodal(nsamp): 170 | inputs = [] 171 | for k in xrange(3): 172 | inputs.append((80*k, (k+1)^2)) 173 | counts = rand.multinomial(nsamp, [1/3]*3, size=1)[0] 174 | return _generate(inputs, counts) 175 | trimodal.__doc__ = _doc.format(dist='trimodal', 176 | eq='1/3*sum_{k=0}^2 N(80*k, (k+1)^4)') 177 | 178 | @nsamp_opt 179 | def five_modes(nsamp): 180 | inputs = [] 181 | for k in xrange(5): 182 | inputs.append((80*k, (k+1)^2)) 183 | counts = rand.multinomial(nsamp, [1/5]*5, size=1)[0] 184 | return _generate(inputs, counts) 185 | five_modes.__doc__ = _doc.format(dist='five modal', 186 | eq='1/5*sum_{k=0}^4 N(80*k, (k+1)^4)') 187 | 188 | @nsamp_opt 189 | def ten_modes(nsamp): 190 | inputs = [] 191 | for k in xrange(10): 192 | inputs.append((100*k, (k+1)^2)) 193 | counts = rand.multinomial(nsamp, [1/10]*10, size=1)[0] 194 | return _generate(inputs, counts) 195 | ten_modes.__doc__ = _doc.format(dist='ten modal', 196 | eq='1/10*sum_{k=0}^9 N(100*k, (k+1)^4)') 197 | 198 | @nsamp_opt 199 | def smooth_comb(nsamp): 200 | inputs = [] 201 | rates = [] 202 | for k in xrange(6): 203 | inputs.append(((65-96)*2**-k/21, 32/63*2**(-2*k))) 204 | rates.append(2**(5-k)/63) 205 | counts = rand.multinomial(nsamp, rates, size=1)[0] 206 | return _generate(inputs, counts) 207 | eq = ('sum_{k=0}^5 2**(5-k)/63*N((65-96)*2**-k/21, (32/63*2**(-2*k))^2)' 208 | smooth_comb.__doc__ = _doc.format(dist='smooth comb', eq=eq) 209 | -------------------------------------------------------------------------------- /dgp_class.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file creates the data generating functions necessary to reproduce Table 1 3 | from: 4 | 5 | Z. I. Botev, J. F. Grotowski, and D. P. Kroese. Kernel density 6 | estimation via diffusion. The Annals of Statistics, 38(5):2916-2957, 2010. 7 | 8 | Daniel B. Smith, PhD 9 | 1-29-2013 10 | """ 11 | 12 | from __future__ import division 13 | 14 | import numpy as np 15 | from random import shuffle 16 | 17 | rand = np.random 18 | 19 | _samp_doc = """Generates random numbers according to a {dist} distribution 20 | 21 | Parameters: 22 | ---------- 23 | size: d1, ..., dn : `n` ints, optional 24 | The dimensions of the returned array, should be all positive. 25 | 26 | {eq} 27 | """ 28 | 29 | _pdf_doc = """Calculated probability distribution function according to a 30 | {dist} distribution. 31 | 32 | {eq} 33 | """ 34 | 35 | _class_doc = """{class_} class to generate data generating function objects. 36 | Each object has two methods: 37 | 38 | dgp.sample(size=1): generates array of samples according to shape defined 39 | in size. 40 | dgp.pdf(mesh): calculates pdf on the given mesh 41 | 42 | Probability distribution function, N(mu, sigma^2) normal: 43 | {eq} 44 | """ 45 | 46 | # Adapted from scipy: 47 | _NORM_PDF_C = np.sqrt(2*np.pi) 48 | def _norm(x, params): 49 | x = np.asarray(x) 50 | return np.exp(-(x-params[0])**2/2.0/params[1]**2) / _NORM_PDF_C / params[1] 51 | 52 | def _generate(inputs, counts): 53 | """ 54 | Generates random samples from a sum of normals based on inputs, counts 55 | 56 | Parameters 57 | ---------- 58 | inputs : list of (mean, standard deviation) tuples 59 | counts : list of number of samples to draw from each normal defined in 60 | inputs 61 | """ 62 | out = [] 63 | for iC, count in enumerate(counts): 64 | out.extend(inputs[iC][1]*rand.randn(count)+inputs[iC][0]) 65 | shuffle(out) 66 | return out 67 | 68 | class dgp(object): 69 | __doc__ = _class_doc.format(class_='Default', eq='N/A') 70 | def __str__(self): 71 | # Print first line of documentation 72 | return self.__doc__.split('\n')[0] 73 | def sample(self, size=1): 74 | nsamp = np.prod(size) 75 | out = self._sample(nsamp) 76 | return np.resize(out, size) 77 | def pdf(self, mesh): 78 | return self._pdf(mesh) 79 | def _pdf(self, mesh): 80 | return sum((self._rates[k]*_norm(mesh, input_) for k, input_ in 81 | enumerate(self._inputs))) 82 | def _sample(self, nsamp): 83 | counts = rand.multinomial(nsamp, self._rates, size=1)[0] 84 | return _generate(self._inputs, counts) 85 | def mesh(self, N=None): 86 | """ 87 | Generates default mesh 88 | """ 89 | if N is None: 90 | N = 2**14 91 | maxes = [input_[0]+4*input_[1] for input_ in self._inputs] 92 | mins = [input_[0]-4*input_[1] for input_ in self._inputs] 93 | min_ = min(mins) 94 | max_ = max(maxes) 95 | return np.linspace(min_, max_, num=N) 96 | _inputs = [] 97 | _rates = [] 98 | 99 | class Claw(dgp): 100 | def __init__(self): 101 | dgp.__init__(self) 102 | eq = ' 1/2*N(0,1) + sum_{k=0}^4 1/10*N(k/2-1, (1/10)^2)' 103 | self.sample.__func__.__doc__ = _samp_doc.format(dist='claw', eq=eq) 104 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='claw', eq=eq) 105 | self.__doc__ = _class_doc.format(class_='Claw', eq=eq) 106 | _inputs = [(0, 1)] 107 | for k in xrange(5): 108 | _inputs.append((k/2-1, 1/10)) 109 | _rates = [1/2] + [1/10]*5 110 | 111 | class StronglySkewed(dgp): 112 | def __init__(self): 113 | dgp.__init__(self) 114 | eq = 'sum_{k=0}^7 1/8*N(3*((2/3)^k-1), (2/3)^(2k))' 115 | self.sample.__func__.__doc__ = _samp_doc.format(dist='strongly skewed', 116 | eq=eq) 117 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='strongly skewed', 118 | eq=eq) 119 | self.__doc__ = _class_doc.format(class_='Strongly Skewed', eq=eq) 120 | for k in xrange(8): 121 | self._inputs.append((3*((2/3)**k-1), (2/3)**k)) 122 | _rates = [1/8]*8 123 | 124 | class KurtoticUnimodal(dgp): 125 | def __init__(self): 126 | dgp.__init__(self) 127 | eq = '2/3*N(0,1) + 1/3*N(0,(1/10)^2)' 128 | dist = 'kurtotic unimodal' 129 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 130 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 131 | self.__doc__ = _class_doc.format(class_='Kurtotic Unimodal', eq=eq) 132 | _inputs = [(0, 1), (0, 1/10)] 133 | _rates = [2/3, 1/3] 134 | 135 | class DoubleClaw(dgp): 136 | def __init__(self): 137 | dgp.__init__(self) 138 | eq = ('49/100*N(-1, (2/3)^2) + 49/100*N(1, (2/3)^2) + \n' + 139 | ' sum_{k=0}^6 1/350*N((k-3)/2, (1/100)^2)') 140 | self.sample.__func__.__doc__ = _samp_doc.format(dist='double claw', 141 | eq=eq) 142 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='double claw', eq=eq) 143 | self.__doc__ = _class_doc.format(class_='Double Claw', eq=eq) 144 | _inputs = [(-1, 2/3), (1, 2/3)] 145 | for k in xrange(7): 146 | _inputs.append(((k-3)/2, 1/100)) 147 | _rates = [49/100]*2+[1/350]*7 148 | 149 | class DiscreteComb(dgp): 150 | def __init__(self): 151 | dgp.__init__(self) 152 | eq = ('2/7*sum_{k=0}^2 N((12*k-15/7), (2/7)^2) + \n' + 153 | ' 1/21*sum_{k=8}^10 N(2*k/7, (1/21)^2)') 154 | self.sample.__func__.__doc__ = _samp_doc.format(dist='discrete comb', 155 | eq=eq) 156 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='discrete comb', 157 | eq=eq) 158 | self.__doc__ = _class_doc.format(class_='Discrete Comb', eq=eq) 159 | _inputs = [] 160 | for k in xrange(3): 161 | _inputs.append(((12*k-15)/7, 2/7)) 162 | for k in xrange(8, 11): 163 | _inputs.append((2*k/7, 1/21)) 164 | _rates = [2/7]*3 + [1/21]*3 165 | 166 | class AsymDoubleClaw(dgp): 167 | def __init__(self): 168 | dgp.__init__(self) 169 | eq = ('46/100*sum_{k=0}^1 N(2*k-1, (2/3)^2) + 1/300*sum_{k=1}^3 ' + 170 | 'N(-k/2, (1/100)^2)\n sum_{k=1}^3 N(k/2, (7/100)^2)') 171 | dist='asymmetric double claw' 172 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 173 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 174 | self.__doc__ = _class_doc.format(class_='Asymmetric Double Claw', eq=eq) 175 | _inputs = [] 176 | for k in xrange(2): 177 | _inputs.append((2*k-1, 2/3)) 178 | for k in xrange(1, 4): 179 | _inputs.append((-k/2, 1/100)) 180 | for k in xrange(1, 4): 181 | _inputs.append((k/2, 7/100)) 182 | _rates = [46/100]*2 + [1/300]*3 + [7/300]*3 183 | 184 | class Outlier(dgp): 185 | def __init__(self): 186 | dgp.__init__(self) 187 | eq = '1/10*N(0, 1)+9/10*N(0, (1/10)^2)' 188 | self.sample.__func__.__doc__ = _samp_doc.format(dist='outlier', eq=eq) 189 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='outlier', eq=eq) 190 | self.__doc__ = _class_doc.format(class_='Outlier', eq=eq) 191 | _inputs = [(0, 1), (0, 1/10)] 192 | _rates = [1/10, 9/10] 193 | 194 | class SeparatedBimodal(dgp): 195 | def __init__(self): 196 | dgp.__init__(self) 197 | eq = '1/2*N(-12, (1/2)^2) + 1/2*N(12, (1/2)^2)' 198 | dist = 'separated bimodal' 199 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 200 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 201 | self.__doc__ = _class_doc.format(class_='Separated Bimodal', eq=eq) 202 | _inputs = [(-12, 1/2), (12, 1/2)] 203 | _rates = [1/2]*2 204 | 205 | class SkewBimodal(dgp): 206 | def __init__(self): 207 | dgp.__init__(self) 208 | eq = '3/4*N(0, 1) + 1/4*N(3/2, (1/3)^2)' 209 | dist = 'skewed bimodal' 210 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 211 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 212 | self.__doc__ = _class_doc.format(class_='Skewed Bimodal', eq=eq) 213 | _inputs = [(0,1), (3/2, 1/3)] 214 | _rates = [3/4, 1/4] 215 | 216 | class Bimodal(dgp): 217 | def __init__(self): 218 | dgp.__init__(self) 219 | eq = '1/2*N(0, (1/10)^2) + 1/2*N(5, 1)' 220 | self.sample.__func__.__doc__ = _samp_doc.format(dist='bimodal', eq=eq) 221 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='bimodal', eq=eq) 222 | self.__doc__ = _class_doc.format(class_='Bimodal', eq=eq) 223 | _inputs = [(0, 1/10), (5, 1)] 224 | _rates = [1/2]*2 225 | 226 | class LogNormal(dgp): 227 | def __init__(self): 228 | dgp.__init__(self) 229 | eq = 'Wrapper for Numpy\'s log normal random generator' 230 | dist = 'log normal' 231 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 232 | self.__doc__ = _class_doc.format(class_='Log Normal', eq=eq) 233 | sample = rand.lognormal 234 | def _pdf(self, mesh): 235 | mesh = np.asarray(mesh) 236 | if (mesh<=0).any(): 237 | raise ValueError('mesh must be >0') 238 | return np.exp(-np.log(mesh)**2/2)/mesh/_NORM_PDF_C 239 | def mesh(self, N=None): 240 | if N is None: 241 | N = 2**14 242 | mesh, step = np.linspace(0, 10, num=N, retstep=True) 243 | mesh += step 244 | return mesh 245 | 246 | class AsymClaw(dgp): 247 | def __init__(self): 248 | dgp.__init__(self) 249 | eq = '1/2*N(0, 1) + sum_{k=-2}^2 2**(1-k)/31*N(k+1/2, (2**-k/10)^2)' 250 | dist = 'asymmetric claw' 251 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 252 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 253 | self.__doc__ = _class_doc.format(class_='Asymmetric Claw', eq=eq) 254 | _inputs = [(0, 1)] 255 | _rates = [1/2] 256 | for k in xrange(-2, 3): 257 | _inputs.append((k+1/2, 2**(-k)/10)) 258 | _rates.append(2**(1-k)/31) 259 | 260 | class Trimodal(dgp): 261 | def __init__(self): 262 | dgp.__init__(self) 263 | eq = '1/3*sum_{k=0}^2 N(80*k, (k+1)^4)' 264 | self.sample.__func__.__doc__ = _samp_doc.format(dist='trimodal', eq=eq) 265 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist='trimodal', eq=eq) 266 | self.__doc__ = _class_doc.format(class_='Trimodal', eq=eq) 267 | _inputs = [] 268 | for k in xrange(3): 269 | _inputs.append((80*k, (k+1)**2)) 270 | _rates = [1/3]*3 271 | 272 | class FiveModes(dgp): 273 | def __init__(self): 274 | dgp.__init__(self) 275 | eq = '1/5*sum_{k=0}^4 N(80*k, (k+1)^2)' 276 | dist = 'five modal' 277 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 278 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 279 | self.__doc__ = _class_doc.format(class_='Five Modal', eq=eq) 280 | _inputs = [] 281 | for k in xrange(5): 282 | _inputs.append((80*k, k+1)) 283 | _rates = [1/5]*5 284 | 285 | class TenModes(dgp): 286 | def __init__(self): 287 | dgp.__init__(self) 288 | eq = '1/10*sum_{k=0}^9 N(100*k, (k+1)^2)' 289 | dist = 'ten modal' 290 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 291 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 292 | self.__doc__ = _class_doc.format(class_='Ten Modal', eq=eq) 293 | _inputs = [] 294 | for k in xrange(10): 295 | _inputs.append((100*k, k+1)) 296 | _rates = [1/10]*10 297 | 298 | class SmoothComb(dgp): 299 | def __init__(self): 300 | dgp.__init__(self) 301 | eq = 'sum_{k=0}^5 2**(5-k)/63*N((65-96*2**-k)/21, (32/63*2**(-2*k))^2)' 302 | dist = 'smooth comb' 303 | self.sample.__func__.__doc__ = _samp_doc.format(dist=dist, eq=eq) 304 | self.pdf.__func__.__doc__ = _pdf_doc.format(dist=dist, eq=eq) 305 | self.__doc__ = _class_doc.format(class_='Ten Modal', eq=eq) 306 | _inputs = [] 307 | _rates = [] 308 | for k in xrange(6): 309 | _inputs.append(((65-96*2**-k)/21, 32/63*2**(-k))) 310 | _rates.append(2**(5-k)/63) 311 | --------------------------------------------------------------------------------