!pip install pyomoRequirement already satisfied: pyomo in /usr/local/lib/python3.12/dist-packages (6.9.4) Requirement already satisfied: ply in /usr/local/lib/python3.12/dist-packages (from pyomo) (3.11)
!apt-get install -y -qq glpk-utils- Setting up glpk-utils (5.0-1) …
/sbin/ldconfig.real: /usr/local/lib/libtcm_debug.so.1 is not a symbolic link- (similar warnings repeated…)
import pandas as pdimport numpy as np
from google.colab import drivedrive.mount('/content/drive', force_remount=True)Mounted at /content/drive
dt_simulated_weekly = pd.read_csv('/content/drive/My Drive/Colab Notebooks/dt_simulated_weekly.csv')dt_simulated_weekly| DATE | revenue | tv_S | ooh_S | print_S | search_S | facebook_S |
|---|---|---|---|---|---|---|
| 11/23/15 | 2754371.667 | 22358.35 | 0.0 | 12728.49 | 0.0 | 7607.13 |
| 11/30/15 | 2584276.667 | 28613.45 | 0.0 | 0.0 | 4133.33 | 1141.95 |
| 12/07/15 | 2547386.667 | 0.0 | 132278.40 | 453.87 | 3786.67 | 4256.38 |
| 12/14/15 | 2875220.0 | 83450.31 | 0.0 | 17680.0 | 4253.33 | 2800.49 |
| 12/21/15 | 2215953.333 | 0.0 | 277336.0 | 0.0 | 3613.33 | 689.58 |
| … | … | … | … | … | … | … |
| 10/14/19 | 2456240.0 | 0.0 | 32230.93 | 20496.49 | 14946.67 | 0.0 |
| 10/21/19 | 2182825.0 | 20655.68 | 0.0 | 0.0 | 13826.67 | 4454.15 |
| 10/28/19 | 2377706.667 | 2931.0 | 7516.8 | 2330.13 | 15293.33 | 0.0 |
| 11/04/19 | 2732825.0 | 2993.73 | 0.0 | 3206.84 | 17880.0 | 0.0 |
| 11/11/19 | 2767788.333 | 0.0 | 0.0 | 0.0 | 0.0 | 12206.36 |
(208 rows × 7 columns)
dt_simulated_weekly.isnull().sum()| Column | Null Count |
|---|---|
| DATE | 0 |
| revenue | 0 |
| tv_S | 0 |
| ooh_S | 0 |
| print_S | 0 |
| search_S | 0 |
| facebook_S | 0 |
dt_simulated_weekly.describe()| revenue | tv_S | ooh_S | print_S | search_S | facebook_S | |
|---|---|---|---|---|---|---|
| count | 208.0 | 208.0 | 208.0 | 208.0 | 208.0 | 208.0 |
| mean | 1.82e+06 | 14843.69 | 43217.94 | 3728.63 | 5915.51 | 2145.66 |
| std | 7.16e+05 | 28558.37 | 83991.43 | 6483.07 | 4702.50 | 3160.36 |
| min | 6.72e+05 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 |
| 25% | 1.16e+06 | 0.00 | 0.00 | 0.00 | 2353.33 | 0.00 |
| 50% | 1.87e+06 | 0.00 | 0.00 | 0.00 | 4806.67 | 0.00 |
| 75% | 2.38e+06 | 18406.71 | 50858.13 | 4767.83 | 8536.67 | 3623.01 |
| max | 3.83e+06 | 158046.57 | 500361.6 | 31922.31 | 17880.0 | 15400.39 |
import matplotlib.pyplot as plt
dt_simulated_weekly['DATE'] = pd.to_datetime(dt_simulated_weekly['DATE'])dt_simulated_weekly = dt_simulated_weekly.set_index('DATE')
plt.figure(figsize=(12, 6))plt.plot(dt_simulated_weekly.index, dt_simulated_weekly['revenue'], marker='o')plt.title("Weekly Time Series")plt.xlabel("Date")plt.ylabel("revenue")plt.grid(True)plt.show()Warning: Could not infer format, falling back to dateutil
import random
hyperparameters = { 'facebook_S_alpha': 1.115, 'facebook_S_gamma': 2145.66, 'facebook_S_theta': 0.216, 'print_S_alpha': 2.735, 'print_S_gamma': 3728.63, 'print_S_theta': 0.130, 'tv_S_alpha': 2.317, 'tv_S_gamma': 14843.69, 'tv_S_theta': 0.441, 'search_S_alpha': 2.008, 'search_S_gamma': 5915.51, 'search_S_theta': 0.156, 'ooh_S_alpha': 2.646, 'ooh_S_gamma': 43217.94, 'ooh_S_theta': 0.179}hyperparameters{'facebook_S_alpha': 1.115, 'facebook_S_gamma': 2145.66, ... }def adstock_transformation(x, decay): adstock = np.zeros(len(x)) for t in range(len(x)): if t == 0: adstock[t] = x[t] else: adstock[t] = x[t] + decay * adstock[t-1] return adstockFutureWarning: Series indexing behavior changing in future pandas
def saturation_transformation(adstock_var, alpha, gamma): epsilon = 1e-6 return 1/(1+(gamma/(adstock_var+epsilon))**alpha)from sklearn.linear_model import LinearRegressionX = saturatedy = dt_simulated_weekly["revenue"]
model = LinearRegression()model.fit(X, y)
print(f"Intercept: {model.intercept_}")print(f"Coefficients: {model.coef_}")Intercept: 672269.07Coefficients: [879382.83 182146.33 152446.92 647846.63 796880.31]np.max(model.coef_)879382.83Step 4: Optimal budget allocation
import pyomo.environ as pyofrom pyomo.opt import SolverFactory
budget = {}for m in media_var: budget[m] = [20000]adstocked_spend = {}for m in media_var: adstocked_spend[m] = adstock_transformation(budget[m], hyperparameters[m+'_theta'])adstocked_spend = pd.DataFrame.from_dict(adstocked_spend)adstocked_spend
saturated_spend = {}for m in media_var: saturated_spend[m] = saturation_transformation(adstocked_spend[m], hyperparameters[m+'_alpha'],hyperparameters[m+'_gamma'])saturated_spend = pd.DataFrame.from_dict(saturated_spend)saturated_spend
# scikit-learn 회귀 모델에서 intercept_와 coef_를 가져옴intercept_ = model.intercept_ # 회귀 모델의 절편coef_ = model.coef_ # 회귀 모델의 계수
# Pyomo 모델 생성model_pyomo = pyo.ConcreteModel()
# 광고 채널의 개수 (예: 5개 채널)n_channels = len(X.columns)
# 변수 설정 (각 채널에 할당할 광고비)model_pyomo.spend = pyo.Var(range(n_channels), domain=pyo.NonNegativeReals)
# 목적 함수 정의def objective_function(model_pyomo): # Pyomo에서 회귀 모델 기반 목적 함수 정의 return (model.intercept_ + sum(coef_[i] * model_pyomo.spend[i] for i in range(n_channels)))
model_pyomo.objective = pyo.Objective(rule=objective_function, sense=pyo.maximize)
# 제약 조건: 총 광고비는 sum(saturated_spend.iloc[0]) 을 넘지 않도록def total_budget_constraint(model_pyomo): return sum(model_pyomo.spend[i] for i in range(n_channels)) <= sum(saturated_spend.iloc[0])
model_pyomo.budget_constraint = pyo.Constraint(rule=total_budget_constraint)
# # 제약 조건: TV 채널은 전체 예산의 50% 이하def tv_constraint(model_pyomo): total_budget = sum(model_pyomo.spend[i] for i in range(n_channels)) return model_pyomo.spend[0] <= 0.5 * total_budget
model_pyomo.tv_constraint = pyo.Constraint(rule=tv_constraint)
# Solver 설정 및 최적화 실행opt = SolverFactory('glpk', executable='/usr/bin/glpsol')result = opt.solve(model_pyomo)
# 최적화 결과 출력optimal_spend = np.array([pyo.value(model_pyomo.spend[i]) for i in range(n_channels)])actual_budget = sum(v[0] for v in budget.values())print("Optimal spend allocation:")df_alloc = pd.DataFrame({"Channel": ['tv_S', 'ooh_S', 'print_S', 'search_S', 'facebook_S'], "Spend": optimal_spend/(sum(optimal_spend))*actual_budget})display(df_alloc)Optimal spend allocation:
| Channel | Spend |
|---|---|
| tv_S | 50000.0 |
| ooh_S | 0.0 |
| print_S | 0.0 |
| search_S | 0.0 |
| facebook_S | 50000.0 |