Corin1998 commited on
Commit
79a730b
·
verified ·
1 Parent(s): c667cc7

Update app/forecast.py

Browse files
Files changed (1) hide show
  1. app/forecast.py +10 -34
app/forecast.py CHANGED
@@ -3,7 +3,6 @@ import pandas as pd
3
  import numpy as np
4
  from . import storage
5
 
6
- # 可能なら Prophet / NeuralProphet を使用(無ければフォールバック)
7
  try:
8
  from prophet import Prophet
9
  except Exception:
@@ -20,17 +19,15 @@ class SeasonalityModel:
20
  self.campaign_id = campaign_id
21
  self.model = None
22
  self.model_type = "none"
23
- self.global_mean = 0.05 # データが乏しいときの既定CTR
24
 
25
  def fit(self):
26
- # イベントから時系列(1時間粒度のCTR)を作る
27
  with storage.get_conn() as con:
28
  df = pd.read_sql_query(
29
  "SELECT ts, event_type FROM events WHERE campaign_id=?",
30
  con,
31
  params=(self.campaign_id,),
32
  )
33
-
34
  if df.empty:
35
  self.model_type = "none"
36
  return
@@ -38,28 +35,16 @@ class SeasonalityModel:
38
  df["ts"] = pd.to_datetime(df["ts"], errors="coerce")
39
  df = df.dropna(subset=["ts"])
40
  df["hour"] = df["ts"].dt.floor("h")
 
 
 
41
 
42
- agg = (
43
- df.pivot_table(
44
- index="hour", columns="event_type", values="ts", aggfunc="count"
45
- )
46
- .fillna(0)
47
- )
48
- if "impression" not in agg:
49
- agg["impression"] = 0
50
- if "click" not in agg:
51
- agg["click"] = 0
52
-
53
- ctr = np.where(
54
- agg["impression"] > 0, agg["click"] / agg["impression"], np.nan
55
- )
56
  if np.all(np.isnan(ctr)):
57
  self.model_type = "none"
58
  return
59
-
60
  self.global_mean = float(np.nanmean(ctr))
61
 
62
- # Prophet / NeuralProphet の学習データ
63
  ds = agg.index.to_series().reset_index(drop=True)
64
  train = pd.DataFrame({"ds": ds, "y": pd.Series(ctr).fillna(self.global_mean).values})
65
 
@@ -67,36 +52,27 @@ class SeasonalityModel:
67
  if Prophet is not None:
68
  m = Prophet(weekly_seasonality=True, daily_seasonality=True)
69
  m.fit(train)
70
- self.model = m
71
- self.model_type = "prophet"
72
  elif NeuralProphet is not None:
73
  m = NeuralProphet(weekly_seasonality=True, daily_seasonality=True)
74
  m.fit(train, freq="H")
75
- self.model = m
76
- self.model_type = "neuralprophet"
77
  else:
78
  self.model_type = "none"
79
  except Exception:
80
- # 失敗時はフォールバック
81
  self.model_type = "none"
82
 
83
  def expected_ctr(self, context: dict) -> float:
84
  hour = int(context.get("hour", 12))
85
-
86
- # モデルが無い場合は簡易ヒューリスティック
87
  if self.model_type in {None, "none"}:
88
  base = self.global_mean
89
- if 11 <= hour <= 13:
90
- return min(0.99, base * 1.1)
91
- if 20 <= hour <= 23:
92
- return min(0.99, base * 1.15)
93
  return max(0.01, base)
94
 
95
- # モデルあり:当日・指定時間の1点予測
96
  now_ds = pd.Timestamp.utcnow().floor("D") + pd.Timedelta(hours=hour)
97
  if self.model_type == "prophet":
98
  yhat = float(self.model.predict(pd.DataFrame({"ds": [now_ds]}))["yhat"].iloc[0])
99
- else: # neuralprophet
100
  yhat = float(self.model.predict(pd.DataFrame({"ds": [now_ds]}))["yhat1"].iloc[0])
101
-
102
  return max(0.01, min(0.99, yhat))
 
3
  import numpy as np
4
  from . import storage
5
 
 
6
  try:
7
  from prophet import Prophet
8
  except Exception:
 
19
  self.campaign_id = campaign_id
20
  self.model = None
21
  self.model_type = "none"
22
+ self.global_mean = 0.05
23
 
24
  def fit(self):
 
25
  with storage.get_conn() as con:
26
  df = pd.read_sql_query(
27
  "SELECT ts, event_type FROM events WHERE campaign_id=?",
28
  con,
29
  params=(self.campaign_id,),
30
  )
 
31
  if df.empty:
32
  self.model_type = "none"
33
  return
 
35
  df["ts"] = pd.to_datetime(df["ts"], errors="coerce")
36
  df = df.dropna(subset=["ts"])
37
  df["hour"] = df["ts"].dt.floor("h")
38
+ agg = df.pivot_table(index="hour", columns="event_type", values="ts", aggfunc="count").fillna(0)
39
+ if "impression" not in agg: agg["impression"] = 0
40
+ if "click" not in agg: agg["click"] = 0
41
 
42
+ ctr = np.where(agg["impression"] > 0, agg["click"] / agg["impression"], np.nan)
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  if np.all(np.isnan(ctr)):
44
  self.model_type = "none"
45
  return
 
46
  self.global_mean = float(np.nanmean(ctr))
47
 
 
48
  ds = agg.index.to_series().reset_index(drop=True)
49
  train = pd.DataFrame({"ds": ds, "y": pd.Series(ctr).fillna(self.global_mean).values})
50
 
 
52
  if Prophet is not None:
53
  m = Prophet(weekly_seasonality=True, daily_seasonality=True)
54
  m.fit(train)
55
+ self.model, self.model_type = m, "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, self.model_type = m, "neuralprophet"
 
60
  else:
61
  self.model_type = "none"
62
  except Exception:
 
63
  self.model_type = "none"
64
 
65
  def expected_ctr(self, context: dict) -> float:
66
  hour = int(context.get("hour", 12))
 
 
67
  if self.model_type in {None, "none"}:
68
  base = self.global_mean
69
+ if 11 <= hour <= 13: return min(0.99, base * 1.1)
70
+ if 20 <= hour <= 23: return min(0.99, base * 1.15)
 
 
71
  return max(0.01, base)
72
 
 
73
  now_ds = pd.Timestamp.utcnow().floor("D") + pd.Timedelta(hours=hour)
74
  if self.model_type == "prophet":
75
  yhat = float(self.model.predict(pd.DataFrame({"ds": [now_ds]}))["yhat"].iloc[0])
76
+ else:
77
  yhat = float(self.model.predict(pd.DataFrame({"ds": [now_ds]}))["yhat1"].iloc[0])
 
78
  return max(0.01, min(0.99, yhat))