我正在尝试为我的地质研究创建某种简化的Oracle Crystal Ball应用程序,该应用程序将使用P90(90%置信度)和P10(10%置信度)值作为不同概率情形的输入和收益分布。听起来像蒙特卡洛发行。我刚接触Python,最近才开始,顺便说一句:)
该主题将分为四个关键部分:
PART 1.工作范围的一般说明。
为简单起见,假设我们只有三个类别,每个类别都有P90和P10参数,而它们之间没有任何步骤:
利用笛卡尔积,我们获得了以下8种可能的情况的列表:
在每个列表中乘以参数会得出以下乘积:
测量每种产品的频率会导致:
{6:1,12:3,24:3,48:1},或考虑以下百分比:
{6:12.5%,12:37.5%,24:37.5%,48:12:5%,},这意味着发生12或24的可能性高于6或48。
这就是我想要得到的结果:知道乘积能够获得均值,中位数和众数值的可能性。
对于我的硬件而言,最困难的部分是大量实际情况。共有六类,在P90和P10值之间有小的变化。考虑到公制,P90和P10值的范围可能如下:
通常情况下,实际的案例研究将使用更窄的范围,例如,squeurea区域为0.1-2.0 km2,厚度为1-10 m,孔隙度为8-15等。尽管如此,即使听起来像是考虑上述步骤,“ google”可能的方案数量。结果,我收到以下通知,这是关键问题:
以退出代码137(被信号9:SIGKILL中断)结束的过程。
当计算总量超过〜10MM和〜1分钟(通过实验检查,因此数字比较粗糙)时,就会发生这种情况。
PART2。伪编码。
优良作法是,在进行伪编码时不应抽象化,但是我在该领域的经验为零,因此会尽力而为。
User inputs minimum possible values (P90) for total 6 categories
User inputs maximum possible values (P10) for total 6 categories
Total 6 list are created (square area, layer thickness, porosity etc.), 1 per each category that contain a range of possible values and indicated step (P90_category1, P10_category1, step1)
Use a Cartesian product to create a list_of_tuples with possible scenarios
Convert list_of_tuples to the list_of_lists
Create empty_list
for each element in the list_of_lists:
calculate its product
append to the empty_list
Round values in the empty_list
Create a dictionary that counts similar values in the empty_list
Calculate a probability of each value according to its repetition frequency in the dictionary
就是这样。还应用了一些基本的统计数据和绘图,但这并不是关键时刻。
PART 3.实际的Python代码。
最初的P90值(置信度为90%):
P90_area = float(input('P90 area: '))
P90_thickness = float(input('P90 thickness: '))
P90_porosity = float(input('P90 porosity: '))
P90_saturation = float(input('P90 saturation: '))
P90_pressure = float(input('P90 pressure: '))
P90_surface = float(input('P90 surface: '))
然后是P10值(置信度为10%):
P10_area = float(input('P10 area: '))
P10_thickness = float(input('P10 thickness: '))
P10_porosity = float(input('P10 porosity: '))
P10_saturation = float(input('P10 saturation: '))
P10_pressure = float(input('P10 pressure: '))
P10_surface = float(input('P10 surface: '))
使用特定步骤创建从P90到P10的值范围
area_values = np.arange(P90_area, P10_area + 0.01, 0.01)
thickness_values = np.arange(P90_thickness, P10_thickness + 0.1, 0.1)
porosity_values = np.arange(P90_porosity, P10_porosity + 0.01, 0.01)
saturation_range = np.arange(P90_saturation, P10_saturation + 0.01, 0.01)
pressure_range = np.arange(P90_pressure, P10_pressure + 1, 1)
surface_range = np.arange(P90_surface, P10_surface + 0.01, 0.01)
将所有列表合并为笛卡尔积(即[(area1,thickness1,孔隙率1),(area1,thickness1,孔隙率2)等]):
list_of_tuples = list(itertools.product(area_values, thickness_values, porosity_values, saturation_range, pressure_range, surface_range)
将元组列表转换为列表列表:
list_of_lists = [list(elem) for elem in list_of_tuples]
创建一个包含多个值的列表并将其排序('np.prod'返回每个列表的乘积):
multiplied_values = []
for i in list_of_lists:
i = np.prod(np.array(i))
multiplied_values.append(i)
multiplied_values = sorted(multiplied_values)
舍入值:
rounded_values = [float(Decimal('%.2f' % elem)) for elem in multiplied_values]
创建一个包含所有相似/唯一对象的字典:
counts = Counter(rounded_values)
通过将值除以列表中元素的总数来计算概率:
probability_mass = {k: v/total for k, v in counts.items()}
它起作用了,这里有简单的统计信息和特定情况的图表:
Figure. Probability distribution diagram
第一个问题至关重要,因为它阻止了大数据堆栈的计算:
PART 4.关键问题。
问题1。关键问题:
结果,我收到以下通知,这是关键问题:
以退出代码137(被信号9:SIGKILL中断)结束的过程。
根据类似的主题,很可能我的脚本由于CPU使用率过高而被操作系统杀死。在运行代码时,我用'top'命令检查了CPU的负载,当CPU可以处理输入参数时,CPU的负载达到了100%,而在中断时,CPU的负载达到了110%。
规格:华硕G531GU笔记本电脑| i7-9750H CPU 2.60GHz | GeForce GTX 1660 TI,6Gb | 16Gb DDR4 | Ubuntu 18 | PyCharm社区IDE。
问题:如果有什么机会,我该如何摆脱这种干扰并让脚本在必要时运行?我愿意等待所需的时间,以便为大型数据堆栈获取正确的分发。为每个参数增加一个步骤是一个硬性选择,我宁愿这样做。
第二季度。概率分布图看起来不像经典的正态分布,而最大概率值和平均值之间的差异非常大。您怎么看,代码的逻辑可能有问题吗?
P.S。我了解此脚本看起来很坎y,希望您的眼睛不会流血)
答案 0 :(得分:0)
由于您正在尝试计算每种可能的情况,因此此处所需的计算与每个范围中元素的数量成指数关系增长。 我很想为您调试一个完整的代码,但是我需要输入,因此您可以将完整的代码与已经指定的输入一起发布,以便我们知道要使用什么合理的值。
有一点不同,我们可以尝试解决您的原始问题,而不是尝试修复您的代码?当您说“简化的概率分布计算器”时,您是什么意思?在尝试查看如何在Python中实现该过程之前,您能以伪代码编写步骤以使我们理解该过程吗。
根据您对上述问题的回答,我可能建议您采用抽样方法,而不是评估每种可能性。查找蒙特卡洛模拟。如果您有一个要使用新数据更新的先前发行版,并且想知道后验(最终)发行版,那么可以考虑使用贝叶斯方法,特别是Winbugs(不是Python的贝叶斯程序的理想独立程序)。
PS。我知道我的答案可能更适合写为评论,但显然您需要+50的声誉,而我还没有呢:(
答案 1 :(得分:0)
因此,对于输入参数,随机采样和笛卡尔积的均匀分布,我已经完成了您需要的工作。结果看起来像是指数分布。最好用weibull分布进行建模。
我做了一些进一步的分析,因为任何模拟的结果都应该进一步研究以检查模拟是否足够。为此,我对10,100,1000,10000,100000,10000000个样本进行了蒙特卡洛采样以生成直方图。从拟合的weibull的alpha和beta的收敛中可以看出,一百万个样本就足够了。
我确定您会对此有疑问,因此请在下面提问。请注意,直方图以对数对数比例显示,因此在可视化分布(或注释掉xscale和yscale线)时,您需要牢记这一点。
结果如下: https://i.stack.imgur.com/viQ9i.png https://i.stack.imgur.com/0kc4n.png
这是生成输出的代码:
import numpy as np
from tqdm import tqdm
import random
import matplotlib.pyplot as plt
import scipy.stats as ss
#these should be user inputs
area_min = 0.01
area_max = 100
thickness_min = 0.1
thickness_max = 100
porosity_min = 0.01
porosity_max = 1
saturation_min = 0.01
saturation_max = 1
pressure_min = 1
pressure_max = 2000
surface_min = 0.01
surface_max = 1
grid_resolution = 1000 #how finely we will slice each property. I have kept this consistent as it makes more sense to do so when sampling
#With a grid_resolution of 1000, the number of possible combinations here is 1000^6 ==> 10^18 so we will randomly sample the array
#I assume you want to get a probability distribution of these combinations.
area_array = np.linspace(area_min,area_max,grid_resolution)
thickness_array = np.linspace(thickness_min,thickness_max,grid_resolution)
porosity_array = np.linspace(porosity_min,porosity_max,grid_resolution)
saturation_array = np.linspace(saturation_min,saturation_max,grid_resolution)
pressure_array = np.linspace(pressure_min,pressure_max,grid_resolution)
surface_array = np.linspace(surface_min,surface_max,grid_resolution)
#it is important to try different sample sizes to be sure your sample is large enough
samples_to_test = [1,2,3,4,5,6] #log10 scale
xmax = 10**8
alpha_array = []
beta_array = []
plt.figure(figsize=(12,10))
for i,s in enumerate(samples_to_test):
plt.subplot(231+i)
samples = 10**s
product_array = []
for _ in tqdm(range(samples)):
area = random.choice(area_array)
thickness = random.choice(thickness_array)
porosity = random.choice(porosity_array)
saturation = random.choice(saturation_array)
pressure = random.choice(pressure_array)
surface = random.choice(surface_array)
product_array.append(area*thickness*porosity*saturation*pressure*surface)
xvals = np.logspace(1,np.log10(xmax),1000)
[beta,_,alpha] = ss.weibull_min.fit(data=product_array,floc=0)
alpha_array.append(alpha)
beta_array.append(beta)
weibull_yvals = ss.weibull_min.pdf(xvals,beta,scale=alpha)
plt.plot(xvals,weibull_yvals)
print('Weibull fit parameters:\nalpha =',alpha,'\nbeta =',beta)
[mean,variance] = ss.weibull_min.stats(beta, loc=0, scale=alpha, moments='mv')
median = ss.weibull_min.median(beta, loc=0, scale=alpha)
print('Mean =',mean)
print('Median =',median)
print('Standard deviation =',variance**0.5)
plt.hist(product_array,bins=1000,density=True)
plt.yscale('log')
plt.xscale('log')
plt.xlabel('Cartesian Product of parameters')
plt.ylabel('Probability density ($log_{10}$ scale)')
plt.title(str('Monte Carlo samples = '+str(samples)))
plt.xlim(10,xmax)
plt.ylim(10**-8,0.0001)
plt.suptitle('Probability of of a given cartesian product of the specified parameters\nmeasured using different numbers of Monte Carlo samples')
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.semilogx(10**np.array(samples_to_test),alpha_array,label='alpha')
plt.legend()
plt.subplot(122)
plt.semilogx(10**np.array(samples_to_test),beta_array,label='beta')
plt.legend()
plt.suptitle('Test results for alpha and beta')
plt.show()
Output:
100%|██████████| 10/10 [00:00<?, ?it/s]
Weibull fit parameters:
alpha = 86642.0194345818
beta = 0.4938259951069627
Mean = 177350.7081149186
Median = 41247.66458603765
Standard deviation = 403557.41514732403
100%|██████████| 100/100 [00:00<00:00, 100246.27it/s]
Weibull fit parameters:
alpha = 177861.91287733015
beta = 0.6310314479279571
Mean = 251385.7124440623
Median = 99503.40459313976
Standard deviation = 415414.97618995525
100%|██████████| 1000/1000 [00:00<00:00, 199131.37it/s]
Weibull fit parameters:
alpha = 171932.22877129668
beta = 0.5452693527437176
Mean = 296661.14084923535
Median = 87788.61401806296
Standard deviation = 589615.4680695855
100%|██████████| 10000/10000 [00:00<00:00, 179051.70it/s]
Weibull fit parameters:
alpha = 166909.86147776648
beta = 0.5172460791589029
Mean = 314175.4976503747
Median = 82176.44526800542
Standard deviation = 670314.3944630618
100%|██████████| 100000/100000 [00:00<00:00, 144477.93it/s]
Weibull fit parameters:
alpha = 167711.26073670806
beta = 0.5194333533253157
Mean = 313393.61873437575
Median = 82817.74728224205
Standard deviation = 664803.5086740599
100%|██████████| 1000000/1000000 [00:07<00:00, 140706.15it/s]
Weibull fit parameters:
alpha = 168089.6178189406
beta = 0.5186379527889259
Mean = 314930.2501968761
Median = 82914.8108556469
Standard deviation = 669461.6904337168