# Optimization of an advertising campaign for launching a new product on the market¶

Description: The modern world is unthinkable without advertising. Advertising is the engine of progress. When developing and conducting advertising campaigns, companies strive to maximize the effectiveness of their advertising dollars. In this case, the effectiveness of advertising is measured by the degree to which the campaign goals are achieved. The main goals of an advertising campaign can be:

Increase in sales;

Attraction of new clients;

Launch of a new product on the market;

Increasing/maintaining market share or profit level.

When developing and planning an advertising campaigns, it is necessary to take into account many factors that influence the final effectiveness of advertising, namely:

Characteristics of advertising channels (availability, cost, etc.);

Structure and composition of the target audience of each advertising channel;

Conversion rates for various advertising channels;

Limitation of the budget allocated for advertising;

Cost of 1 impression/click;

Localization of the target audience;

The intensity of advertising displays during the day, week;

Marketing activity of competitors;

Mathematical optimization allows you to synchronize all the specified factors and limitations of advertising channels with each other, and determine, among many options, the most effective (from the point of view of campaign goals) plan for investing in advertising.

Tags: Marketing, Advertisement, Deterministic model, Piecewise-linear, mip, ampl-only, cbc

Notebook author: Mikhail Riabtsev <mail@solverytic.com>

## 1. Problem statement¶

Let’s start by downloading the necessary extensions and libraries

```
# Install dependencies
%pip install -q amplpy pandas
import pandas as pd # Loading panda to work with pandas.DataFrame objects (https://pandas.pydata.org/)
import numpy as np # Loading numpy to perform multidimensional calculations numpy.matrix (https://numpy.org/)
import matplotlib.pyplot as plt # Loading library for creating static, animated, and interactive visualizations (https://matplotlib.org/)
```

```
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook
ampl = ampl_notebook(
modules=["cbc"], # modules to install
license_uuid="default", # license to use
) # instantiate AMPL object and register magics
```

The company plans to launch a new product on the market of 2 large cities (City1, City2). To inform customers about the new offer, the company is considering the possibility of placing the following types of advertisements:

```
broadcasters = pd.DataFrame(
[
["TV: сhannel#1_time 0PM-5PM", "minutes", 1000, 18000, 3],
["TV: сhannel#1_time 5PM-10PM", "minutes", 1200, 25000, 3],
["Magazine A: cover", "pages", 200, 7000, 6],
["Magazine B: inner page", "pages", 220, 8000, 6],
["Radio A: time 5PM-10PM ", "minutes", 200, 11000, 5],
["Radio B: time 5PM-10PM ", "minutes", 250, 15000, 5],
["Subway: red line", "page to week", 250, 15000, 4],
["WebSite1", "weeks", 500, 10000, 15],
["WebSite2", "weeks", 400, 7000, 14],
["Billboard: 1place per week", "place", 400, 3000, 3],
],
columns=[
"Type of advertising",
"Unit",
"Cost 1 unit",
"Audience, person",
"Conversion, impressions to clicks, %",
],
)
display(broadcasters)
```

Type of advertising | Unit | Cost 1 unit | Audience, person | Conversion, impressions to clicks, % | |
---|---|---|---|---|---|

0 | TV: сhannel#1_time 0PM-5PM | minutes | 1000 | 18000 | 3 |

1 | TV: сhannel#1_time 5PM-10PM | minutes | 1200 | 25000 | 3 |

2 | Magazine A: cover | pages | 200 | 7000 | 6 |

3 | Magazine B: inner page | pages | 220 | 8000 | 6 |

4 | Radio A: time 5PM-10PM | minutes | 200 | 11000 | 5 |

5 | Radio B: time 5PM-10PM | minutes | 250 | 15000 | 5 |

6 | Subway: red line | page to week | 250 | 15000 | 4 |

7 | WebSite1 | weeks | 500 | 10000 | 15 |

8 | WebSite2 | weeks | 400 | 7000 | 14 |

9 | Billboard: 1place per week | place | 400 | 3000 | 3 |

*Table.1. Main characteristics of advertisements.*

The audience of advertising consists of the following user segments, % of the total audience:

