使用numpy进行三角形和法线概率分布的蒙特卡罗模拟

时间:2018-09-10 13:02:19

标签: python numpy simulation montecarlo

我正在尝试执行蒙特卡洛模拟,以计算热泵系统的电费不确定性。我有几个输入参数(COP,电费),它们是三角概率分布。电力总成本由三个子组件(热泵和泵)的计算成本之和组成,并且具有(近似)正态分布。

我想知道我是否正确执行了MC仿真。由于我必须在70个不同的热泵系统上循环进行MC模拟,因此我也想知道是否有更快的方法。

由于我在编码方面绝对是新手,请为我的凌乱代码道歉。

感谢您的帮助!

我的代码:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import triangular

N = 1_000_000

def energy_output(coef_performance, energy_input):
    return energy_input * coef_performance / (coef_performance - 1)  
COP_DISTRIBUTION_PARAM = dict(left=4, mode=4.5, right=5)
def seed_cop():
    return triangular(**COP_DISTRIBUTION_PARAM )
INPUT_ENERGY_HEATING = 866
INPUT_ENERGY_COOLING = 912
def random_energy_output():
    return energy_output(seed_cop(), energy_input=INPUT_ENERGY_HEATING)    
energy_outputs = [random_energy_output() for _ in range(N)]

a = min(energy_outputs)
b = max(energy_outputs)
med = np.median(energy_outputs)
############################
def elec_costs_heatpump(elec_costs, coef_performance,energy_output):
    return energy_output * 1000 / coef_performance * elec_costs

ELEC_DISTRIBUTION_PARAM = dict(left=0.14, mode=0.15, right=0.16)
def seed_elec():
    return triangular(**ELEC_DISTRIBUTION_PARAM )

HP_OUTPUT_DISTRIBUTION_PARAM = dict(left=a, mode=med, right=b)
def seed_output():
    return triangular(**HP_OUTPUT_DISTRIBUTION_PARAM )

def random_elec_costs_heatpump():
    return elec_costs_heatpump(seed_elec(),seed_cop(),seed_output() )    
elec_costs_heatpump = [random_elec_costs_heatpump() for _ in range(N)]
mean_hp = np.mean(elec_costs_heatpump)
std_hp = np.std(elec_costs_heatpump)
############################
def elec_costs_coldpump(elec_costs, coef_performance_pump,energy_input):
    return energy_input * 1000 / coef_performance_pump * elec_costs

COP_PUMP_DISTRIBUTION_PARAM = dict(left=35, mode=40, right=45)
def seed_cop_pump():
    return triangular(**COP_PUMP_DISTRIBUTION_PARAM )

def random_elec_costs_coldpump():
    return elec_costs_coldpump(seed_elec(),seed_cop_pump(), energy_input=INPUT_ENERGY_COOLING)    
elec_costs_coldpump = [random_elec_costs_coldpump() for _ in range(N)]
mean_cp = np.mean(elec_costs_coldpump)
sdt_cp = np.std(elec_costs_coldpump)
#########################
def elec_costs_warmpump(elec_costs, coef_performance_pump,energy_input):
    return energy_input * 1000 / coef_performance_pump * elec_costs

def random_elec_costs_warmpump():
    return elec_costs_warmpump(seed_elec(),seed_cop_pump(), energy_input=INPUT_ENERGY_HEATING)    
elec_costs_warmpump = [random_elec_costs_warmpump() for _ in range(N)]
mean_wp = np.mean(elec_costs_warmpump)
sdt_wp = np.std(elec_costs_warmpump)
#########################
def total_costs(costs_heatpump, costs_coldpump, costs_warmpump):
    return costs_heatpump + costs_coldpump + costs_warmpump  

ELEC_COSTS_HEATPUMP_PARAM = dict(loc=mean_hp, scale=sdt_hp)
def seed_costs_hp():
    return np.random.normal(**ELEC_COSTS_HEATPUMP_PARAM )

