# Ecommerce Optimization Code

### Formulation of a promotion optimization problem:

\begin{equation}
\begin{split}
\max~ & \sum_{j=1}^9\sum_{i=1}^n p_{ij}x_{ij}d_{ij} + \sum_{i=1}^n y_{i} \bar{p}_{i} \bar{d}_{i} \\
s.t. ~& \sum_{j=1}^9x_{ij}\leq 1, ~ i = 1,...,n\\
& \sum _{j=1}^{9}\sum _{i=1}^{n} p_{ij} x_{ij} \leq c\\
& y_{i}= 1-\sum_{j=1}^9 x_{ij}, ~ i = 1,...,n \\
& x_{ij} \in \{0,1\}
\end{split}
\end{equation}


c = User defined constant to limit number of promotion <br>
n = number of products <br>
j = different prices for each product <br>

d<sub>ij</sub> = Demand for product i at j price <br>
x<sub>ij</sub> = Binary Selection, if x<sup>ij</sup> = 1 means select at product i and price j, 0 if otherwise  <br>
p<sub>ij</sub> = j<sup>th</sup> Price for product i 


```python
import csv
from gurobipy import *
import pandas as pd
import numpy as np
file = open('OptimizerInput.csv')
csvreader = csv.reader(file)
```


```python
header = []
header = next(csvreader)
header
```




    ['id', 'product', 'price', 'demand']




```python
rows = []
for row in csvreader:
        rows.append(row)
print(rows[1:12])

print(type(rows))
```

    [['1', '10 COLOUR SPACEBOY PEN', '0.821111111111111', '73.172647756993'], ['1', '10 COLOUR SPACEBOY PEN', '0.922222222222222', '54.126225061631'], ['1', '10 COLOUR SPACEBOY PEN', '1.02333333333333', '45.3624739654545'], ['1', '10 COLOUR SPACEBOY PEN', '1.12444444444444', '38.5223929109929'], ['1', '10 COLOUR SPACEBOY PEN', '1.22555555555556', '31.2091023340694'], ['1', '10 COLOUR SPACEBOY PEN', '1.32666666666667', '23.4628943416618'], ['1', '10 COLOUR SPACEBOY PEN', '1.42777777777778', '15.7804425707803'], ['1', '10 COLOUR SPACEBOY PEN', '1.52888888888889', '8.54204136171192'], ['1', '10 COLOUR SPACEBOY PEN', '1.63', '1.9488612255597'], ['2', '12 COLOURED PARTY BALLOONS', '0.65', '13.9609375'], ['2', '12 COLOURED PARTY BALLOONS', '0.716666666666667', '12.5208333333333']]
    <class 'list'>



```python
#We have 2395 products and 10 different price
#We will have 2 different set. 1 discounted set with 9 prices and 1 original set with 1 price
numofproducts = 2395

discprice = np.zeros((numofproducts, 9))
discrevenue = np.zeros((numofproducts, 9))

origprice = np.zeros(numofproducts)
origrevenue = np.zeros(numofproducts)

oneprice = np.ones(numofproducts)
prod = []


for row in range(0, len(rows), 10):
    pid = (int(rows[row][0]) - 1)
    for col in range(10):
        if col == 9:
            origprice[pid] = float(rows[row + col][2])
            origrevenue[pid] = float(rows[row + col][2]) * float(rows[row + col][3])
            prod.append(rows[row + col][1])
        else:
            discprice[pid,col] = float(rows[row + col][2])
            discrevenue[pid,col] = float(rows[row + col][2]) * float(rows[row + col][3])
            
```


```python
print(discprice.shape)
print(oneprice.shape)
print(discrevenue.shape)
print(origprice.shape)
print(origrevenue.shape)
```

    (2395, 9)
    (2395,)
    (2395, 9)
    (2395,)
    (2395,)



```python
#print(oneprice)
#print(discprice)
#print(origprice)
#print(revenue)
#print(prod)
```


```python
I, J = discrevenue.shape
print(I, J)
```

    2395 9