```
segmentation_of_audience = pd.DataFrame(
[
["TV: сhannel#1_time 0PM-5PM", 7, 25, 25, 27, 16],
["TV: сhannel#1_time 5PM-10PM", 5, 18, 32, 30, 15],
["Magazine A: cover", 1, 5, 35, 45, 14],
["Magazine B: inner page", 5, 18, 32, 30, 15],
["Radio A: time 5PM-10PM ", 15, 32, 18, 30, 5],
["Radio B: time 5PM-10PM ", 14, 45, 35, 5, 1],
["Subway: red line", 16, 27, 25, 25, 7],
["WebSite1", 5, 18, 32, 30, 15],
["WebSite2", 15, 32, 18, 30, 5],
["Billboard: 1place per week", 5, 18, 32, 30, 15],
],
columns=[
"Type of advertising",
"14-17 years old",
"17-25 years old",
"25-40 years old",
"40-60 years old",
"over 60",
],
)
display(segmentation_of_audience)
```

Type of advertising | 14-17 years old | 17-25 years old | 25-40 years old | 40-60 years old | over 60 | |
---|---|---|---|---|---|---|

0 | TV: сhannel#1_time 0PM-5PM | 7 | 25 | 25 | 27 | 16 |

1 | TV: сhannel#1_time 5PM-10PM | 5 | 18 | 32 | 30 | 15 |

2 | Magazine A: cover | 1 | 5 | 35 | 45 | 14 |

3 | Magazine B: inner page | 5 | 18 | 32 | 30 | 15 |

4 | Radio A: time 5PM-10PM | 15 | 32 | 18 | 30 | 5 |

5 | Radio B: time 5PM-10PM | 14 | 45 | 35 | 5 | 1 |

6 | Subway: red line | 16 | 27 | 25 | 25 | 7 |

7 | WebSite1 | 5 | 18 | 32 | 30 | 15 |

8 | WebSite2 | 15 | 32 | 18 | 30 | 5 |

9 | Billboard: 1place per week | 5 | 18 | 32 | 30 | 15 |

*Table.2. Segment composition of advertising audiences, %.*

In addition, the audience of advertising media is distributed differently in cities (City1, City2), %:

```
distrib_audience_by_city = pd.DataFrame(
[
["TV: сhannel#1_time 0PM-5PM", 60, 40],
["TV: сhannel#1_time 5PM-10PM", 70, 30],
["Magazine A: cover", 0, 100],
["Magazine B: inner page", 80, 20],
["Radio A: time 5PM-10PM ", 80, 20],
["Radio B: time 5PM-10PM ", 20, 80],
["Subway: red line", 0, 100],
["WebSite1", 75, 25],
["WebSite2", 80, 20],
["Billboard: 1place per week", 0, 100],
],
columns=["Type of advertising", "City#1", "City#2"],
)
display(distrib_audience_by_city)
```

Type of advertising | City#1 | City#2 | |
---|---|---|---|

0 | TV: сhannel#1_time 0PM-5PM | 60 | 40 |

1 | TV: сhannel#1_time 5PM-10PM | 70 | 30 |

2 | Magazine A: cover | 0 | 100 |

3 | Magazine B: inner page | 80 | 20 |

4 | Radio A: time 5PM-10PM | 80 | 20 |

5 | Radio B: time 5PM-10PM | 20 | 80 |

6 | Subway: red line | 0 | 100 |

7 | WebSite1 | 75 | 25 |

8 | WebSite2 | 80 | 20 |

9 | Billboard: 1place per week | 0 | 100 |

*Table.3. Distribution of the audience of broadcasters by city, %*

When developing an advertising campaign, it is necessary to take into account that the effectiveness of advertisements decreases as the number of views increases. So the first 10 views have the one indicated in Table 1. conversion rate, while the next 10 impressions are 10% less effective, and impressions >20 are 40% less effective.

Data on the reduction rate of advertising effectiveness are given in the following table, %:

