Corin1998 commited on
Commit
6b1a839
·
verified ·
1 Parent(s): 1b42d21

Create forecast.py

Browse files
Files changed (1) hide show
  1. app/forecast.py +85 -0
app/forecast.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ import pandas as pd
3
+ import numpy as np
4
+ from datetime import datetime
5
+
6
+ try:
7
+ from prophet import Prophet
8
+ except Exception:
9
+ Prophet = None
10
+
11
+ try:
12
+ from neuralprophet import NeuralProphet
13
+ except Exception:
14
+ NeuralProphet = None
15
+
16
+ from . import storage
17
+
18
+ class SeasonalityModel:
19
+ def __init__(self, campaign_id: str):
20
+ self.campaign_id = campaign_id
21
+ self.model_type = None
22
+ self.model = None
23
+ self.global_mean = 0.05
24
+
25
+ def fit(self):
26
+ with storage.get_conn() as con:
27
+ df = pd.read_sql_query(
28
+ """
29
+ SELECT ts, event_type, variant_id FROM events WHERE campaign_id=?
30
+ """,
31
+ con, params=(self.campaign_id,)
32
+ )
33
+ if df.empty or Prophet is None:
34
+ self.model_type = "none"
35
+ return
36
+ df["ts"] = pd.to_datetime(df["ts"], errors="coerce")
37
+ df = df.dropna(subset=["ts"])
38
+
39
+ df["hour"] = df["ts"].dt.floor("h")
40
+ agg = df.pivot_table(index="hour", columns="event_type", values="variant_id", aggfunc="count").fillna(0)
41
+ if "impression" not in agg:
42
+ agg["impression"] = 0
43
+ if "click" not in agg:
44
+ agg["click"] = 0
45
+ agg["ctr"] = np.where(agg["impression"] > 0, agg["click"]/agg["impression"],np.nan)
46
+ agg["ctr"].fillna(agg["ctr"].mean()) if not np.isna(agg["ctr"].mean())else self.global_mean
47
+
48
+ ds = agg.index.to_series().reset_index(drop=True)
49
+ train = pd.DataFrame({"ds":ds, "y":agg["ctr"].values})
50
+
51
+ if Prophet is not None:
52
+ m = Prophet(weekly_seasonality=True, daily_seasonality=True)
53
+ m.fit(train)
54
+ self.model = m
55
+ self.model_type = "prophet"
56
+ elif NeuralProphet is not None:
57
+ m = NeuralProphet(weekly_seasonality=True, daily_seasonality=True)
58
+ m.fit(train, freq='H')
59
+ self.model = m
60
+ self.model_type = "neuralprophet"
61
+ else:
62
+ self.model_type = "none"
63
+
64
+ def expected_ctr(self, context: dict) -> float:
65
+ if self.model_type in {None, "none"}:
66
+ hour = int(context.get("hour", 12))
67
+ base = self.global_mean
68
+
69
+ if 11 <= hour <= 13:
70
+ return min(0.99, base * 1.1)
71
+ if 20 <= hour <= 23:
72
+ return min(0.99, base*1.15)
73
+ return max(0.01, base)
74
+ else:
75
+ now = pd.Timestamp.utcnow().floor('h')
76
+ ds = now
77
+
78
+ if "hour" in context:
79
+ ds = pd.Timestamp.utcnow().floor('D') + pd.Timedelta(hours=int(context["hour"]))
80
+ df = pd.DataFrame({"ds":[ds]})
81
+ if self.model_type == "prophet"
82
+ yhat = self.model.predict(df)["yhat"].iloc[0]
83
+ else:
84
+ yhat = self.model.predict(df)["yhat1"].iloc[0]
85
+ return float(max(0.01, min(0.99,yhat)))