Python 如何使用 n, min, max, mean, std, 25%, 50%, 75%, Skew, Kurtosis 来定义 psudo-random Probability Density EstimateFunction?

问题描述 投票:0回答:1

在阅读和实验 numpy.random 的过程中,我似乎无法找到或创建我所需要的东西;一个 10 个参数的 Python 伪随机值生成器,包括 count、min、max、mean、sd、25%ile、50th%ile (中位数)、75th%ile、skew 和 kurtosis。

https:/docs.python.org3libraryrandom.html。 我看到这些分布有均匀分布、正态分布(高斯)、对数正态分布、负指数分布、gamma分布和β分布,不过我需要直接生成一个只由我的10个参数定义的分布值,而不参考分布族。

是否有文档,或者作者,numpy.random.xxxxxx(n, min, max, mean, sd, 25%, 50%, 75%, skew, kurtosis),或者请问有什么最接近的现有源代码,我可以修改来实现这个目标?

这将是describe()的反面,在某种程度上包括偏度和峰度。  我可以做一个循环或优化,直到用随机生成的数字满足一个标准,尽管这可能需要无限的时间来满足我的10个参数。

我在R中找到了能生成数据集的optim,但到目前为止,我还能在R optim源代码中增加参数,或者用Python scipy.optim或类似的方法复制,不过这些仍然依赖于方法,而不是按照我的需要直接psudo-随机地根据我的10个参数创建一个数据集。

m0 <- 20
sd0 <- 5
min <- 1
max <- 45
n <- 15
set.seed(1)
mm <- min:max
x0 <- sample(mm, size=n, replace=TRUE)
objfun <- function(x) {(mean(x)-m0)^2+(sd(x)-sd0)^2}
candfun <- function(x) {x[sample(n, size=1)] <- sample(mm, size=1)
    return(x)}
objfun(x0) ##INITIAL RESULT:83.93495
o1 <- optim(par=x0, fn=objfun, gr=candfun, method="SANN", control=list(maxit=1e6))
mean(o1$par) ##INITIAL RESULT:20
sd(o1$par) ##INITIAL RESULT:5
plot(table(o1$par))
python-3.x random probability-density
1个回答
0
投票

按照分布生成随机数的最一般方法如下。

  • 生成一个以0和1为界的均匀随机数(例如... ) numpy.random.random()).
  • 取该数的逆CDF(逆累积分布函数)。

结果是一个遵循分布的数字。

在你的例子中,逆CDF(ICDF(x))已经由你的五个参数--最小值、最大值和三个百分位数决定,如下所示。

  • ICDF(0) = 最小值
  • ICDF(0.25)=第25个百分位数。
  • ICDF(0.5)=第50个百分位数。
  • ICDF(0.75)=第75个百分位数。
  • ICDF(1)=最大值

因此,你已经对反CDF的样子有了一些概念。现在你要做的就是以某种方式优化反CDF的其他参数(平均值、标准差、偏度和峰度)。例如,你可以 "填入 "其他百分位数的反CDF,看看它们与你要追求的参数有多匹配。在这个意义上,一个好的开始猜测是刚才提到的百分位数的线性插值。 另一个需要注意的是,反CDF "永远不能下降"。


下面的代码显示了一个解决方案。 它做了以下步骤。

  • 通过线性插值计算出反CDF的初始猜测值 初始猜测由该函数在101个均匀分布的点的值组成,包括上面提到的五个百分位数。
  • 它设置了优化的边界。 除五个百分位数外,优化的边界是各地的最小值和最大值。
  • 它设置了其他四个参数。
  • 然后,它将目标函数(_lossfunc)、初始猜测、边界和SciPy的其他参数。scipy.optimize.minimize 方法进行优化。
  • 优化完成后,代码会检查是否成功,如果不成功则会引发错误。
  • 如果优化成功,代码计算出最终结果的反CDF。
  • 它生成N个均匀的随机值。
  • 它用反CDF转换这些值并返回这些值。
import scipy.stats.mstats as mst
from scipy.optimize import minimize
from scipy.interpolate import interp1d
import numpy

# Define the loss function, which compares the calculated
# and ideal parameters
def _lossfunc(x, *args):
    mean, stdev, skew, kurt, chunks = args
    st = (
        (numpy.mean(x) - mean) ** 2
        + (numpy.sqrt(numpy.var(x)) - stdev) ** 2
        + ((mst.skew(x) - skew)) ** 2
        + ((mst.kurtosis(x) - kurt)) ** 2
    )
    return st

