Spaces:
Sleeping
Sleeping
Update dashboard.py
Browse files- dashboard.py +76 -5
dashboard.py
CHANGED
|
@@ -1,21 +1,78 @@
|
|
| 1 |
from __future__ import annotations
|
| 2 |
import gradio as gr
|
| 3 |
import plotly.express as px
|
| 4 |
-
|
|
|
|
| 5 |
from bandit import EmpiricalBayesHierarchicalThompson
|
| 6 |
from causal import fit_uplift_binary
|
| 7 |
|
| 8 |
BANDIT = EmpiricalBayesHierarchicalThompson(min_explore=0.05, margin=0.0, n_draws=10000)
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
def ui_refresh_tables():
|
| 11 |
df = read_events()
|
| 12 |
agg = aggregate()
|
| 13 |
-
return df, agg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
|
|
|
| 15 |
def ui_recommend():
|
| 16 |
agg = aggregate()
|
| 17 |
if agg.empty:
|
| 18 |
-
return {"message": "No data yet.
|
| 19 |
return BANDIT.recommend(agg)
|
| 20 |
|
| 21 |
def ui_plot_posteriors(medium: str):
|
|
@@ -36,14 +93,26 @@ def ui_fit_uplift():
|
|
| 36 |
return {"message": "No data"}
|
| 37 |
return fit_uplift_binary(agg)
|
| 38 |
|
|
|
|
| 39 |
def build_ui():
|
| 40 |
with gr.Blocks(title="AdCopy MAB Optimizer Pro") as demo:
|
| 41 |
gr.Markdown("# AdCopy MAB Optimizer Pro — Hierarchical TS + Uplift")
|
|
|
|
| 42 |
with gr.Tab("Data"):
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
grid = gr.Dataframe(headers=["ts","date","medium","creative","is_control","impressions","clicks","conversions","cost","features_json"], wrap=True)
|
| 45 |
grid_agg = gr.Dataframe()
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
with gr.Tab("Bandit"):
|
| 48 |
bbtn = gr.Button("Suggest Allocation (TS)")
|
| 49 |
jout = gr.JSON()
|
|
@@ -53,8 +122,10 @@ def build_ui():
|
|
| 53 |
plot = gr.Plot(visible=False)
|
| 54 |
msg = gr.Markdown()
|
| 55 |
gr.Button("Plot CTR by Creative").click(ui_plot_posteriors, inputs=[medium], outputs=[plot, msg])
|
|
|
|
| 56 |
with gr.Tab("Uplift (Causal)"):
|
| 57 |
cbtn = gr.Button("Fit Uplift Model")
|
| 58 |
cout = gr.JSON()
|
| 59 |
cbtn.click(ui_fit_uplift, outputs=cout)
|
|
|
|
| 60 |
return demo
|
|
|
|
| 1 |
from __future__ import annotations
|
| 2 |
import gradio as gr
|
| 3 |
import plotly.express as px
|
| 4 |
+
import pandas as pd
|
| 5 |
+
from data import read_events, aggregate, append_events
|
| 6 |
from bandit import EmpiricalBayesHierarchicalThompson
|
| 7 |
from causal import fit_uplift_binary
|
| 8 |
|
| 9 |
BANDIT = EmpiricalBayesHierarchicalThompson(min_explore=0.05, margin=0.0, n_draws=10000)
|
| 10 |
|
| 11 |
+
REQUIRED_COLS = [
|
| 12 |
+
"date","medium","creative","is_control",
|
| 13 |
+
"impressions","clicks","conversions","cost","features_json"
|
| 14 |
+
]
|
| 15 |
+
|
| 16 |
+
# ---- helpers ----
|
| 17 |
+
def _ok(msg: str):
|
| 18 |
+
return gr.Markdown.update(value=f"✅ {msg}")
|
| 19 |
+
def _warn(msg: str):
|
| 20 |
+
return gr.Markdown.update(value=f"⚠️ {msg}")
|
| 21 |
+
def _err(msg: str):
|
| 22 |
+
return gr.Markdown.update(value=f"❌ {msg}")
|
| 23 |
+
|
| 24 |
+
# ---- data ops ----
|
| 25 |
def ui_refresh_tables():
|
| 26 |
df = read_events()
|
| 27 |
agg = aggregate()
|
| 28 |
+
return df, agg, _ok("Refreshed.")
|
| 29 |
+
|
| 30 |
+
def ui_load_sample():
|
| 31 |
+
rows = [
|
| 32 |
+
{"date":"2025-09-01","medium":"FB","creative":"A1","is_control":1,"impressions":1000,"clicks":25,"conversions":3,"cost":480,"features_json":"{}"},
|
| 33 |
+
{"date":"2025-09-01","medium":"FB","creative":"B1","is_control":0,"impressions":850,"clicks":24,"conversions":2,"cost":395,"features_json":"{}"},
|
| 34 |
+
{"date":"2025-09-01","medium":"FB","creative":"C1","is_control":0,"impressions":900,"clicks":21,"conversions":2,"cost":420,"features_json":"{}"},
|
| 35 |
+
{"date":"2025-09-01","medium":"FB","creative":"D1","is_control":0,"impressions":820,"clicks":20,"conversions":2,"cost":380,"features_json":"{}"},
|
| 36 |
+
{"date":"2025-09-01","medium":"FB","creative":"E1","is_control":0,"impressions":960,"clicks":31,"conversions":3,"cost":490,"features_json":"{}"},
|
| 37 |
+
{"date":"2025-09-01","medium":"GDN","creative":"A2","is_control":1,"impressions":1100,"clicks":25,"conversions":2,"cost":545,"features_json":"{}"},
|
| 38 |
+
{"date":"2025-09-01","medium":"GDN","creative":"B2","is_control":0,"impressions":990,"clicks":27,"conversions":3,"cost":514,"features_json":"{}"},
|
| 39 |
+
{"date":"2025-09-01","medium":"GDN","creative":"C2","is_control":0,"impressions":860,"clicks":19,"conversions":2,"cost":450,"features_json":"{}"},
|
| 40 |
+
{"date":"2025-09-01","medium":"GDN","creative":"D2","is_control":0,"impressions":1045,"clicks":33,"conversions":4,"cost":570,"features_json":"{}"},
|
| 41 |
+
{"date":"2025-09-01","medium":"GDN","creative":"E2","is_control":0,"impressions":905,"clicks":20,"conversions":2,"cost":462,"features_json":"{}"}
|
| 42 |
+
]
|
| 43 |
+
append_events(pd.DataFrame(rows))
|
| 44 |
+
df, agg = read_events(), aggregate()
|
| 45 |
+
return df, agg, _ok("Sample data loaded.")
|
| 46 |
+
|
| 47 |
+
def ui_upload_csv(file: gr.File):
|
| 48 |
+
if file is None:
|
| 49 |
+
return gr.update(), gr.update(), _warn("CSVファイルを選択してください。")
|
| 50 |
+
try:
|
| 51 |
+
df = pd.read_csv(file.name)
|
| 52 |
+
except Exception as e:
|
| 53 |
+
return gr.update(), gr.update(), _err(f"CSV読み込みに失敗: {e}")
|
| 54 |
+
|
| 55 |
+
missing = [c for c in REQUIRED_COLS if c not in df.columns]
|
| 56 |
+
if missing:
|
| 57 |
+
return gr.update(), gr.update(), _err(f"必須列が不足: {missing}")
|
| 58 |
+
|
| 59 |
+
# 型のサニタイズ(軽く)
|
| 60 |
+
for c in ["is_control","impressions","clicks","conversions"]:
|
| 61 |
+
df[c] = df[c].fillna(0).astype(int)
|
| 62 |
+
if "cost" in df.columns:
|
| 63 |
+
df["cost"] = df["cost"].fillna(0.0).astype(float)
|
| 64 |
+
if "features_json" in df.columns:
|
| 65 |
+
df["features_json"] = df["features_json"].fillna("{}").astype(str)
|
| 66 |
+
|
| 67 |
+
append_events(df)
|
| 68 |
+
df2, agg2 = read_events(), aggregate()
|
| 69 |
+
return df2, agg2, _ok(f"取り込み完了({len(df)}行)。")
|
| 70 |
|
| 71 |
+
# ---- bandit / causal ----
|
| 72 |
def ui_recommend():
|
| 73 |
agg = aggregate()
|
| 74 |
if agg.empty:
|
| 75 |
+
return {"message": "No data yet. Dataタブで 'Load Sample Data' を押すか、CSVを取り込んでください。"}
|
| 76 |
return BANDIT.recommend(agg)
|
| 77 |
|
| 78 |
def ui_plot_posteriors(medium: str):
|
|
|
|
| 93 |
return {"message": "No data"}
|
| 94 |
return fit_uplift_binary(agg)
|
| 95 |
|
| 96 |
+
# ---- UI ----
|
| 97 |
def build_ui():
|
| 98 |
with gr.Blocks(title="AdCopy MAB Optimizer Pro") as demo:
|
| 99 |
gr.Markdown("# AdCopy MAB Optimizer Pro — Hierarchical TS + Uplift")
|
| 100 |
+
|
| 101 |
with gr.Tab("Data"):
|
| 102 |
+
status = gr.Markdown() # メッセージ表示
|
| 103 |
+
with gr.Row():
|
| 104 |
+
btn_refresh = gr.Button("Refresh")
|
| 105 |
+
btn_seed = gr.Button("Load Sample Data")
|
| 106 |
+
with gr.Row():
|
| 107 |
+
file = gr.File(label="Upload CSV (columns: " + ", ".join(REQUIRED_COLS) + ")", file_types=[".csv"])
|
| 108 |
+
btn_upload = gr.Button("Import CSV")
|
| 109 |
grid = gr.Dataframe(headers=["ts","date","medium","creative","is_control","impressions","clicks","conversions","cost","features_json"], wrap=True)
|
| 110 |
grid_agg = gr.Dataframe()
|
| 111 |
+
|
| 112 |
+
btn_refresh.click(ui_refresh_tables, outputs=[grid, grid_agg, status])
|
| 113 |
+
btn_seed.click(ui_load_sample, outputs=[grid, grid_agg, status])
|
| 114 |
+
btn_upload.click(ui_upload_csv, inputs=[file], outputs=[grid, grid_agg, status])
|
| 115 |
+
|
| 116 |
with gr.Tab("Bandit"):
|
| 117 |
bbtn = gr.Button("Suggest Allocation (TS)")
|
| 118 |
jout = gr.JSON()
|
|
|
|
| 122 |
plot = gr.Plot(visible=False)
|
| 123 |
msg = gr.Markdown()
|
| 124 |
gr.Button("Plot CTR by Creative").click(ui_plot_posteriors, inputs=[medium], outputs=[plot, msg])
|
| 125 |
+
|
| 126 |
with gr.Tab("Uplift (Causal)"):
|
| 127 |
cbtn = gr.Button("Fit Uplift Model")
|
| 128 |
cout = gr.JSON()
|
| 129 |
cbtn.click(ui_fit_uplift, outputs=cout)
|
| 130 |
+
|
| 131 |
return demo
|