Prescriptive Analytics: Making Optimal Decisions with Data
Overview
Prescriptive analytics answers the most actionable question: "What should we do?" Moving beyond describing what happened, diagnosing why it happened, or predicting what will happen, prescriptive analytics uses optimization and simulation to recommend specific actions. It transforms predictions into decisions, uncertainty into opportunity, and data into strategy.
The Four Types of Analytics — A Comparison
Type | Question Answered | Difficulty | Value |
|---|---|---|---|
Descriptive | "What happened?" | Low | Understand past |
Diagnostic | "Why did it happen?" | Medium | Find root causes |
Predictive | "What will happen?" | High | Anticipate future |
Prescriptive | "What should we do?" | Very High | Optimize decisions |
What is Prescriptive Analytics?
Prescriptive analytics is the process of using mathematical optimization, simulation, and advanced analytics to determine the best course of action given a business objective, constraints, and available resources. It combines predictive models with optimization algorithms to recommend decisions that maximize or minimize specific outcomes.
Key Characteristics
Action-Oriented: Recommends specific decisions, not just insights
Optimization-Driven: Seeks the optimal or near-optimal solution
Constraint-Aware: Respects business and resource limitations
Risk-Adjusted: Incorporates uncertainty in recommendations
Optimization Problem Structure
Every prescriptive model consists of four core components:
Component | Description | Example |
|---|---|---|
Decision Variables | What we control | Units to produce per product |
Objective Function | What to maximize or minimize | Maximize total profit |
Constraints | Limitations and requirements | Labor ≤ 1000 hrs, Budget ≤ $50k |
Parameters | Fixed or uncertain inputs | Cost per unit, demand estimates |
Linear Programming
Standard Form
A linear program maximizes a linear objective subject to linear constraints:
\text{Maximize} \quad \mathbf{c}^T \mathbf{x} \\
\text{subject to} \quad A\mathbf{x} \leq \mathbf{b}, \quad \mathbf{x} \geq \mathbf{0}Production Planning Example
import numpy as np
from scipy.optimize import linprog
# Profit per unit (maximize, so negate for linprog)
products = ['Product A', 'Product B', 'Product C']
profit = np.array([50, 75, 60])
# Resource usage per unit
labor = np.array([2, 3, 2.5])
machine = np.array([1, 2, 1.5])
material = np.array([5, 8, 6])
# Available resources
A_ub = np.array([labor, machine, material])
b_ub = np.array([1000, 500, 1500])
bounds = [(0, None)] * 3
result = linprog(-profit, A_ub=A_ub, b_ub=b_ub, bounds=bounds, method='highs')
optimal_qty = result.x
max_profit = -result.fun
print("Optimal Production:")
for p, q in zip(products, optimal_qty):
print(f" {p}: {q:.0f} units")
print(f"Maximum Profit: ${max_profit:,.2f}")Portfolio Optimization
The Markowitz mean-variance objective minimizes portfolio risk for a target return:
\min_{\mathbf{w}} \quad \mathbf{w}^T \Sigma \mathbf{w} \\
\text{s.t.} \quad \mathbf{w}^T \boldsymbol{\mu} \geq r_{\min}, \quad \sum_i w_i = 1, \quad w_i \geq 0import numpy as np
from scipy.optimize import minimize
assets = ['Stock A', 'Stock B', 'Stock C', 'Bond Fund', 'Money Market']
expected_returns = np.array([0.10, 0.12, 0.08, 0.04, 0.02])
std_dev = np.array([0.18, 0.20, 0.15, 0.05, 0.02])
corr = np.array([[1.00, 0.65, 0.60, -0.10, 0.05],
[0.65, 1.00, 0.70, -0.05, 0.10],
[0.60, 0.70, 1.00, 0.00, 0.08],
[-0.10,-0.05, 0.00, 1.00, 0.70],
[0.05, 0.10, 0.08, 0.70, 1.00]])
cov = np.outer(std_dev, std_dev) * corr
def portfolio_variance(w): return w @ cov @ w
def portfolio_return(w): return w @ expected_returns
constraints = [
{'type': 'eq', 'fun': lambda w: np.sum(w) - 1},
{'type': 'ineq', 'fun': lambda w: portfolio_return(w) - 0.06}
]
bounds = [(0, 1)] * 5
result = minimize(portfolio_variance, [0.2]*5, method='SLSQP', bounds=bounds, constraints=constraints)
print("Optimal Portfolio Allocation:")
for a, w in zip(assets, result.x):
print(f" {a}: {w:.1%}")
print(f"Expected Return: {portfolio_return(result.x):.2%}")
print(f"Portfolio Risk: {np.sqrt(portfolio_variance(result.x)):.2%}")Integer Programming (MILP)
When decisions are binary (yes/no) or must be whole numbers, we use Mixed-Integer Linear Programming:
from pulp import *
prob = LpProblem("Facility_Location", LpMinimize)
# Binary: open warehouse i (1) or not (0)
fixed_costs = [100000, 120000, 90000]
n_warehouses = 3
n_customers = 4
demand = [50, 80, 60, 70]
transport = [[10, 20, 15, 25], [18, 12, 22, 14], [14, 16, 10, 20]]
capacity = [200, 250, 180]
y = [LpVariable(f"Open_{i}", cat='Binary') for i in range(n_warehouses)]
x = [[LpVariable(f"Ship_{i}_{j}", lowBound=0)
for j in range(n_customers)] for i in range(n_warehouses)]
prob += (lpSum(fixed_costs[i] * y[i] for i in range(n_warehouses)) +
lpSum(transport[i][j] * x[i][j]
for i in range(n_warehouses) for j in range(n_customers)))
for j in range(n_customers):
prob += lpSum(x[i][j] for i in range(n_warehouses)) == demand[j]
for i in range(n_warehouses):
prob += lpSum(x[i][j] for j in range(n_customers)) <= capacity[i] * y[i]
prob.solve(PULP_CBC_CMD(msg=0))
print(f"Status: {LpStatus[prob.status]}, Total Cost: ${value(prob.objective):,.0f}")
for i in range(n_warehouses):
if y[i].varValue == 1:
print(f" Warehouse {i+1} OPEN")Stochastic Optimization
Newsvendor Problem (Monte Carlo)
The critical ratio formula determines the optimal order quantity under demand uncertainty:
Q^* = F^{-1}\left(\frac{p - c}{p - s}\right)import numpy as np
import matplotlib.pyplot as plt
cost, price, salvage = 0.5, 1.0, 0.1
demand_mean, demand_std = 100, 20
def simulate_profit(order_qty, n=10000):
demands = np.random.normal(demand_mean, demand_std, n)
sold = np.minimum(order_qty, demands)
leftover = np.maximum(0, order_qty - demands)
return np.mean(sold * price + leftover * salvage - order_qty * cost)
quantities = np.arange(50, 160, 5)
profits = [simulate_profit(q) for q in quantities]
optimal_q = quantities[np.argmax(profits)]
print(f"Optimal Order Quantity: {optimal_q} units")
print(f"Expected Profit: ${max(profits):.2f}")
# Theoretical: critical ratio
critical_ratio = (price - cost) / (price - salvage)
theoretical_q = demand_mean + demand_std * np.percentile(np.random.standard_normal(10000), critical_ratio * 100)
print(f"Theoretical Optimal (critical ratio={critical_ratio:.2f}): {theoretical_q:.0f} units")Scenario & Sensitivity Analysis
Understanding how decisions change under different assumptions:
Scenario | Elasticity | Unit Cost | Optimal Price | Max Profit |
|---|---|---|---|---|
Conservative | -1.2 | $55 | ~$118 | Lower |
Base Case | -1.5 | $50 | ~$110 | Moderate |
Optimistic | -1.8 | $45 | ~$102 | Higher |
import numpy as np
base_price, base_demand = 100, 1000
scenarios = {
'Conservative': {'elasticity': -1.2, 'cost': 55},
'Base Case': {'elasticity': -1.5, 'cost': 50},
'Optimistic': {'elasticity': -1.8, 'cost': 45}
}
prices = np.linspace(60, 150, 500)
for name, params in scenarios.items():
e, c = params['elasticity'], params['cost']
profits = [(p - c) * base_demand * (p / base_price) ** e for p in prices]
opt_idx = np.argmax(profits)
print(f"{name}: Optimal price=${prices[opt_idx]:.0f}, Profit=${profits[opt_idx]:,.0f}")Common Optimization Problems
Problem | Decision Variables | Objective | Key Constraints |
|---|---|---|---|
Portfolio Optimization | Asset weights | Maximize return / Minimize risk | Weights sum to 1, min return |
Production Planning | Units per product | Maximize profit | Capacity, demand, materials |
Supply Chain Design | Facility locations & flows | Minimize total cost | Demand satisfaction, capacity |
Workforce Scheduling | Staff assignments by shift | Minimize cost | Coverage, labor laws |
Revenue Management | Price / inventory allocation | Maximize revenue | Capacity, demand curves |
Vehicle Routing | Routes and assignments | Minimize distance/time | Vehicle capacity, time windows |
Key Optimization Tools
Tool | Language | Best For | License |
|---|---|---|---|
PuLP | Python | LP and MILP problems | Open source |
Pyomo | Python | Large complex models | Open source |
SciPy.optimize | Python | Nonlinear optimization | Open source |
CPLEX | Python/Java/C++ | Enterprise-scale MILP | Commercial |
Gurobi | Python/R/C++ | High-performance MILP | Commercial |
OR-Tools | Python/C++ | Routing and scheduling | Open source |
Best Practices
Start simple: Begin with linear models, add complexity only when justified
Validate against reality: Back-test with historical data before deployment
Sensitivity analysis: Always test how solutions change with key parameter variations
Engage domain experts: Business constraints must reflect operational reality
Plan for implementation: The best model is useless without a deployment and monitoring plan
Conclusion
Prescriptive analytics represents the frontier of data-driven decision-making. While descriptive analytics tells you what happened, diagnostic analytics explains why, and predictive analytics forecasts what will happen, prescriptive analytics tells you what to do — and proves that choice is optimal given your constraints and objectives. Organizations that master prescriptive analytics gain significant competitive advantages through better resource allocation, faster decision cycles, and improved outcomes.
Create a free reader account to keep reading.