```
reduce = pd.DataFrame(
[
["TV: сhannel#1_time 0PM-5PM", 10, 40],
["TV: сhannel#1_time 5PM-10PM", 10, 40],
["Magazine A: cover", 5, 20],
["Magazine B: inner page", 5, 20],
["Radio A: time 5PM-10PM ", 20, 40],
["Radio B: time 5PM-10PM ", 20, 40],
["Subway: red line", 5, 40],
["WebSite1", 10, 40],
["WebSite2", 10, 40],
["Billboard: 1place per week", 10, 20],
],
columns=["Type of advertising", "10-20 views", ">20 views"],
)
display(reduce)
```

Type of advertising | 10-20 views | >20 views | |
---|---|---|---|

0 | TV: сhannel#1_time 0PM-5PM | 10 | 40 |

1 | TV: сhannel#1_time 5PM-10PM | 10 | 40 |

2 | Magazine A: cover | 5 | 20 |

3 | Magazine B: inner page | 5 | 20 |

4 | Radio A: time 5PM-10PM | 20 | 40 |

5 | Radio B: time 5PM-10PM | 20 | 40 |

6 | Subway: red line | 5 | 40 |

7 | WebSite1 | 10 | 40 |

8 | WebSite2 | 10 | 40 |

9 | Billboard: 1place per week | 10 | 20 |

*Table.4. Indicators of decrease in the effectiveness of advertisements, %.*

One of the client’s requirements is the need to allocate at least 20% of the budget to TV advertising.

**Optimization goals**

It is necessary to distribute the advertising budget in such a way as to maximize the following indicators:

Inform the maximum number of people about the release of a new product;

Inform the maximum number of people from the target audience (persons 25-40 years old) about the release of a new product;

Maximize the total number of conversions (purchases) of the target audience.

## 2. Mathematical formulation of the problem¶

Let’s start by describing **index sets**

$i$ - list of available types of advertising, for $i$ = “TV: сhannel#1_time 0PM-5PM”,…, I.

$i’$ - list of TV types of advertising, $i’ \in$ I. $i’$ = ‘TV: сhannel#1_time 0PM-5PM’, ‘TV: сhannel#1_time 5PM-10PM’.

$j$ - segments of advertising audiences, for $j$ = “14-17 years old”,…, J.

$j’$ - types of target segment. $j’$ = “25-40”.

$k$ - list of cities for broadcasting advertisements, for $k$ = “City#1”,… K.

$A_i$ - number of people in the advertising $i$ audience (Table 1).

$B_i$ - the cost of displaying 1 unit of advertising $i$ (Table 1).

$C_i$ - the conversion rate of advertising $i$ into targeted customer actions (Table 1).

**Parameters**

$D_{ij}$ - the share of various target groups ($j$) in the total number of advertising audiences ($i$) (Table 2).

$F_{ij}$ - distribution of advertising audience ($i$) between cities ($j$) (Table 3).

$M$ - available advertising budget, $.

$R_i$ - The coefficient of decrease in advertising effectiveness $i$ with increasing number impressions (Table 4).

To describe the conditions for changing the effectiveness of advertisements after n number of impressions, we use the concept of Piecewise-Linear Programing. Thus, the objective function of the model is to maximize the number of targeted actions of the ‘25-40’ audience as a result of advertising impressions.

**The variables in this case are**

$x_i$: Number of advertisement broadcasts $i$, for $i$ = “TV: сhannel#1_time 0PM-5PM”,…, N.

**Objective function is**

max $ \sum_i^I \sum_k^K \sum_{j’}^J X_i * A_i * D_{ij} * F_{ij} * R_i$ (goal 3)

**The constraints are**

s.t. $ \sum_i^I X_i * B_i <= M $

s.t. $ \sum_{i’}^I X_{i’} * B_{i’} <= M * 0.2$

## 3. AMPL formulation of the model¶

Use %%ampl_eval to evaluate AMPL commands and declarations