def adjust(rx, percentiles):
    eps = (max(rx) - min(rx)) / (3.0 * len(rx))
    # Make result monotonic
    for i in range(1, len(rx)):
        if (
            i - 2 >= 0
            and rx[i - 2] < rx[i - 1]
            and rx[i - 1] >= rx[i]
            and rx[i - 2] < rx[i]
        ):
            rx[i - 1] = (rx[i - 2] + rx[i]) / 2.0
        elif rx[i - 1] >= rx[i]:
            rx[i] = rx[i - 1] + eps
    # Constrain to percentiles
    for pi in range(1, len(percentiles)):
        previ = percentiles[pi - 1][0]
        prev = rx[previ]
        curr = rx[percentiles[pi][0]]
        prevideal = percentiles[pi - 1][1]
        currideal = percentiles[pi][1]
        realrange = max(eps, curr - prev)
        idealrange = max(eps, currideal - prevideal)
        for i in range(previ + 1, percentiles[pi][0]):
            if rx[i] >= currideal or rx[i] <= prevideal:
              rx[i] = (
                  prevideal
                  + max(eps * (i - previ + 1 + 1), rx[i] - prev) * idealrange / realrange
              )
        rx[percentiles[pi][0]] = currideal
    # Make monotonic again
    for pi in range(1, len(percentiles)):
        previ = percentiles[pi - 1][0]
        curri = percentiles[pi][0]
        for i in range(previ+1, curri+1):
          if (
            i - 2 >= 0
            and rx[i - 2] < rx[i - 1]
            and rx[i - 1] >= rx[i]
            and rx[i - 2] < rx[i]
            and i-1!=previ and i-1!=curri
          ):
            rx[i - 1] = (rx[i - 2] + rx[i]) / 2.0
          elif rx[i - 1] >= rx[i] and i!=curri:
            rx[i] = rx[i - 1] + eps
    return rx

# Calculates an inverse CDF for the given nine parameters.
def _get_inverse_cdf(mn, p25, p50, p75, mx, mean, stdev, skew, kurt, chunks=100):
    if chunks < 0:
        raise ValueError
    # Minimum of 16 chunks
    chunks = max(16, chunks)
    # Round chunks up to closest multiple of 4
    if chunks % 4 != 0:
        chunks += 4 - (chunks % 4)
    # Calculate initial guess for the inverse CDF; an
    # interpolation of the inverse CDF through the known
    # percentiles
    interp = interp1d([0, 0.25, 0.5, 0.75, 1.0], [mn, p25, p50, p75, mx], kind="cubic")
    rnge = mx - mn
    x = interp(numpy.linspace(0, 1, chunks + 1))
    # Bounds, taking percentiles into account
    bounds = [(mn, mx) for i in range(chunks + 1)]
    percentiles = [
        [0, mn],
        [int(chunks * 1 / 4), p25],
        [int(chunks * 2 / 4), p50],
        [int(chunks * 3 / 4), p75],
        [int(chunks), mx],
    ]
    for p in percentiles:
        bounds[p[0]] = (p[1], p[1])
    # Other parameters
    otherParams = (mean, stdev, skew, kurt, chunks)
    # Optimize the result for the given parameters
    # using the initial guess and the bounds
    result = minimize(
        _lossfunc,  # Loss function
        x,  # Initial guess
        otherParams,  # Arguments
        bounds=bounds,
    )
    rx = result.x
    if result.success:
        adjust(rx, percentiles)
        # Minimize again
        result = minimize(
            _lossfunc,  # Loss function
            rx,  # Initial guess
            otherParams,  # Arguments
            bounds=bounds,
        )
        rx = result.x
        adjust(rx, percentiles)
        # Minimize again
        result = minimize(
            _lossfunc,  # Loss function
            rx,  # Initial guess
            otherParams,  # Arguments
            bounds=bounds,
        )
        rx = result.x
    # Calculate interpolating function of result
    ls = numpy.linspace(0, 1, chunks + 1)
    success = result.success
    icdf=interp1d(ls, rx, kind="linear")
    # == To check the quality of the result
    if False:
       meandiff = numpy.mean(rx) - mean
       stdevdiff = numpy.sqrt(numpy.var(rx)) - stdev
       print(meandiff)
       print(stdevdiff)
       print(mst.skew(rx)-skew)
       print(mst.kurtosis(rx)-kurt)
       print(icdf(0)-percentiles[0][1])
       print(icdf(0.25)-percentiles[1][1])
       print(icdf(0.5)-percentiles[2][1])
       print(icdf(0.75)-percentiles[3][1])
       print(icdf(1)-percentiles[4][1])
    return (icdf, success)

def random_10params(n, mn, p25, p50, p75, mx, mean, stdev, skew, kurt):
   """ Note: Kurtosis as used here is Fisher's kurtosis, 
     or kurtosis excess. Stdev is square root of numpy.var(). """
   # Calculate inverse CDF
   icdf, success = (None, False)
   tries = 0
   # Try up to 10 times to get a converging inverse CDF, increasing the mesh each time
   chunks = 500
   while tries < 10:
      icdf, success = _get_inverse_cdf(mn, p25, p50, p75, mx, mean, stdev, skew, kurt,chunks=chunks)
      tries+=1
      chunks+=100
      if success: break
   if not success:
     print("Warning: Estimation failed and may be inaccurate")
   # Generate uniform random variables
   npr=numpy.random.random(size=n)
   # Transform them with the inverse CDF
   return icdf(npr)

例子。

print(random_10params(n=1000, mn=39, p25=116, p50=147, p75=186, mx=401, mean=154.1207, stdev=52.3257, skew=.7083, kurt=.5383))

最后一点: 如果你可以访问底层的数据点, 而不仅仅是它们的统计数据, 那么就有 其他方法 你可以用来从这些数据点形成的分布中取样。 例子包括 核密度估计, 直方图回归模型 (特别是对于时间序列数据)。 另见 根据现有数据生成随机数据.

© www.soinside.com 2019 - 2024. All rights reserved.