Fast arbitrary distribution random sampling (inverse transform sampling)

前端 未结 5 623
名媛妹妹
名媛妹妹 2020-11-28 07:00

The random module (http://docs.python.org/2/library/random.html) has several fixed functions to randomly sample from. For example random.gauss

5条回答
  •  夕颜
    夕颜 (楼主)
    2020-11-28 07:33

    Here is a rather nice way of performing inverse transform sampling with a decorator.

    import numpy as np
    from scipy.interpolate import interp1d
    
    def inverse_sample_decorator(dist):
        
        def wrapper(pnts, x_min=-100, x_max=100, n=1e5, **kwargs):
            
            x = np.linspace(x_min, x_max, int(n))
            cumulative = np.cumsum(dist(x, **kwargs))
            cumulative -= cumulative.min()
            f = interp1d(cumulative/cumulative.max(), x)
            return f(np.random.random(pnts))
        
        return wrapper
    

    Using this decorator on a Gaussian distribution, for example:

    @inverse_sample_decorator
    def gauss(x, amp=1.0, mean=0.0, std=0.2):
        return amp*np.exp(-(x-mean)**2/std**2/2.0)
    

    You can then generate sample points from the distribution by calling the function. The keyword arguments x_min and x_max are the limits of the original distribution and can be passed as arguments to gauss along with the other key word arguments that parameterise the distribution.

    samples = gauss(5000, mean=20, std=0.8, x_min=19, x_max=21)
    

    Alternatively, this can be done as a function that takes the distribution as an argument (as in your original question),

    def inverse_sample_function(dist, pnts, x_min=-100, x_max=100, n=1e5, 
                                **kwargs):
            
        x = np.linspace(x_min, x_max, int(n))
        cumulative = np.cumsum(dist(x, **kwargs))
        cumulative -= cumulative.min()
        f = interp1d(cumulative/cumulative.max(), x)
            
        return f(np.random.random(pnts))
    

提交回复
热议问题