前端加载和后端加载 | Excel 中的正态分布柱形图和 S 曲线

Front Loaded and Back Loaded | Normal Distribution Column Chart and S Curves in Excel

我们大多数人可能都知道正态分布曲线,但是对于前载和后载正态分布的新手,我想提供背景知识,然后继续说明我的问题。


Front-Loaded Distribution:如下所示,它有一个快速启动。例如在假设项目早期消耗更多资源的项目中,cost/hours 在项目开始时积极分配。


Back-Loaded Distribution:与 Front-Loaded 分布相反,它以较低的坡度开始,并在项目结束时逐渐陡峭。例如当大多数资源假定在项目后期消耗时。

在上面的图表中,绿线是 S 曲线,代表累积分布(建议时间内的资源利用),蓝色柱代表孤立的资源分布( Cost/Hours) 在那个时期。


作为参考,我提供了贝尔曲线/标准正态分布(当均值=中值时)图表(如下)和相关公式。


问题陈述:我能够生成正态分布曲线(参见下面的公式)但是我无法找到前载或后载曲线的解决方案。

如何使偏度向右(前向加载/正偏分布,这意味着平均值大于中位数)和左偏(后向加载/负偏分布,这意味着平均值小于中位数)比中位数)在正态分布中?

公式解释:

单元格 B8 表示任意选择的标准偏差。它影响正态分布的峰度。在上面的屏幕截图中,我选择的正态分布范围是从 -3SD 到 3SD。

单元格 B9 到 B18 表示 Z-Score 的均匀分布,使用公式:

=B8-((2*$B)/Period)

单元格 C9 到 C18 表示基于 Z 分数和金额的正态分布,使用公式:

=(NORMSDIST(B9)-NORMSDIST(B8))*Amount/(1-2*NORMSDIST($B))

更新: 在评论中的一个 link 之后,我最接近以下情况。由于使用了 volatile Rand() 函数,该问题以黄色模式突出显示,图表并不像它们应该的那样平滑。由于我上面给出的公式不会创建 ZigZag 模式,我相信我们也可以有倾斜的正态分布和平滑!

注:

  1. 我用的是Excel2016,如果有什么新引进的公式可以解决我的问题,我欢迎。另外,我毫不犹豫地使用UDF。

  2. 前负荷分配和后负荷分配的数量是名义上的。他们可能会有所不同。我只对结果图表的形状感兴趣。

请帮忙!

您可以使用以下方法生成曲线,并可以根据您的要求使用它们生成的数字。

有公式

曲线

备注:

  1. 如果你想改变你必须向下或向上拖动单元格的容器 为了完成这个系列
  2. 如果你想改变总成本,你可以改变乘数
  3. 如果你想改变曲线的倾斜度,你可以改变 C 列中的分隔符,当前设置为 2,如果为 -2,则倾斜 会改变方向,你可以尝试不同的数字, 方向取决于它是小于零还是大于 比零

复制过去

=A2+180/($G-1)
=RADIANS(A2)
=$G*SIN(B2 + SIN(B2)/2)

我使用实际的数学公式得出了结果。在我看来你想要实现的目标。 'Skewed' 部分中的橙色单元格是可以更改以改变倾斜程度和方向的单元格。下面是演示用的一些图片,后面是用到的公式。

第 5 行第 1 列的公式

B:=(A5*$A)+0(0为均值,可随意更改)

C:=(1/($A* SQRT(2*PI())))*EXP(-(B5^2)/2)

D:=0.5*(1+ERF(B5/SQRT(2)))

E:=$A*C5

F:=(A5*$A*(1+$F*SIN((F4*PI())/(2*$F))))+0(0为均值,可随意更改)

G:=(1/($A* SQRT(2*PI())))*EXP(-((F5+$G)^2)/2)

H:=0.5*(1+ERF((B5+$G)/SQRT(2)))

我:=$A*G5

如果您对 Python 答案持开放态度,我可以为您提供获取 Python Pandas 库的代码,以从倾斜的法线生成随机观测值,然后将 bin (桶)他们给你。 Python 脚本中的以下内容捕获用例,但也可以使用 COM 创建,因此可从 VBA.

创建
import numpy as np
import pandas as pd
from scipy.stats import skewnorm

class PythonSkewedNormal(object):
    _reg_clsid_ = "{1583241D-27EA-4A01-ACFB-4905810F6B98}"
    _reg_progid_= 'SciPyInVBA.PythonSkewedNormal'
    _public_methods_ = ['GeneratePopulation','BinnedSkewedNormal']

    def GeneratePopulation(self,a, sz):
        # https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html
        np.random.seed(10);
        #https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html
        return skewnorm.rvs(a, size=sz).tolist();

    def BinnedSkewedNormal(self,a, sz, bins):
        # https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.random.seed.html
        np.random.seed(10);
        #https://docs.scipy.org/doc/scipy-0.19.1/reference/generated/scipy.stats.skewnorm.html
        pop = skewnorm.rvs(a, size=sz); #.tolist();
        bins2 = np.array(bins)
        bins3 = pd.cut(pop,bins2)

        table = pd.value_counts(bins3, sort=False)

        table.index = table.index.astype(str)

        return table.reset_index().values.tolist();

if __name__=='__main__':
    print ("Registering COM server...")
    import win32com.server.register
    win32com.server.register.UseCommandLine(PythonSkewedNormal)

和 VBA 客户端代码

Option Explicit

Sub TestPythonSkewedNormal()

    Dim skewedNormal As Object
    Set skewedNormal = CreateObject("SciPyInVBA.PythonSkewedNormal")

    Dim lSize As Long
    lSize = 100

    Dim shtData As Excel.Worksheet
    Set shtData = ThisWorkbook.Worksheets.Item("Sheet3") '<--- change sheet to your circumstances
    shtData.Cells.Clear

    Dim vBins
    vBins = Array(-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5)

    'Stop
    Dim vBinnedData
    vBinnedData = skewedNormal.BinnedSkewedNormal(-5, lSize, vBins)

    Dim rngData As Excel.Range
    Set rngData = shtData.Cells(2, 1).Resize(UBound(vBins) - LBound(vBins), 2)

    rngData.Value2 = vBinnedData

    'Stop

End Sub

示例输出

(-5, -4]        0
(-4, -3]        0
(-3, -2]        4
(-2, -1]       32
(-1, 0]        57
(0, 1]          7
(1, 2]          0
(2, 3]          0
(3, 4]          0
(4, 5]          0

原始代码存放于my blog

如果您想确保 bin 中始终有一个值,您可以使用以下方法,它使用正态分布并简单地更改均值和标准差以获得您想要的曲线。

改变均值会使峰值向左或向右移动。更改标准偏差会使数量更均匀或更多变。我在下面的示例中使用了 0-1000 作为我的默认范围,但修改公式以获取您想要的任何值应该很容易。请注意,为了满足所有箱子必须非零的要求,您需要手动调整数字,直到获得适合的曲线。

黄色单元格用于数据输入,绿色单元格用于计数(因此,如果您添加分箱,则需要根据顺序对它们进行编号)。

单元格 B7 中的公式(复制到单元格 B16): =NORMDIST($A7*1000/MAX($A:$A),$B,$B,TRUE)-NORMDIST($A6*1000/MAX($A:$A),$B,$B,TRUE)

单元格 C7 中的公式(复制到单元格 C16): =IF(A7=MAX($A:$A),$C-SUM(C:C6),ROUND(B7/SUM($B:$B)*$C,0))

添加新的 bins 非常简单,并且仍然基于 0-1000 范围,因此除了添加行和复制公式外,您不需要更改任何数字:

上面的例子还展示了窄标准差和高均值如何结合起来使得起始 bin 的数量非常少。但是还是有值的(只要count足够大)。

如果这将被其他人使用(例如,使 B 列依赖于查找),您可能希望预先定义不同的偏度选择,但希望这足以满足您的需求。

基于@usmanhaq 的回答,vba 为分布曲线模拟制作的宏。针对前后加载曲线的 100% 缩放进行了校正。 click here to go Github Lib