簡體   English   中英

np.random.choice 沒有產生預期的直方圖

[英]np.random.choice not producing expected histogram

我正在尋找生成 1 和 0 之間的random normally distributed數字,但隨着mean接近 1 或 0,右側或左側分別變得“壓扁”。

修改正態分布並在 geogebra 中使用滑塊后,我得出以下結論:

方程

mu=0.5, stddev = 0.8

mu=0.75, stddev = 0.8

接下來我需要在python中創建一個方法,它會生成隨機樣本,這些樣本將根據這個 PDF 進行分配。

最初我認為做到這一點的唯一方法是嘗試推導一個新的方程來生成隨機數,如Box-Muller證明中所示(我通過跟隨教程獲得)。

但是,我認為使用numpy庫的np.random.choice()方法可能有更簡單的方法來執行此操作。

畢竟,我應該能夠以非常小的步長對 PDF 進行積分,並獲得所述步長的各種概率(當然是近似值)。

因此,我編寫了以下腳本:

# Standard libs
import math

# Third party libs
import numpy as np

from alive_progress import alive_bar
from matplotlib import pyplot as plt

class RandomNumberGenerator:
    def __init__(self):
        pass

    def clamped_normal_distribution(self, mu: float, 
            stddev: float, x: float):
        """ Computes a value from the clamped normal distribution """
        divideByZeroAvoider = 1e-5
        if x < 0 or x > 1:
            return 0
        elif x >= 0 and x <= mu:
            return math.exp(-0.5*( (x - mu) / (stddev)  )**2 \
                    * (1/(x**2 + divideByZeroAvoider)))
        elif x <= 1 and x > mu:
            return math.exp(-0.5*( (x - mu) / (stddev)  )**2 \
                    * (1/((1-x)**2 + divideByZeroAvoider))) 
        else:
            print("This shouldn't happen!: {}".format(x))
            return 0

if __name__ == '__main__':
    rng = RandomNumberGenerator()

    mu = 0.7
    stddev = 1
    stepSize = 1e-3
    x = np.linspace(stepSize,1, int(1/stepSize) - 1)

    # Determine the total area under the curve
    samples = []
    print("Generating samples...")
    with alive_bar(len(x.tolist())) as bar:
        for i in x:
            samples.append(rng.clamped_normal_distribution(
                    mu, stddev, i))
            bar()

    area = np.trapz(samples, dx=stepSize)
    print("Area = {}".format(area))

    # Determine the probability of x falling in a specific interval
    probabilities = []

    print("Generating probabilties...")
    with alive_bar(len(x.tolist())) as bar:
        for i in x:
            lead = rng.clamped_normal_distribution(mu, 
                    stddev, i)
            lag = rng.clamped_normal_distribution(mu, 
                    stddev, i - stepSize)
            probability = np.trapz(
                    np.array([lag, lead]), 
                    dx=stepSize)
            
            # Divide by the area because this isn't a standard normal
            probabilities.append(probability / area)
            bar()
    
    # Should be approximately 1
    print("Probability: {}".format(sum(probabilities)))

    plt.plot(x, probabilities)
    plt.show()

    y = []
    print("Performing distribution test...")
    testSize = int(10e3)
    with alive_bar(testSize) as bar:
        for _ in range(testSize):
            randSamp = np.random.choice(samples, p=probabilities)
            y.append(randSamp)
            bar()

    plt.hist(y,300)
    plt.show()

線性間隔樣本的第一個 plot 概率看起來很有希望,給出了下圖:

在此處輸入圖像描述

但是,如果我們使用這些樣本作為具有給定概率的選擇,我們將得到以下直方圖:

在此處輸入圖像描述

我不知道為什么這不能正常工作。

我嘗試了其他(較小的)示例,例如numpy 網站上列出的示例,它們根據給定的概率數組生成直方圖。

如果可能的話,我真的很感激一些建議/直覺:)。

看起來調用np.random.choice(samples, p=probabilities)中的第一個參數有問題。 第一個參數應該是x ,而不是samples

作者添加:

這樣做的原因是samples是曲線的值(即 y 軸而不是 x 軸)。

因此,具有最高概率的值(即平均值附近的樣本)的值都約為 1,這就是為什么我們在值 1 附近看到如此巨大的尖峰。

將其更改為x可為我們提供以下圖表(對於10e3樣本):

在此處輸入圖像描述 在此處輸入圖像描述 在此處輸入圖像描述 在此處輸入圖像描述

按預期工作,非常好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM