前端加载和后端加载 | 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 模式,我相信我们也可以有倾斜的正态分布和平滑!
注:
我用的是Excel2016,如果有什么新引进的公式可以解决我的问题,我欢迎。另外,我毫不犹豫地使用UDF。
前负荷分配和后负荷分配的数量是名义上的。他们可能会有所不同。我只对结果图表的形状感兴趣。
请帮忙!
您可以使用以下方法生成曲线,并可以根据您的要求使用它们生成的数字。
有公式
曲线
备注:
- 如果你想改变你必须向下或向上拖动单元格的容器
为了完成这个系列
- 如果你想改变总成本,你可以改变乘数
- 如果你想改变曲线的倾斜度,你可以改变
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
我们大多数人可能都知道正态分布曲线,但是对于前载和后载正态分布的新手,我想提供背景知识,然后继续说明我的问题。
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 模式,我相信我们也可以有倾斜的正态分布和平滑!
注:
我用的是Excel2016,如果有什么新引进的公式可以解决我的问题,我欢迎。另外,我毫不犹豫地使用UDF。
前负荷分配和后负荷分配的数量是名义上的。他们可能会有所不同。我只对结果图表的形状感兴趣。
请帮忙!
您可以使用以下方法生成曲线,并可以根据您的要求使用它们生成的数字。
有公式
曲线
备注:
- 如果你想改变你必须向下或向上拖动单元格的容器 为了完成这个系列
- 如果你想改变总成本,你可以改变乘数
- 如果你想改变曲线的倾斜度,你可以改变 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