Jay1121 commited on
Commit
a5ffa06
ยท
verified ยท
1 Parent(s): 98c9d0c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -38
app.py CHANGED
@@ -1521,39 +1521,60 @@ COL_STAGE_OVERALL = "#C32C2C" # ๋นจ
1521
  COL_STAGE_PREF = "#D24D3E" # ์ฃผ
1522
  COL_STAGE_REC = "#DE937A" # ๋…ธ
1523
  COL_STAGE_INTENT = "#D49442" # ๋ฒ (๊ณจ๋“œํ†ค)
1524
- COL_STAGE_BUY = "#2B8E81" # ์ดˆfig.update_xaxes
1525
  COL_STAGE_NONPREF = "#9CA3AF" # ๋ฏธ์„ ํ˜ธ(ํšŒ์ƒ‰)
1526
 
1527
 
1528
  def matrix_funnel_figure(row, df_tm, seg, mod, loy, **kwargs):
1529
- # --- 0) ๋กœ์ปฌ ํ™•๋ฅ  ์ •๊ทœํ™” + ํด๋ฆฌํ•‘ ์œ ํ‹ธ ---
 
 
 
 
 
 
1530
  def _p(x):
1531
- x = _safe_num(x)
1532
- if not np.isfinite(x):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1533
  return np.nan
1534
- # 1.5๋ณด๋‹ค ํฌ๋ฉด 'ํผ์„ผํŠธ(์˜ˆ: 23=23%)'๋กœ ๋ณด๊ณ  100์œผ๋กœ ๋‚˜๋ˆ”
1535
  return x / 100.0 if x > 1.5 else x
1536
 
1537
- def _clip01(x):
1538
- return np.nan if not np.isfinite(x) else float(min(1.0, max(0.0, x)))
1539
 
1540
- # 1) ๋“œ๋กญ/์ตœ์ข…์œจ ํ™•๋ณด (ํ™•๋ฅ  ์ •๊ทœํ™” + [0,1] ํด๋ฆฌํ•‘)
1541
  d1_raw, d2_raw, d3_raw, full_raw = drops_from_anywhere(row, df_tm, seg, mod, loy)
1542
  d1, d2, d3 = map(_clip01, map(_p, (d1_raw, d2_raw, d3_raw)))
1543
- full_conv = _p(full_raw) # ์ตœ์ข…์œจ์€ ์Œ์ˆ˜/1์ดˆ๊ณผ๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ์–ด๋„ ์•„๋ž˜์—์„œ ๋‹จ๊ณ„ ๋ณด์ •์œผ๋กœ ์ฒ˜๋ฆฌ
1544
 
1545
- # 2) ๊ฐœ๋ณ„ ์Šคํ…Œ์ด์ง€ ์„ฑ๊ณต๋ฅ  (ํ™•๋ฅ  ์ •๊ทœํ™”)
1546
  pref_sr = _p(row.get("pref_success_rate"))
1547
  rec_sr = _p(row.get("rec_success_rate"))
1548
  intent_sr = _p(row.get("intent_success_rate"))
1549
  buy_sr = _p(row.get("buy_success_rate"))
1550
 
1551
- # 3) ๋ˆ„์ ์œจ ๊ณ„์‚ฐ (๋“œ๋กญ ์šฐ์„ , ๊ฒฐ์ธก ์‹œ ํด๋ฐฑ)
1552
  overall = 1.0
1553
- pref = pref_sr
1554
-
1555
- rec = pref * (1 - d1) if np.isfinite(pref) and np.isfinite(d1) else rec_sr
1556
- intent = rec * (1 - d2) if np.isfinite(rec) and np.isfinite(d2) else intent_sr
1557
 
1558
  if np.isfinite(intent) and np.isfinite(d3):
1559
  buy = intent * (1 - d3)
@@ -1564,19 +1585,14 @@ def matrix_funnel_figure(row, df_tm, seg, mod, loy, **kwargs):
1564
  else:
