Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 19 additions & 7 deletions dashboard/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
import plotly.express as px
import streamlit as st

from dashboard.queue_data import capacity_views

ROOT = Path(__file__).resolve().parents[1]
SUMMARY_PATH = ROOT / "artifacts/figures/summary.json"
CAPACITY_PATH = ROOT / "artifacts/figures/capacity_results.csv"
THRESHOLD_PATH = ROOT / "artifacts/figures/threshold_policies.csv"
SCORED_PATH = ROOT / "artifacts/figures/scored_test.csv"
QUEUE_PATH = ROOT / "artifacts/figures/review_queue.csv"
ERROR_PATH = ROOT / "artifacts/figures/error_analysis.csv"

st.set_page_config(page_title="RiskQueue", page_icon="▦", layout="wide")
Expand All @@ -37,7 +38,6 @@
capacity = pd.read_csv(CAPACITY_PATH)
thresholds = pd.read_csv(THRESHOLD_PATH)
scored = pd.read_csv(SCORED_PATH)
queue = pd.read_csv(QUEUE_PATH)
errors = pd.read_csv(ERROR_PATH)
best = summary["metrics"][summary["best_model"]]

Expand Down Expand Up @@ -139,12 +139,11 @@
min(250, int(capacity.capacity.max())),
25,
)
nearest = capacity.iloc[
(capacity.capacity - selected_capacity).abs().argsort()[:3]
].sort_values("fraud_value_capture", ascending=False)
scorecard, ranked_queue = capacity_views(scored, selected_capacity)
scorecard = scorecard.sort_values("fraud_value_capture", ascending=False)
st.subheader("Policy scorecard")
st.dataframe(
nearest[
scorecard[
[
"strategy",
"capacity",
Expand All @@ -170,7 +169,20 @@
options=["critical", "high", "guarded", "low"],
default=["critical", "high", "guarded", "low"],
)
shown_queue = queue[queue.risk_band.isin(selected_bands)].head(display_count)
shown_queue = ranked_queue[ranked_queue.risk_band.isin(selected_bands)].head(display_count)
shown_queue = shown_queue[
[
"transaction_id",
"step",
"type",
"amount",
"fraud_probability",
"expected_loss",
"risk_band",
"rank",
"isFraud",
]
]
st.dataframe(
shown_queue.style.format(
{
Expand Down
18 changes: 18 additions & 0 deletions dashboard/queue_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from __future__ import annotations

import pandas as pd

from riskqueue.decisions.review_queue import build_review_queue, queue_metrics


def capacity_views(scored: pd.DataFrame, capacity: int) -> tuple[pd.DataFrame, pd.DataFrame]:
"""Calculate the scorecard and expected-loss queue for the selected capacity."""
probabilities = scored["fraud_probability"].to_numpy()
rows = []
selected_queue = None
for strategy in ("probability", "expected_loss", "expected_review_value"):
queue = build_review_queue(scored, probabilities, capacity, strategy)
rows.append({"strategy": strategy, "capacity": capacity, **queue_metrics(queue, scored)})
if strategy == "expected_loss":
selected_queue = queue
return pd.DataFrame(rows), selected_queue
24 changes: 24 additions & 0 deletions tests/test_dashboard_queue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from pathlib import Path

import pandas as pd
import pytest

from dashboard.queue_data import capacity_views


def test_capacity_views_use_selected_capacity():
scored_path = Path(__file__).resolve().parents[1] / "artifacts/figures/scored_test.csv"
scored = pd.read_csv(scored_path)

scorecard, queue = capacity_views(scored, 75)

assert set(scorecard.strategy) == {
"probability",
"expected_loss",
"expected_review_value",
}
assert scorecard.capacity.tolist() == [75, 75, 75]
assert scorecard.reviews.tolist() == [75, 75, 75]
assert len(queue) == 75
assert queue["rank"].tolist() == list(range(1, 76))
assert queue.iloc[0].expected_loss == pytest.approx(scored.expected_loss.max())
Loading