I'm trying to model a signal detection problem using Sympy, and need two random variables. One with a Rayleigh distribution to model noise, and one with a Rician distribution to model signal+noise. Sympy provides a Rayleigh distribution, but not a Rician-- or at least not one by that name.
What's the best way of creating one? Does it exist under a different name? Is there a way to manipulate existing distributions into a Rician?
Following advice from @asmeurer, I've implemented my own Rice distribution, like so:
from sympy.stats.crv_types import rv
from sympy.stats.crv import SingleContinuousDistribution
class RicianDistribution(SingleContinuousDistribution):
_argnames=('nu','sigma')
@property
def set(self): return Interval(0,oo)
def pdf(self,x):
nu,sigma=self.nu, self.sigma
return (x/sigma**2)*exp(-(x**2+nu**2)/(2*sigma**2))*besseli(0,x*nu/sigma**2)
def Rician(name,nu,sigma):
return rv(name,RicianDistribution,(nu,sigma))
The distribution seems to match both Wikipedia and Scipy, but oddly I'm getting different results than Scipy. I'll ask that question separately (asked and answered).
As a side note, the following line makes it possible to lambdify the density function, which includes a Bessel function:
printing.lambdarepr.LambdaPrinter._print_besseli=(lambda self,expr: 'i0(%s)'%expr.argument)
It's not generalized to all Bessel functions, but works for the zero order modified Bessel of the first kind used in the Rician distribution.
If you know the pdf function, it's easy to create a new distribution with sympy.stats. Take a look at the existing distributions in the sympy source. You just need to subclass SingleContinuousDistribution and define some methods. For example, here is the normal distribution (with the docstrings removed):
class NormalDistribution(SingleContinuousDistribution):
_argnames = ('mean', 'std')
@staticmethod
def check(mean, std):
_value_check(std > 0, "Standard deviation must be positive")
def pdf(self, x):
return exp(-(x - self.mean)**2 / (2*self.std**2)) / (sqrt(2*pi)*self.std)
def sample(self):
return random.normalvariate(self.mean, self.std)
def Normal(name, mean, std):
return rv(name, NormalDistribution, (mean, std))
Yes, you can generate the Rice from chi-squared and Poisson. See any thorough Rice discussion, such as https://en.wikipedia.org/wiki/Rice_distribution:
Another case where Rice(nu,sigma) comes from the following steps:
- Generate P having a Poisson distribution with parameter (also mean, for a Poisson) lambda = nu^2 / (2*sigma^2).
- Generate X having a chi-squared distribution with 2P + 2 degrees of freedom.
- Set R = sigma * sqrt(X).
来源:https://stackoverflow.com/questions/32981363/how-to-create-a-rician-random-variable