ELEC_COSTS_COLDPUMP_PARAM = dict(loc=mean_cp, scale=sdt_cp)
def seed_costs_cp():
    return np.random.normal(**ELEC_COSTS_COLDPUMP_PARAM )

ELEC_COSTS_WARMPUMP_PARAM = dict(loc=mean_wp,scale=sdt_wp)
def seed_cost_wp():
    return np.random.normal(**ELEC_COSTS_WARMPUMP_PARAM )

def random_total_costs():
    return seed_costs_hp(), seed_costs_cp(), seed_cost_wp()
total_costs = [random_total_costs() for _ in range(N)]

print(total_costs)
#Plot = plt.hist(total_costs, bins=75, density=True)

1 个答案:

答案 0 :(得分:0)

太棒了,您的代码有了原型!

对代码结构和可读性的一些印象:

  • 最快的改进是将功能和脚本部分分开, 这样可以将您的算法拆分为简单的可测试块,并将计算控制在一个位置,然后绘制

  • 一些重复的代码可以使用自己的功能

  • 回报是坚持更广泛接受的命名约定(PEP8),这样人们就不会对样式感到惊讶,而可以将更多精力放在代码实质上。具体来说,通常将函数命名为小写下划线def do_somtheing():,而UPPERCASE则保留给常量使用。

需要能够运行您的代码以检查加速情况,请参阅上面的注释以了解缺少的内容。

更新有问题的更完整代码,并添加一些其他注释:

  • 让函数成为函数,不要将函数参数作为全局变量传递
  • 考虑将模型结构(您的方程式)与参数(固定输入和种子值)分离开来,它会在更长的时间内得到回报
  • 避免使用“幻数”,将其放入常量(例如866)中,或将其显式传递为args。

这里拒绝考虑:

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import triangular

N = 1_000_000

#def EnergyHP():
#    COP_Hp = triangular(4.0, 4.5, 5) # COP of heatpump
#    Ein = 866                       # Energy input heatpump 
#    return Ein * COP_Hp /(COP_Hp-1) # Calculation of Energy output of heatpump
#
#Eout = np.zeros(N)
#for i in range(N):
#    Eout[i] = EnergyHP()
#minval = np.min(Eout[np.nonzero(Eout)])
#maxval = np.max(Eout[np.nonzero(Eout)])
#Medi= np.median(Eout, axis=0)


def energy_output(coef_performance, energy_input):
    """Return energy output of heatpump given efficiency and input.

    Args:
       coef_performance - <description, units of measurement>
       energy_input - <description, units of measurement>
    """
    return energy_input * coef_performance / (coef_performance - 1) 

# you will use this variable again, so we put it into a function to recylce 
COP_DISTRIBUTION_PARAM = dict(left=4, mode=4.5, right=5)
def seed_cop():
    """Get random value for coef of performance."""
    return triangular(**COP_DISTRIBUTION_PARAM )

# rename it if it is a constant, pass as an argument is it is not
SOME_MEANINGFUL_CONSTANT = 866

def random_energy_output():
    return energy_output(seed_cop(), energy_input=SOME_MEANINGFUL_CONSTANT)    

# pure python list and metrics
energy_outputs = [random_energy_output() for _ in range(N)]
a = min(energy_outputs)
b = max(energy_outputs)
med = np.median(energy_outputs)

# above does this does not use numpy, but you can convert it to vector 
energy_outputs_np = np.array(energy_outputs)

# or you can construct np array directrly, this is a very readable way 
# make a vector or random arguements
cop_seeded = triangular(**COP_DISTRIBUTION_PARAM, size=N) 
# apply function
energy_outputs_np_2 = energy_output(cop_seeded, SOME_MEANINGFUL_CONSTANT)  

下一个紧迫的意图是编写HeatPump类,但我建议坚持 尽可能地使用功能-这通常会使我们思考课程 状态和方法更好。

我还发现参数的种子值可能不是独立的,在某些实验中,您可以从联合分布中提取它们。