如何在 pyomo 模型中提取索引变量信息并构建 Pandas Dataframe

How to extract Indexed Variable information in pyomo Model and build Pandas Dataframe

我正在尝试使用具有多个不同大小的索引变量的 Pyomo 来求解 MIP。我想做的是将变量的结果导出到数据框,以便我可以进一步分析这些结果。我的问题是我无法找到一种很好地自动化该过程的方法,因为索引变量的大小在每个模型模拟中都会发生变化。

这是我要创建的一些变量的示例:

import pyomo.environ as pyo

model = pyo.ConcreteModel()

model.T = pyo.RangeSet(0, 10)
model.Generators = pyo.Set(initialize=(['equip_1', 'equip_2']))
model.Storages = pyo.Set(initialize=(['storage_1', 'storage_2']))

model.var_1 = pyo.Var(model.T, model.Generators, domain=pyo.NonNegativeReals)
model.var_2 = pyo.Var(model.T, domain=pyo.NonNegativeReals)
model.var_3 = pyo.Var(model.T, model.Storages, domain=pyo.NonNegativeReals)
model.var_4 = pyo.Var(model.T, model.Generators, domain=pyo.Binary, initialize=0)

# constraints and objective function here, which I don't think are relevant for the question
.
.
.
SolverFactory('cbc').solve(model).write()

现在,我想创建数据框 model.T 作为索引和变量名加上 model.Generator 或 model.Storages 作为 multiIndex 列(我假设它会有成为一个 multiIndex,但也许不是)。下面显示了我希望它看起来如何的粗略示例:

  |      Var_1      | Var_2 |        Var_3        |      Var_4
  | equip_1 equip_2 | None  | storage_1 storage_2 | equip_1 equip_2
m |   0    |   0    |   0   |     0     |   0     |   0    |   1  
o |   1    |   1    |   1   |     1     |   1     |   1    |   0  
d |   2    |   2    |   2   |     2     |   2     |   0    |   1  
e |   3    |   3    |   3   |     3     |   3     |   1    |   0  
l |   4    |   4    |   4   |     4     |   4     |   0    |   1  
. |   5    |   5    |   5   |     5     |   5     |   1    |   0  
T |   6    |   6    |   6   |     6     |   6     |   0    |   1  

显示的值只是示例。此外,model.T 不必是索引,因为它与使用数据框创建的标准索引相关联。

我一直在绞尽脑汁试图让它工作,但我正在努力在 pyomo 中找到正确的语法。我觉得必须有一种简单的方法来提取这些数据,因为我不是第一个尝试这样做的人,但我已经在互联网上搜索了这个特定问题但无济于事。我遇到的问题是尝试从变量结果中提取变量索引(model.T 和模型。Generators/Storages)以简单的循环或矢量化方式。

拜托!任何建议将不胜感激。如果我还不够清楚,请告诉我。

完全可行...

首先,让我们介绍一个基本示例,说明如何(在一般意义上)在求解后从 pyomo 中获取结果。回想一下,求解器完成后,变量的最优值将加载到模型变量中。这是一个简单的示例,显示了从变量中提取数据的 3 种方法。还有很多其他的,这取决于需要做什么。值得注意的是,这里的最后一种方法,转换为字典,是过渡到 pandas 的关键,因为我们都知道 pandas 喜欢字典。

# extracting values 1
import pyomo.environ as pyo
import pandas as pd

m = pyo.ConcreteModel('value extraction')

m.S = pyo.Set(initialize=range(3))
m.X = pyo.Var(m.S, initialize={0:-1, 1:2.5, 2:12})   # just initializing to simulate solving

# show all of X
m.X.display()

# iterate through and show values
print()
for s in m.S:
    print(f'for index {s} X[{s}] is: {m.X[s].value}')

# dump into a dictionary.... an entry point for pandas!
print()
print(m.X.extract_values())

# make a pd.Series indexed by the index set(s)
print()
x_vals = pd.Series(m.X.extract_values(), name=m.X.name)
print(x_vals)

产量:

X : Size=3, Index=S
    Key : Lower : Value : Upper : Fixed : Stale : Domain
      0 :  None :    -1 :  None : False : False :  Reals
      1 :  None :   2.5 :  None : False : False :  Reals
      2 :  None :    12 :  None : False : False :  Reals

for index 0 X[0] is: -1
for index 1 X[1] is: 2.5
for index 2 X[2] is: 12

{0: -1, 1: 2.5, 2: 12}

0    -1.0
1     2.5
2    12.0
Name: X, dtype: float64

第二部分:

好的,接下来构建一个更全面的解决方案来应对将整个结果混合到多索引数据帧中的挑战。值得注意的是,我初始化了这里变量的值来模拟求解。通常,变量不需要。

所以这建立在之前的概念之上,即 pd.Series 只是一个由索引数组索引的数组,可以从字典构造。我不是最聪明的 pandas 操作员,所以可能有一些方法可以组合一些 pd 命令,但谁在乎呢?这是一步一步的,如果你正在处理来自优化的变量,它不会太大,所以不需要调整。

# extracting values 2
import pyomo.environ as pyo
import pandas as pd

model = pyo.ConcreteModel()

model.T = pyo.RangeSet(0, 10)
model.Generators = pyo.Set(initialize=(['equip_1', 'equip_2']))
model.Storages = pyo.Set(initialize=(['storage_1', 'storage_2']))

model.var_1 = pyo.Var(model.T, model.Generators, initialize=1.5, domain=pyo.NonNegativeReals)
model.var_2 = pyo.Var(model.T, initialize=2.5, domain=pyo.NonNegativeReals)
model.var_3 = pyo.Var(model.T, model.Storages, initialize=3.5, domain=pyo.NonNegativeReals)
model.var_4 = pyo.Var(model.T, model.Generators, domain=pyo.Binary, initialize=0)

#model.display()

# let's convert each var to a pandas series, indexed by model.T...

# get all the variables (assuming the fuller model will have constraints, params, etc.)
model_vars = model.component_map(ctype=pyo.Var)


serieses = []   # collection to hold the converted "serieses"
for k in model_vars.keys():   # this is a map of {name:pyo.Var}
    v = model_vars[k]

    # make a pd.Series from each    
    s = pd.Series(v.extract_values(), index=v.extract_values().keys())

    # if the series is multi-indexed we need to unstack it...
    if type(s.index[0]) == tuple:  # it is multi-indexed
        s = s.unstack(level=1)
    else:
        s = pd.DataFrame(s)         # force transition from Series -> df
    #print(s)

    # multi-index the columns
    s.columns = pd.MultiIndex.from_tuples([(k, t) for t in s.columns])

    serieses.append(s)

df = pd.concat(serieses, axis=1)
print(df)

产量

     var_1         var_2     var_3             var_4        
   equip_1 equip_2     0 storage_1 storage_2 equip_1 equip_2
0      1.5     1.5   2.5       3.5       3.5       0       0
1      1.5     1.5   2.5       3.5       3.5       0       0
2      1.5     1.5   2.5       3.5       3.5       0       0
3      1.5     1.5   2.5       3.5       3.5       0       0
4      1.5     1.5   2.5       3.5       3.5       0       0
5      1.5     1.5   2.5       3.5       3.5       0       0
6      1.5     1.5   2.5       3.5       3.5       0       0
7      1.5     1.5   2.5       3.5       3.5       0       0
8      1.5     1.5   2.5       3.5       3.5       0       0
9      1.5     1.5   2.5       3.5       3.5       0       0
10     1.5     1.5   2.5       3.5       3.5       0       0