```python
tp = Model("promotion")

# User Defined Parameters, In this example we set c=4000
c = 268

# x variable is binary hence added vtype=GRB.BINARY
x = tp.addVars(I, J, vtype=GRB.BINARY)

# Set objective
tp.setObjective(quicksum(discrevenue[i,j]*x[i,j] for i in range(I) for j in range(J)) + quicksum(quicksum(1-x[i,j] for j in range(J))*origrevenue[i] for i in range(I)), GRB.MAXIMIZE)

# Add 1 chosen price constraints: 
tp.addConstrs((quicksum(x[i,j] for j in range(J)) <= oneprice[i] for i in range(I)), "oneprice")

# Add sum of all the selected prices is equal to c constraints: 
tp.addConstrs((quicksum(discprice[i,j]*x[i,j] for i in range(I) for j in range(J))) <= c for j in range(J))


# Solving the model
tp.optimize()

# Show Result
print('\nObj:', tp.objVal)
```

    Set parameter Username
    Academic license - for non-commercial use only - expires 2022-01-10
    Gurobi Optimizer version 9.5.0 build v9.5.0rc5 (win64)
    Thread count: 2 physical cores, 4 logical processors, using up to 4 threads
    Optimize a model with 2404 rows, 21555 columns and 215550 nonzeros
    Model fingerprint: 0xbb744248
    Variable types: 0 continuous, 21555 integer (21555 binary)
    Coefficient statistics:
      Matrix range     [4e-02, 3e+02]
      Objective range  [2e-14, 4e+03]
      Bounds range     [1e+00, 1e+00]
      RHS range        [1e+00, 3e+02]
    Found heuristic solution: objective 476123.43899
    Presolve removed 1622 rows and 16866 columns
    Presolve time: 0.20s
    Presolved: 782 rows, 4689 columns, 7920 nonzeros
    Variable types: 0 continuous, 4689 integer (4689 binary)
    Found heuristic solution: objective 486371.76507
    
    Root relaxation: objective 5.490079e+05, 1172 iterations, 0.08 seconds (0.09 work units)
    
        Nodes    |    Current Node    |     Objective Bounds      |     Work
     Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
    
         0     0 549007.912    0    1 486371.765 549007.912  12.9%     -    0s
    H    0     0                    548398.13901 549007.912  0.11%     -    0s
    H    0     0                    548956.46737 549007.912  0.01%     -    0s
    
    Explored 1 nodes (1172 simplex iterations) in 0.36 seconds (0.26 work units)
    Thread count was 4 (of 4 available processors)
    
    Solution count 4: 548956 548398 486372 476123 
    
    Optimal solution found (tolerance 1.00e-04)
    Best objective 5.489564673688e+05, best bound 5.490079120080e+05, gap 0.0094%
    
    Obj: 548956.467368834



```python
result = np.zeros(I*J)
idx = 0
print(result.shape)

for i in range(I):
    for j in range(J):
        result[idx] = int(x[i,j].x)
        idx = idx + 1
```

    (21555,)



```python
print(type(result))
print(result[1:10])
#np.savetxt("promoResult.csv", result, delimiter=",")
```

    <class 'numpy.ndarray'>
    [0. 0. 0. 0. 0. 0. 0. 0. 0.]



```python
#Process the optimized solution into more user friendly rather than 0s and 1s

idx = 0
discitem = []
discpid = []
optprice = []
optrev = []

origitem =[]
origpid = []
pricemax = []
origrev = []

for row in range(0, len(result), 9):
    for col in range(9):
        #print(result[row+col])
        if result[row+col] == 1:
            discitem.append(prod[idx])
            discpid.append(idx+1)
            optprice.append(discprice[idx,col])
            optrev.append(discrevenue[idx,col])
            break
        #Keep it as original price if no discount 
        if col == 8:
            origitem.append(prod[idx])
            origpid.append(idx+1)
            pricemax.append(origprice[idx])
            origrev.append(origrevenue[idx])
        
    idx = idx + 1
```


```python
d = {'id':discpid, 'Product':discitem, 'Optimized Price': optprice, 'Optimized Revenue': optrev}
df = pd.DataFrame(d)
df.to_csv('DiscountResult.csv', index=False)
```


```python
d2 = {'id':origpid, 'Product':origitem, 'Original Price': pricemax, 'Original Revenue': origrev}
df2 = pd.DataFrame(d2)
df2.to_csv('NonDiscountItem.csv', index=False)
```


```python
print(c)
sum(optrev) + sum(origrev)
```

    268





    136770.41537521078




```python

```

dacdefce47c09db5324e1a1bc68cbbece9aa4e07
ipynb
Jupyter Notebook
Code/promotionOpt_code_Final.ipynb
Chen-XueWen/PromotionOptimization
ca6c90902270b93637f91a58333b29566cbb2fc8
MIT
Code/promotionOpt_code_Final.ipynb
Chen-XueWen/PromotionOptimization
ca6c90902270b93637f91a58333b29566cbb2fc8
MIT
Code/promotionOpt_code_Final.ipynb
Chen-XueWen/PromotionOptimization
ca6c90902270b93637f91a58333b29566cbb2fc8
MIT
Qwen/Qwen-72B
1. YES
2. YES