Перебирая элементы ключа словаря вместо перечисления всех - PullRequest
0 голосов
/ 12 сентября 2018

Я работаю со словарями Python, чтобы определить некоторые переменные и параметры для математического режима.

Фрейм данных выглядит следующим образом:

    Service Bill Weight Zone    Resi    UPS FedEx   USPS    DHL
    1DEA           1       2    N      33.02    9999    9999    9999
    2DAM           2       2    N      33.02    9999    9999    9999

Я определил некоторые входные данные и переменную из этого следующим образом:

cost = {}
for carrier in carriers:
   for row in df.to_dict('records'):
            key = (row['Service'], row['Bill Weight'], 
            row['Zone'],row['Resi'], carrier)
            cost[key] = row[carrier]

services = df['Service'].unique().tolist()
weights = df['Bill Weight'].unique().tolist()
zones = df['Zone'].unique().tolist()
addresses = df['Resi'].unique().tolist()

Мои единственные допустимые комбинации для присвоения и стоимости должны быть:

 ['1DEA',1,2,'N','UPS']
 ['1DEA',1,2,'N','FedEx']
 ['1DEA',1,2,'N','USPS']
 ['1DEA',1,2,'N','DHL']
 ['2DAM',2,2,'N','UPS']
 ['2DAM',2,2,'N','FedEx']
 ['2DAM',2,2,'N','USPS']
 ['2DAM',2,2,'N','DHL']

И следующее с gurobi python, но на самом деле меня интересует только построение моих циклов через python, а не синтаксис gurobi:

Подход A:

assign = {}
for carrier in carriers:
   for row in df.to_dict('records'):
            key = (row['Service'], row['Bill Weight'], 
            row['Zone'],row['Resi'], carrier)
            cost[key] = row[carrier]
 obj = quicksum(cost[key]*assign[key] \
            for key in assign)

Теперь это прекрасно работает, чтобы убедиться, что переменные и параметры генерируются только из ключей словаря, а не из всех возможных комбинаций служб, весов, зон и адресов. Но это не будет работать, когда у меня есть определенные ограничения, как показано ниже:

  m.addConstrs((assign['1DEA', w, z, r, 'UPS']+assign['1DEA', w, z, r, 'USPS']+assign['1DEA', w, z, r, 'USPS 1C']==1\
          for i in clients for s in services for w in weights for z in zones for r in addresses),"C02")

Подход 2:

  assign = m.addVars(services, weights, zones, addresses, carriers, name = "Assign", vtype=GRB.BINARY)
  obj = quicksum(cost[s, w, z, r, l]*assign[ s, w, z, r, l] \
           for s in services for w in weights for z in zones for r in addresses for l in carriers)

Таким образом, я легко могу написать все свои ограничения, но он создаст все комбинации услуг, весов, зон, адресов, носителей, что делает мою модель неверной. например, ['2DAM', 1,2, 'N', 'UPS'] не является допустимой комбинацией.

Есть ли способ ограничить этот цикл для служб, весов, зон, адресов, носителей только комбинациями, которые определены в ключах словаря стоимости?

1 Ответ

0 голосов
/ 13 сентября 2018

Поскольку у вас уже есть данные в фрейме данных Pandas, вы можете использовать его функции для создания переменных и кратковременных связей. Создайте столбец с переменной решения, затем используйте 'groupby' и grb.quicksum, чтобы определить свои ограничения.

Во-первых, больше имен питонических колонок

df.columns = ['service', 'bill_weight', 'zone', 'resi', 'UPS', 'FedEx', 'USPS', 'DHL']

Затем преобразуйте кадр данных в удобную форму.

df1 = (df.set_index(['service', 'bill_weight', 'zone', 'resi']).
          rename_axis('carrier', axis=1).stack().to_frame('cost'))

Новый кадр данных будет иметь одну строку для каждой переменной.

                                          cost
service bill_weight zone resi carrier         
1DEA    1           2    N    UPS        33.02
                              FedEx    9999.00
                              USPS     9999.00
                              DHL      9999.00
2DAM    2           2    N    UPS        33.02
                              FedEx    9999.00
                              USPS     9999.00
                              DHL      9999.00

Вы можете создать переменную (и добавить их к цели с помощью.

df1['assign'] = [m.addVar(name=".".join(map(str, row.Index), 
                 obj=row.cost, vtype='B') 
                 for row in df1.itertuples()]
m.update()

Теперь кадр будет содержать переменные решения.

                                          cost                         assign
service bill_weight zone resi carrier                                        
1DEA    1           2    N    UPS        33.02    <gurobi.Var 1DEA.1.2.N.UPS>
                              FedEx    9999.00  <gurobi.Var 1DEA.1.2.N.FedEx>
                              USPS     9999.00   <gurobi.Var 1DEA.1.2.N.USPS>
                              DHL      9999.00    <gurobi.Var 1DEA.1.2.N.DHL>
2DAM    2           2    N    UPS        33.02    <gurobi.Var 2DAM.2.2.N.UPS>
                              FedEx    9999.00  <gurobi.Var 2DAM.2.2.N.FedEx>
                              USPS     9999.00   <gurobi.Var 2DAM.2.2.N.USPS>
                              DHL      9999.00    <gurobi.Var 2DAM.2.2.N.DHL>

Наконец, вы можете использовать pandas groupby, чтобы добавить ограничения, подобные тем, которые задаются в вашем вопросе

lhs = (df1.groupby(level=['service', 'bill_weight', 
                          'zone', 'resi']).assign.apply(grb.quicksum)
single_carrier_constrs = [m.addConstr(l == 1 for l in lhs]
...