1565
  buy = intent
1566
 
1567
- # 3-1) ๋‹จ๊ณ„ ๋‹จ์กฐ๊ฐ์†Œ ๋ณด์žฅ + 0~1 ํด๋ฆฌํ•‘ (์—ฌ๊ธฐ์„œ 1.6 ๊ฐ™์€ ๊ฐ’ ์ฐจ๋‹จ)
1568
- seq = [overall,
1569
- _clip01(pref),
1570
- _clip01(rec),
1571
- _clip01(intent),
1572
- _clip01(buy)]
1573
  for i in range(1, len(seq)):
1574
- if np.isfinite(seq[i]) and np.isfinite(seq[i-1]):
1575
- if seq[i] > seq[i-1]:
1576
- seq[i] = seq[i-1]
1577
  overall, pref, rec, intent, buy = seq
1578
 
1579
- # 4) ๋ผ๋ฒจ/๊ฐ’ ๊ตฌ์„ฑ (์ดํ•˜๋Š” ๊ธฐ์กด ๊ทธ๋Œ€๋กœ)
1580
  labels, values = ["์ „์ฒด"], [overall]
1581
  if np.isfinite(pref): labels.append("์„ ํ˜ธ"); values.append(pref)
1582
  if np.isfinite(rec): labels.append("์ถ”์ฒœ"); values.append(rec)
@@ -1584,7 +1600,16 @@ def matrix_funnel_figure(row, df_tm, seg, mod, loy, **kwargs):
1584
  if np.isfinite(buy): labels.append("๊ตฌ๋งค"); values.append(buy)
1585
 
1586
  if len(labels) <= 1:
1587
- return _empty_fig("No Funnel data")
 
 
 
 
 
 
 
 
 
1588
 
1589
  txtpos = ["inside" if v >= 0.07 else "outside" for v in values]
1590
 
@@ -1610,14 +1635,16 @@ def matrix_funnel_figure(row, df_tm, seg, mod, loy, **kwargs):
1610
  connector=dict(line=dict(color="rgba(0,0,0,0.25)", width=0.6)),
1611
  ))
1612
 
 
1613
  fig.update_layout(
1614
  title="Funnel (๋ˆ„์ ์œจ)",
1615
- height=360,
1616
- margin=dict(l=10, r=10, t=30, b=10),
1617
  paper_bgcolor="#ffffff",
1618
  plot_bgcolor="#ffffff",
1619
  )
1620
  fig.update_xaxes(dtick=_auto_dtick(1.0), tickformat=".0%")
 
1621
  return apply_dense_grid(fig, x_prob=True)
1622
 
1623
  def survival_curve_figure(row, df_tm, seg, mod, loy):
@@ -2917,15 +2944,15 @@ def update_all(seg, mod, loy, drag_val, stage_label, tab_right,
2917
  empty, empty, empty, empty, empty, empty, empty, empty
2918
  )
2919
 
2920
- # === [HF ADD] ์„œ๋ฒ„ ๊ธฐ๋™๋ถ€ ===
2921
  if __name__ == "__main__":
2922
- HOST = "0.0.0.0"
2923
- import os
2924
- PORT = int(os.getenv("PORT", "7860"))
2925
- try:
2926
- app # Dash ์ธ์Šคํ„ด์Šค๊ฐ€ ์ „์—ญ์— ์žˆ์–ด์•ผ ํ•จ (์˜ˆ: app = Dash(__name__))
2927
- except NameError as e:
2928
- raise SystemExit("โŒ 'app' ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. (app = Dash(__name__))") from e
2929
- app.run_server(host=HOST, port=PORT, debug=False)
2930
-
2931
 
 
1521
  COL_STAGE_PREF = "#D24D3E" # ์ฃผ
1522
  COL_STAGE_REC = "#DE937A" # ๋…ธ
1523
  COL_STAGE_INTENT = "#D49442" # ๋ฒ (๊ณจ๋“œํ†ค)
