Estoy tratando de crear una distribución basada en algunos datos que tengo, luego dibujo al azar de esa distribución. Aquí es lo que tengo:Creando nuevas distribuciones en scipy
from scipy import stats
import numpy
def getDistribution(data):
kernel = stats.gaussian_kde(data)
class rv(stats.rv_continuous):
def _cdf(self, x):
return kernel.integrate_box_1d(-numpy.Inf, x)
return rv()
if __name__ == "__main__":
# pretend this is real data
data = numpy.concatenate((numpy.random.normal(2,5,100), numpy.random.normal(25,5,100)))
d = getDistribution(data)
print d.rvs(size=100) # this usually fails
creo que esto es hacer lo que yo quiero que, con frecuencia, pero me sale un error (véase más adelante) cuando intento hacer d.rvs()
, y d.rvs(100)
nunca funciona. ¿Estoy haciendo algo mal? ¿Hay una manera más fácil o mejor de hacer esto? Si es un error en scipy, ¿hay alguna forma de evitarlo?
Finalmente, ¿hay más documentación sobre cómo crear distribuciones personalizadas en alguna parte? Lo mejor que he encontrado es la documentación scipy.stats.rv_continuous, que es bastante espartana y no contiene ejemplos útiles.
El rastreo:
Traceback (most recent call last): File "testDistributions.py", line 19, in print d.rvs(size=100) File "/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py", line 696, in rvs vals = self._rvs(*args) File "/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py", line 1193, in _rvs Y = self._ppf(U,*args) File "/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py", line 1212, in _ppf return self.vecfunc(q,*args) File "/usr/local/lib/python2.6/dist-packages/numpy-1.6.1-py2.6-linux-x86_64.egg/numpy/lib/function_base.py", line 1862, in call theout = self.thefunc(*newargs) File "/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/stats/distributions.py", line 1158, in _ppf_single_call return optimize.brentq(self._ppf_to_solve, self.xa, self.xb, args=(q,)+args, xtol=self.xtol) File "/usr/local/lib/python2.6/dist-packages/scipy-0.10.0-py2.6-linux-x86_64.egg/scipy/optimize/zeros.py", line 366, in brentq r = _zeros._brentq(f,a,b,xtol,maxiter,args,full_output,disp) ValueError: f(a) and f(b) must have different signs
Editar
Para los curiosos, siguiendo el consejo de la respuesta a continuación, aquí está el código que funciona:
from scipy import stats
import numpy
def getDistribution(data):
kernel = stats.gaussian_kde(data)
class rv(stats.rv_continuous):
def _rvs(self, *x, **y):
# don't ask me why it's using self._size
# nor why I have to cast to int
return kernel.resample(int(self._size))
def _cdf(self, x):
return kernel.integrate_box_1d(-numpy.Inf, x)
def _pdf(self, x):
return kernel.evaluate(x)
return rv(name='kdedist', xa=-200, xb=200)
Entonces, cuando estamos haciendo lo anterior y llamamos 'randoms = getDistribution (Mydata)' y luego 'randoms = randoms.rvs (size = 1000)' ¿realiza los tres 'def' dentro de la clase? es decir, calcular el pdf, integrarlo, etc. – ThePredator
obtengo mis randoms para seguir la distribución de datos, pero me gustaría suavizarlo para que no siga exactamente la distribución de datos. He estado ajustando manualmente el ancho de banda en 'kernel' para hacer eso. Por ejemplo, algo así como cómo especificamos una función PDF y luego usamos la función PDF para crear randoms usando Metropolis Hastings. – ThePredator