【问题标题】:How can Python use n, min, max, mean, std, 25%, 50%, 75%, Skew, Kurtosis to define a psudo-random Probability Density Estimate/Function?Python 如何使用 n、min、max、mean、std、25%、50%、75%、Skew、Kurtosis 来定义伪随机概率密度估计/函数?
【发布时间】:2020-08-09 12:09:02
【问题描述】:

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

https://docs.python.org/3/library/random.html 我看到这些分布均匀、正态(高斯)、对数正态、负指数、伽马和 beta 分布,但我需要直接生成仅由我的 10 个参数定义的分布的值,而不参考一个分布族。

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

这将是 describe() 的反面,在某种程度上包括偏斜和峰度。我可以进行循环或优化,直到随机生成的数字满足条件,但这可能需要无限的时间才能满足我的 10 个参数。

我在 R 中找到了生成数据集的 optim,但到目前为止,我已经能够增加 R optim 源代码中的参数或使用 Python scipy.optimize 或类似方法复制它,尽管这些仍然依赖于方法而不是根据我的需要直接psudo-randomly根据我的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))

【问题讨论】:

  • 你的 10 个参数没有定义概率分布函数。您必须首先找到一种方法来定义分布。
  • 感谢 Peter 改进问题,感谢 rpoleski 提供更简短的问题总结。也许假设基础反馈(可能是错误的)在这里会有所帮助。人类创建和使用分布族(正常等),因此我们有限的能力可以感知和分类 PDF。但是,Python 在相对无限能力的机器上运行,这些机器“应该”能够生成 10 参数伪随机值,而无需任何分布族参考(参数密度估计)。这是我目前使用 Python 编码的目标。 IE; numpy.random.xxx(n=3,min=4,max=6,mu=5,,,,,,,) psudo-randomly = [4,5,6]。想法?
  • numpy.random中没有这个功能。您需要以某种方式定义概率分布。那么你的问题的标题不是很丰富。请尝试改写问题以获得答案。
  • 谢谢 rpoleski。希望我现在可以更好地措辞我的标题。我已将 describe() 源文件 generic.py 修改为 describeSK(),因此它包含 Skew 和 Kurtosis。同样,到目前为止,我没有成功地尝试在 numpy.random 源文件 init.py 中修改现有定义或添加新定义,它将使用我的 10 个参数来定义如何伪随机产生价值。我们热切欢迎任何 init__.py 或替代建议。
  • 目前是否有另一种语言/工具具有这种能力,可以使用 n,min,max,mean,sd,25%,50%,75%,skew, kurtosis 作为参数来定义 psudo-概率密度估计/函数的随机值?

标签: python-3.x random probability-density


【解决方案1】:

根据分布生成随机数的最通用方法如下:

  • 生成以 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 个均匀分布的点上的值,包括上面提到的 5 个百分位数。
  • 它设置了优化的界限。除 5 个百分位数外,优化在所有地方都受到最小值和最大值的限制。
  • 它设置了其他四个参数。
  • 然后它将目标函数 (_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))

最后一点:如果您可以访问基础数据点,而不仅仅是它们的统计数据,那么您可以使用 other methods 从这些数据点形式的分布中进行抽样。示例包括内核密度估计直方图回归模型(特别是对于时间序列数据)。另见Generate random data based on existing data

【讨论】:

  • 再次感谢彼得。我没有考虑组合一个已知的分布族工具,然后只优化剩余的参数。我现在就试试看。
  • 这种方法仍然需要大量工作,而且我看不出如何满足所有 10 个约束。我不知道 R,但很难看出问题中的代码如何完成所要求的工作。特别是,objfun 只使用了 10 个参数中的 2 个。
  • 仍然没有完全成功,虽然我记得一些用于离散分布的 SIMIO 代码,当以 SIMIO 的累积分布形式输入时,“Random.Discrete(1, 0.25, 2, 0.50, 3, 0.75, 4, 1.00)”,类似于上面 Peter 的 ICDF 提议,可以为每个四分位数定义“一个形状”。此外,我发现 Jason Brown Brownlee 的这两篇文章使用了 ECDF(),但仍然没有完全使用所有 10 个参数。我会继续尝试,尽管任何进一步的建议将不胜感激。 machinelearningmastery.com/…
  • 您是否尝试优化偏度、峰度、均值和标准差,同时保持其他参数不变? (如前所述,您问题中的代码似乎没有这样做。)如果是这样,您能否编辑您的问题,显示以这种方式优化时得到的结果以及更新的代码?
  • 另外,您能否编辑您的问题,显示您希望使用的“10 参数集”示例?
猜你喜欢
  • 2015-06-28
  • 1970-01-01
  • 2018-12-11
  • 2016-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-11
  • 1970-01-01
相关资源
最近更新 更多