Britzzy commited on
Commit
d3d0acb
·
verified ·
1 Parent(s): 76614c0

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -157
app.py DELETED
@@ -1,157 +0,0 @@
1
- import streamlit as st
2
- import plotly.graph_objects as go
3
- import pandas as pd
4
- import numpy as np
5
- import os
6
- import xgboost as xgb
7
- import shap
8
-
9
- st.set_page_config(page_title="FairValue Transfer Cap Estimator", layout="wide")
10
-
11
- st.title("FairValue Transfer Cap Estimator")
12
- st.markdown("A rigorous, data-driven 'Transfer Ceiling Calculator' grounded in ML and Hedonic Pricing Theory.")
13
-
14
- @st.cache_resource
15
- def load_data_and_model():
16
- df_path = "data/processed/app_features.csv"
17
- if not os.path.exists(df_path):
18
- return None, None
19
- df = pd.read_csv(df_path)
20
-
21
- # Standardize ALL possible market value names to match model
22
- # We look for ANY column containing 'market_value' and 'eur'
23
- mv_cols = [c for c in df.columns if 'market_value' in c.lower()]
24
- if mv_cols:
25
- target_col = next((c for c in mv_cols if 'eur' in c.lower()), mv_cols[0])
26
- df.rename(columns={target_col: 'market_value_in_eur'}, inplace=True)
27
-
28
- # Deduplicate early
29
- df = df.loc[:, ~df.columns.duplicated()].copy()
30
-
31
- # Load model
32
- model = xgb.XGBRegressor()
33
- try:
34
- path = "fairvalue_xgboost.json"
35
- if not os.path.exists(path):
36
- path = "FairValue_xgboost.json"
37
-
38
- if os.path.exists(path):
39
- model.load_model(path)
40
- else:
41
- return df, "MODEL_FILE_NOT_FOUND"
42
- except Exception as e:
43
- return df, f"LOAD_ERROR: {str(e)}"
44
-
45
- return df, model
46
-
47
- df, model = load_data_and_model()
48
-
49
- if isinstance(model, str):
50
- st.error(f"⚠️ {model}")
51
- st.stop()
52
- if df is None:
53
- st.error("⚠️ Data file not found.")
54
- st.stop()
55
-
56
- # ======= Sidebar =======
57
- st.sidebar.header("Player Transfer Profile")
58
- input_mode = st.sidebar.radio("Input Mode", ["Select Existing Player", "Create Custom Player"])
59
-
60
- name_col = next((c for c in ['name', 'name_x', 'Player_Name', 'Name'] if c in df.columns), None)
61
-
62
- if input_mode == "Select Existing Player":
63
- if name_col:
64
- player_list = sorted(df[name_col].unique().tolist())
65
- selected_name = st.sidebar.selectbox("Target Database Player", player_list)
66
- player_data = df[df[name_col] == selected_name].iloc[0:1].copy()
67
- else:
68
- st.sidebar.error("Player name column missing.")
69
- st.stop()
70
-
71
- st.sidebar.markdown("---")
72
- st.sidebar.markdown("**Simulation Parameters**")
73
-
74
- # Use position-based access (iloc) for everything to avoid KeyError
75
- contract_default = max(0.5, min(6.0, float(player_data['Contract_Years_Left'].iloc[0])))
76
- age_default = max(16, min(40, int(player_data['Age'].iloc[0])))
77
-
78
- injury_col = next((c for c in ['Injury_Days_Total_24m', 'Injury_Days'] if c in player_data.columns), None)
79
- injury_default = 0
80
- if injury_col:
81
- injury_default = max(0, min(500, int(player_data[injury_col].iloc[0])))
82
-
83
- contract_years = st.sidebar.slider("Contract Years Remaining", 0.5, 6.0, contract_default, 0.5)
84
- age = st.sidebar.slider("Age", 16, 40, age_default)
85
- injuries = st.sidebar.number_input("Injury missed days (24m)", 0, 500, injury_default)
86
-
87
- else:
88
- st.sidebar.markdown("---")
89
- st.sidebar.markdown("**Custom Player Attributes**")
90
- player_data = df.median(numeric_only=True).to_frame().T
91
-
92
- contract_years = st.sidebar.slider("Contract Years Remaining", 0.5, 6.0, 2.0, 0.5)
93
- age = st.sidebar.slider("Age", 16, 40, 24)
94
- injuries = st.sidebar.number_input("Injury missed days (24m)", 0, 500, 10)
95
-
96
- if 'market_value_in_eur' in player_data.columns:
97
- m_val = st.sidebar.number_input("Current Market Value Estimation (£m)", 1.0, 200.0, 20.0)
98
- player_data['market_value_in_eur'] = (m_val * 1_000_000) / 0.85
99
-
100
- asking_price = st.sidebar.number_input("Selling Club Asking Price (£m)", 1.0, 300.0, 45.0)
101
-
102
- # Build X_infer with safety
103
- player_data['Contract_Years_Left'] = contract_years
104
- player_data['Age'] = age
105
- if 'Injury_Days_Total_24m' in player_data.columns:
106
- player_data['Injury_Days_Total_24m'] = injuries
107
-
108
- expected_cols = model.feature_names_in_
109
- X_infer = player_data.reindex(columns=expected_cols, fill_value=0)
110
-
111
- if st.button("Calculate Prediction", type="primary"):
112
- # SAFE CHECK FOR ANY KEYERRORS
113
- mv_v = 0.0
114
- if 'market_value_in_eur' in X_infer.columns:
115
- mv_v = X_infer['market_value_in_eur'].iloc[0]
116
-
117
- if mv_v == 0:
118
- st.warning("⚠️ Market Value is 0.0. Prediction may be inaccurate.")
119
-
120
- log_pv = model.predict(X_infer)[0]
121
- baseline_pv = np.expm1(log_pv)
122
-
123
- # UI Logic
124
- baseline_pv_m = baseline_pv / 1_000_000
125
- conservative_bound = baseline_pv * 0.85
126
-
127
- risk_discount = 0.0
128
- if contract_years < 1.5: risk_discount += 0.20
129
- if age > 30: risk_discount += 0.15
130
- if injuries > 60: risk_discount += 0.10
131
-
132
- hard_cap = conservative_bound * (1 - risk_discount)
133
- hard_cap_m = hard_cap / 1_000_000
134
-
135
- col1, col2 = st.columns([2, 1])
136
- with col1:
137
- st.subheader("Price Range Recommendation")
138
- fig = go.Figure(go.Indicator(
139
- mode="gauge+number+delta",
140
- value=asking_price,
141
- delta={'reference': hard_cap_m, 'increasing': {'color': "red"}},
142
- title={'text': "Asking Price vs Hard Cap (£m)"},
143
- gauge={'axis': {'range': [0, max(asking_price, 100)]}, 'threshold': {'line': {'color': "white", 'width': 4}, 'value': hard_cap_m}}
144
- ))
145
- st.plotly_chart(fig, use_container_width=True)
146
-
147
- with col2:
148
- st.subheader("Metrics Breakdown")
149
- st.metric("Predicted Market Value (Baseline)", f"£{baseline_pv_m:.1f}m")
150
- st.metric("Recommended Hard Cap", f"£{hard_cap_m:.1f}m")
151
- if asking_price > hard_cap_m:
152
- st.error(f"Winner's Curse Risk: £{asking_price-hard_cap_m:.1f}m Overpay")
153
-
154
- st.markdown("---")
155
- st.subheader("Model Performance Assessment")
156
- st.markdown(f"**Training Set:** `{len(df):,}` transfers | **Features:** `{len(expected_cols)}` cols")
157
- st.markdown("**Validation MAE:** `~£23,980,000` | **Engine:** XGBoost + SHAP")