1524
+ COL_STAGE_BUY = "#2B8E81" # ์ดˆ๋ก โ† ์˜คํƒ€ ์ˆ˜์ •
1525
  COL_STAGE_NONPREF = "#9CA3AF" # ๋ฏธ์„ ํ˜ธ(ํšŒ์ƒ‰)
1526
 
1527
 
1528
  def matrix_funnel_figure(row, df_tm, seg, mod, loy, **kwargs):
1529
+ """
1530
+ ๋ˆ„์  ํผ๋„:
1531
+ - ํผ์„ผํŠธ ๋ฌธ์ž์—ด(์˜ˆ: '45.5%')/๊ณต๋ฐฑ ์„ž์—ฌ๋„ robust parsing
1532
+ - ๊ฐ’์ด ๋น„์–ด๋„(drop/success ๋‘˜ ๋‹ค NaN) ์ตœ์†Œ 2๋‹จ๊ณ„ ์ด์ƒ ๊ฐ•์ œ๋กœ ๊ทธ๋ ค์คŒ
1533
+ - ๊ธฐ๋ณธ ๋†’์ด 420 (FUNNEL_H๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ๊ฐ’ ๋”ฐ๋ฆ„)
1534
+ """
1535
+ # --- Robust percent parser -------------------------------------------------
1536
  def _p(x):
1537
+ if x is None:
1538
+ return np.nan
1539
+ if isinstance(x, str):
1540
+ s = x.strip()
1541
+ if not s:
1542
+ return np.nan
1543
+ if s.endswith("%"):
1544
+ try:
1545
+ return float(s[:-1].strip()) / 100.0
1546
+ except Exception:
1547
+ return np.nan
1548
+ try:
1549
+ return float(s)
1550
+ except Exception:
1551
+ return np.nan
1552
+ try:
1553
+ x = float(x)
1554
+ except Exception:
1555
  return np.nan
1556
+ # 1.5 ์ดˆ๊ณผ๋ฉด ํผ์„ผํŠธ๋กœ ๊ฐ„์ฃผ(23 => 0.23)
1557
  return x / 100.0 if x > 1.5 else x
1558
 
1559
+ def _clip01(v):
1560
+ return np.nan if not np.isfinite(v) else float(min(1.0, max(0.0, v)))
1561
 
1562
+ # --- 1) ๋“œ๋กญ/์ตœ์ข…์œจ ํ™•๋ณด ---------------------------------------------------
1563
  d1_raw, d2_raw, d3_raw, full_raw = drops_from_anywhere(row, df_tm, seg, mod, loy)
1564
  d1, d2, d3 = map(_clip01, map(_p, (d1_raw, d2_raw, d3_raw)))
1565
+ full_conv = _p(full_raw)
1566
 
1567
+ # --- 2) ๋‹จ๊ณ„๋ณ„ ์„ฑ๊ณต๋ฅ  ------------------------------------------------------
1568
  pref_sr = _p(row.get("pref_success_rate"))
1569
  rec_sr = _p(row.get("rec_success_rate"))
1570
  intent_sr = _p(row.get("intent_success_rate"))
1571
  buy_sr = _p(row.get("buy_success_rate"))
1572
 
1573
+ # --- 3) ๋ˆ„์ ์œจ ๊ณ„์‚ฐ(๋“œ๋กญ์šฐ์„ , ๊ฒฐ์ธก ํด๋ฐฑ) -----------------------------------
1574
  overall = 1.0
1575
+ pref = pref_sr
1576
+ rec = pref * (1 - d1) if np.isfinite(pref) and np.isfinite(d1) else rec_sr
1577
+ intent = rec * (1 - d2) if np.isfinite(rec) and np.isfinite(d2) else intent_sr
 
1578
 
1579
  if np.isfinite(intent) and np.isfinite(d3):
1580
  buy = intent * (1 - d3)
 
1585
  else:
1586
  buy = intent
1587
 