```
%%ampl_eval
reset;
### SETS
set ADVERTISEMENT; # List of all available advertisements
set AUDIENCE; # Broadcaster's audience segments
set SET within AUDIENCE; # Types of target segments
set CITY; # Target cities of the advertising campaign
### PARAMETERS
param Cost {ADVERTISEMENT} >= 0; # Cost of 1 unit of advertising broadcasting for each type of advertising
param Auditoria {ADVERTISEMENT} >= 0; # Internal number of consumer groups for each advertisement
param Conversion {ADVERTISEMENT} >= 0; # Conversion rate of advertisements into purchases
param TargetAudience {ADVERTISEMENT, AUDIENCE} >= 0; # Segment composition of advertising audiences
param City {ADVERTISEMENT, CITY} >= 0; # Distribution of the audience of broadcasters by city
param Money; # Advertising campaign budget, USD
### Indicators of reduction in advertising effectiveness (we use Piecewise-Linear Programing to describe this conception)
# Number of efficiency ranges
param npiece integer >= 1;
# Ad effectiveness value in each range
param rate {ad in ADVERTISEMENT, p in 1..npiece} >= 0;
param limit {p in 1..npiece-1} > if p=1 then 0 else limit [p-1];
### VARIABLES
var X {ADVERTISEMENT} >= 0; # Number/duration of each type of advertisement
### OBJECTIVE
maximize TotalAudienceInform: # 1. Inform the maximum number of people about the release of a new product
sum {ad in ADVERTISEMENT, c in CITY, a in AUDIENCE} <<{p in 1..npiece-1} limit[p];
{p in 1..npiece} rate[ad,p]>> X[ad] * Auditoria[ad] * City[ad,c];
maximize TargetAudienceInform: # Inform the maximum number of people from the target audience (persons 25-40 years old) about the release of a new product
sum {ad in ADVERTISEMENT, c in CITY, a in AUDIENCE: a in SET} <<{p in 1..npiece-1} limit[p];
{p in 1..npiece} rate[ad,p]>> X[ad] * Auditoria[ad] * TargetAudience[ad,a] * City[ad,c];
maximize TargetAudienceConversion: # 3. Maximize the total number of conversions (purchases) of the target audience
sum {ad in ADVERTISEMENT, c in CITY, a in AUDIENCE: a in SET} <<{p in 1..npiece-1} limit[p];
{p in 1..npiece} rate[ad,p]>> X[ad] * Auditoria[ad] * TargetAudience[ad,a] * Conversion[ad] * City[ad,c];
### CONSTRAINTS
subject to Budget: # Limit the cost of all advertisements to the allocated budget
sum{ad in ADVERTISEMENT} X[ad] * Cost[ad]<= Money;
subject to Budget_TV: # TV advertising costs must be at least 20% of the allocated budget
sum{ad in ADVERTISEMENT: ad in {'TV: сhannel#1_time 0PM-5PM', 'TV: сhannel#1_time 5PM-10PM'}} X[ad] * Cost[ad] >= Money * 0.2;
```

## 4. Load data¶

```
%%writefile Advertisement.dat
param Money = 50000;
param: ADVERTISEMENT: Cost Auditoria Conversion:=
'TV: сhannel#1_time 0PM-5PM' 1000 18000 0.03
'TV: сhannel#1_time 5PM-10PM' 1200 25000 0.03
'Magazine A: cover' 200 7000 0.06
'Magazine B: inner page' 220 8000 0.06
'Radio A: time 5PM-10PM' 200 11000 0.05
'Radio B: time 5PM-10PM' 250 15000 0.05
'Subway: red line' 250 15000 0.04
'WebSite1' 500 10000 0.15
'WebSite2' 400 7000 0.14
'Billboard: 1place per week' 400 3000 0.03 ;
set AUDIENCE = '14-17', '17-25', '25-40', '40-60', '>60';
param TargetAudience: '14-17' '17-25' '25-40' '40-60' '>60':=
'TV: сhannel#1_time 0PM-5PM' 0.07 0.25 0.25 0.27 0.16
'TV: сhannel#1_time 5PM-10PM' 0.05 0.18 0.32 0.30 0.15
'Magazine A: cover' 0.01 0.05 0.35 0.45 0.14
'Magazine B: inner page' 0.05 0.18 0.32 0.30 0.15
'Radio A: time 5PM-10PM' 0.15 0.32 0.18 0.30 0.05
'Radio B: time 5PM-10PM' 0.14 0.45 0.35 0.05 0.01
'Subway: red line' 0.16 0.27 0.25 0.25 0.07
'WebSite1' 0.05 0.18 0.32 0.30 0.15
'WebSite2' 0.15 0.32 0.18 0.30 0.05
'Billboard: 1place per week' 0.05 0.18 0.32 0.30 0.15 ;
set SET='25-40';
set CITY = City1, City2;
param City: City1 City2:=
'TV: сhannel#1_time 0PM-5PM' 0.6 0.4
'TV: сhannel#1_time 5PM-10PM' 0.7 0.3
'Magazine A: cover' 0 1
'Magazine B: inner page' 0.8 0.2
'Radio A: time 5PM-10PM' 0.8 0.2
'Radio B: time 5PM-10PM' 0.2 0.8
'Subway: red line' 0 1
'WebSite1' 0.75 0.25
'WebSite2' 0.8 0.2
'Billboard: 1place per week' 0 1 ;
param npiece default 3;
param rate: 1 2 3:=
'TV: сhannel#1_time 0PM-5PM' 1 0.9 0.6
'TV: сhannel#1_time 5PM-10PM' 1 0.9 0.6
'Magazine A: cover' 1 0.95 0.8
'Magazine B: inner page' 1 0.95 0.8
'Radio A: time 5PM-10PM' 1 0.80 0.6
'Radio B: time 5PM-10PM' 1 0.80 0.6
'Subway: red line' 1 0.95 0.6
'WebSite1' 1 0.9 0.6
'WebSite2' 1 0.9 0.6
'Billboard: 1place per week' 1 0.9 0.8 ;
param limit:=
1 10
2 20;
```

