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

Advertising_campaign_colab.ipynb Open In Colab Kaggle Gradient Open In SageMaker Studio Lab Hits

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:

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

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

  3. 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

7. Retrieve solution as a pandas dataframe