1588
+ # ๋‹จ์กฐ๊ฐ์†Œ ๋ณด์žฅ + [0,1] ํด๋ฆฌํ•‘
1589
+ seq = [overall, _clip01(pref), _clip01(rec), _clip01(intent), _clip01(buy)]
 
 
 
 
1590
  for i in range(1, len(seq)):
1591
+ if np.isfinite(seq[i]) and np.isfinite(seq[i-1]) and seq[i] > seq[i-1]:
1592
+ seq[i] = seq[i-1]
 
1593
  overall, pref, rec, intent, buy = seq
1594
 
1595
+ # --- 4) ๋ผ๋ฒจ/๊ฐ’ ๊ตฌ์„ฑ(๋น„์–ด๋„ ํ•ญ์ƒ ๊ทธ๋ฆฌ๊ธฐ) -----------------------------------
1596
  labels, values = ["์ „์ฒด"], [overall]
1597
  if np.isfinite(pref): labels.append("์„ ํ˜ธ"); values.append(pref)
1598
  if np.isfinite(rec): labels.append("์ถ”์ฒœ"); values.append(rec)
 
1600
  if np.isfinite(buy): labels.append("๊ตฌ๋งค"); values.append(buy)
1601
 
1602
  if len(labels) <= 1:
1603
+ # ๋“œ๋กญ๋ฅ  ๊ธฐ๋ฐ˜์œผ๋กœ ์ตœ์†Œ 2๋‹จ๊ณ„๋ผ๋„ ๊ตฌ์„ฑ
1604
+ v = [1.0]
1605
+ if np.isfinite(d1): v.append(v[-1]*(1-d1))
1606
+ if np.isfinite(d2): v.append(v[-1]*(1-d2))
1607
+ if np.isfinite(d3): v.append(v[-1]*(1-d3))
1608
+ if len(v) == 1:
1609
+ est = _clip01(buy_sr if np.isfinite(buy_sr) else full_conv)
1610
+ v.append(0.0 if not np.isfinite(est) else est)
1611
+ names = ["์ „์ฒด","์„ ํ˜ธ","์ถ”์ฒœ","๊ตฌ๋งค์˜ํ–ฅ","๊ตฌ๋งค"][:len(v)]
1612
+ labels, values = names, v
1613
 
1614
  txtpos = ["inside" if v >= 0.07 else "outside" for v in values]
1615
 
 
1635
  connector=dict(line=dict(color="rgba(0,0,0,0.25)", width=0.6)),
1636
  ))
1637
 
1638
+ # โ€” ๋†’์ด ํ™•์žฅ & ์—ฌ๋ฐฑ ๋‹ค์ด์–ดํŠธ
1639
  fig.update_layout(
1640
  title="Funnel (๋ˆ„์ ์œจ)",
1641
+ height=FUNNEL_H if 'FUNNEL_H' in globals() else 420,
1642
+ margin=dict(l=6, r=6, t=26, b=14),
1643
  paper_bgcolor="#ffffff",
1644
  plot_bgcolor="#ffffff",
1645
  )
1646
  fig.update_xaxes(dtick=_auto_dtick(1.0), tickformat=".0%")
1647
+
1648
  return apply_dense_grid(fig, x_prob=True)
1649
 
1650
  def survival_curve_figure(row, df_tm, seg, mod, loy):
 
2944
  empty, empty, empty, empty, empty, empty, empty, empty
2945
  )
2946
 
2947
+ # ===================== ์‹คํ–‰ =====================
2948
  if __name__ == "__main__":
2949
+ base_port = int(os.getenv("PORT", "8059"))
2950
+ for i in range(5):
2951
+ try:
2952
+ app.run_server(host="0.0.0.0", port=base_port + i, debug=False, use_reloader=False)
2953
+ break
2954
+ except (OSError, SystemExit) as e:
2955
+ if "Address already in use" in str(e) or getattr(e, "code", None) == 1:
2956
+ continue
2957
+ raise
2958