## 5. Solve problem¶

Use %%ampl_eval to evaluate AMPL commands and declarations

```
%%ampl_eval
reset data; # Clearing the data
data Advertisement.dat; # Loading data from a file
option solver cbc; # Choosing a solver
# Defining Output Settings
option show_stats 1; # (1) Show statistical information about the size of the problem. Default 0 (statistics are not displayed)
option display_1col 0; # Data Display Settings
option omit_zero_rows 1; # Hide rows with 0 values. Default (0)
# Selecting an Objective Function (choose one of)
#objective TotalAudienceInform; # goal 1
#objective TargetAudienceInform; # goal 2
objective TargetAudienceConversion; # goal 3
solve; # Solve the model (Objective function TotalAudienceInform)
```

```
Substitution eliminates 10 variables.
10 piecewise-linear terms replaced by 30 variables and 10 constraints.
Adjusted problem:
30 variables, all linear
2 constraints, all linear; 36 nonzeros
2 inequality constraints
1 linear objective; 30 nonzeros.
cbc 2.10.10:cbc 2.10.10: optimal solution; objective 32164.7
0 simplex iterations
```

## 6. Display the solution¶

Use %%ampl_eval

```
%%ampl_eval
display X, # Show the value of the variables for the resulting solution
{ad in ADVERTISEMENT} X[ad] * Cost[ad], # Show the budget spent on each type of advertising
{c in CITY, ad in ADVERTISEMENT, a in AUDIENCE} X[ad] * Auditoria[ad] * TargetAudience[ad,a] * City[ad,c], # Number of views
sum {c in CITY, ad in ADVERTISEMENT, a in AUDIENCE} X[ad] * Auditoria[ad] * TargetAudience[ad,a] * City[ad,c], # Total number of views
{c in CITY, ad in ADVERTISEMENT, a in AUDIENCE} X[ad] * Auditoria[ad] * TargetAudience[ad,a] * Conversion[ad] * City[ad,c], # Number of conversions
sum {c in CITY, ad in ADVERTISEMENT, a in AUDIENCE} X[ad] * Auditoria[ad] * TargetAudience[ad,a] * Conversion[ad] * City[ad,c]; # Total number of conversions
```

```
ampl.var["X"].get_values().to_pandas()
```

X.val | |
---|---|

Billboard: 1place per week | 0.000000 |

Magazine A: cover | 0.000000 |

Magazine B: inner page | 0.000000 |

Radio A: time 5PM-10PM | 20.000000 |

Radio B: time 5PM-10PM | 0.000000 |

Subway: red line | 0.000000 |

TV: сhannel#1_time 0PM-5PM | 0.000000 |

TV: сhannel#1_time 5PM-10PM | 8.333333 |

WebSite1 | 64.000000 |

WebSite2 | 10.000000 |