GenAICoder commited on
Commit
e10ec93
·
verified ·
1 Parent(s): c6ae5bb

Update visualization/vintage_curve.py

Browse files
Files changed (1) hide show
  1. visualization/vintage_curve.py +299 -0
visualization/vintage_curve.py CHANGED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # visualizations/vintage_curves.py
2
+
3
+ import plotly.graph_objects as go
4
+ import plotly.express as px
5
+ from plotly.subplots import make_subplots
6
+ import pandas as pd
7
+ from metrics.metric_registry import METRIC_FUNCTIONS
8
+ from analytics.performance_analysis import generate_metric_view
9
+
10
+
11
+ def generate_delinquency_metric_chart(
12
+ df,
13
+ metric_name,
14
+ chart_type="line"
15
+ ):
16
+ """
17
+ Generate Plotly visualization for delinquency metrics across vintages.
18
+
19
+ Args:
20
+ df: Master dataframe with booking vintage and metric columns
21
+ metric_name: Name of metric (e.g., "30+@3", "30+@6", "60+@6", "Yr1 NCL")
22
+ chart_type: "line" for vintage curves, "bar" for bar chart
23
+
24
+ Returns:
25
+ Plotly figure object
26
+ """
27
+
28
+ # Generate metric data without grouping (overall view)
29
+ result = generate_metric_view(
30
+ df=df,
31
+ metric_name=metric_name,
32
+ group_col=None
33
+ )
34
+
35
+ # Sort by vintage
36
+ result = result.sort_values("booking_vintage")
37
+
38
+ # Identify the rate column
39
+ rate_col = [
40
+ col for col in result.columns
41
+ if "rate" in col.lower()
42
+ ][0]
43
+
44
+ if chart_type == "line":
45
+
46
+ fig = go.Figure()
47
+
48
+ fig.add_trace(
49
+ go.Scatter(
50
+ x=result["booking_vintage"],
51
+ y=result[rate_col],
52
+ mode="lines+markers",
53
+ name=metric_name,
54
+ line=dict(
55
+ width=3,
56
+ color="#1f77b4"
57
+ ),
58
+ marker=dict(
59
+ size=8,
60
+ symbol="circle"
61
+ ),
62
+ hovertemplate=(
63
+ "<b>Vintage: %{x}</b><br>" +
64
+ f"Rate: %{{y:.2f}}%<br>" +
65
+ "<extra></extra>"
66
+ )
67
+ )
68
+ )
69
+
70
+ fig.update_layout(
71
+ title=f"Vintage Curve: {metric_name}",
72
+ xaxis_title="Booking Vintage",
73
+ yaxis_title="Rate (%)",
74
+ hovermode="x unified",
75
+ plot_bgcolor="rgba(240,240,240,0.5)",
76
+ xaxis=dict(
77
+ showgrid=True,
78
+ gridwidth=1,
79
+ gridcolor="white"
80
+ ),
81
+ yaxis=dict(
82
+ showgrid=True,
83
+ gridwidth=1,
84
+ gridcolor="white"
85
+ ),
86
+ height=400,
87
+ template="plotly_white"
88
+ )
89
+
90
+ else: # bar chart
91
+
92
+ fig = px.bar(
93
+ result,
94
+ x="booking_vintage",
95
+ y=rate_col,
96
+ title=f"Delinquency Rate by Vintage: {metric_name}",
97
+ labels={
98
+ "booking_vintage": "Booking Vintage",
99
+ rate_col: "Rate (%)"
100
+ },
101
+ color=rate_col,
102
+ color_continuous_scale="Reds",
103
+ text=rate_col
104
+ )
105
+
106
+ fig.update_traces(
107
+ texttemplate="%{text:.2f}%",
108
+ textposition="outside"
109
+ )
110
+
111
+ fig.update_layout(
112
+ hovermode="x unified",
113
+ height=400,
114
+ template="plotly_white"
115
+ )
116
+
117
+ return fig
118
+
119
+
120
+ def generate_multi_metric_comparison(
121
+ df,
122
+ metrics=None
123
+ ):
124
+ """
125
+ Generate comparison chart for multiple delinquency metrics across vintages.
126
+
127
+ Args:
128
+ df: Master dataframe
129
+ metrics: List of metric names (default: ["30+@3", "30+@6", "60+@6", "Yr1 NCL"])
130
+
131
+ Returns:
132
+ Plotly figure with subplots
133
+ """
134
+
135
+ if metrics is None:
136
+ metrics = ["30+@3", "30+@6", "60+@6", "Yr1 NCL"]
137
+
138
+ # Create subplots
139
+ fig = make_subplots(
140
+ rows=2,
141
+ cols=2,
142
+ subplot_titles=metrics,
143
+ specs=[
144
+ [{"secondary_y": False}, {"secondary_y": False}],
145
+ [{"secondary_y": False}, {"secondary_y": False}]
146
+ ]
147
+ )
148
+
149
+ positions = [
150
+ (1, 1),
151
+ (1, 2),
152
+ (2, 1),
153
+ (2, 2)
154
+ ]
155
+
156
+ for metric, (row, col) in zip(metrics, positions):
157
+
158
+ result = generate_metric_view(
159
+ df=df,
160
+ metric_name=metric,
161
+ group_col=None
162
+ )
163
+
164
+ result = result.sort_values("booking_vintage")
165
+
166
+ rate_col = [
167
+ col_name for col_name in result.columns
168
+ if "rate" in col_name.lower()
169
+ ][0]
170
+
171
+ fig.add_trace(
172
+ go.Scatter(
173
+ x=result["booking_vintage"],
174
+ y=result[rate_col],
175
+ mode="lines+markers",
176
+ name=metric,
177
+ line=dict(width=2),
178
+ marker=dict(size=6),
179
+ hovertemplate=(
180
+ f"<b>{metric}</b><br>" +
181
+ "Vintage: %{x}<br>" +
182
+ "Rate: %{y:.2f}%<br>" +
183
+ "<extra></extra>"
184
+ )
185
+ ),
186
+ row=row,
187
+ col=col
188
+ )
189
+
190
+ fig.update_xaxes(
191
+ title_text="Vintage",
192
+ row=row,
193
+ col=col
194
+ )
195
+
196
+ fig.update_yaxes(
197
+ title_text="Rate (%)",
198
+ row=row,
199
+ col=col
200
+ )
201
+
202
+ fig.update_layout(
203
+ title_text="Delinquency Metrics Comparison Across Vintages",
204
+ height=800,
205
+ showlegend=False,
206
+ template="plotly_white",
207
+ hovermode="x unified"
208
+ )
209
+
210
+ return fig
211
+
212
+
213
+ def generate_segment_delinquency_curve(
214
+ df,
215
+ metric_name,
216
+ category
217
+ ):
218
+ """
219
+ Generate vintage curve for a specific delinquency metric segmented by category.
220
+
221
+ Args:
222
+ df: Master dataframe
223
+ metric_name: Metric name
224
+ category: Segmentation category (e.g., "fico_band", "sourcing_channel")
225
+
226
+ Returns:
227
+ Plotly figure with multiple traces (one per category value)
228
+ """
229
+
230
+ result = generate_metric_view(
231
+ df=df,
232
+ metric_name=metric_name,
233
+ group_col=category
234
+ )
235
+
236
+ result = result.sort_values(["booking_vintage", category])
237
+
238
+ rate_col = [
239
+ col for col in result.columns
240
+ if "rate" in col.lower()
241
+ ][0]
242
+
243
+ fig = go.Figure()
244
+
245
+ # Get unique categories
246
+ categories = result[category].unique()
247
+
248
+ # Color palette
249
+ colors = px.colors.qualitative.Set1
250
+
251
+ for idx, cat in enumerate(sorted(categories)):
252
+
253
+ cat_data = result[result[category] == cat]
254
+ color = colors[idx % len(colors)]
255
+
256
+ fig.add_trace(
257
+ go.Scatter(
258
+ x=cat_data["booking_vintage"],
259
+ y=cat_data[rate_col],
260
+ mode="lines+markers",
261
+ name=str(cat),
262
+ line=dict(width=2, color=color),
263
+ marker=dict(size=6, color=color),
264
+ hovertemplate=(
265
+ f"<b>{cat}</b><br>" +
266
+ "Vintage: %{x}<br>" +
267
+ "Rate: %{y:.2f}%<br>" +
268
+ "<extra></extra>"
269
+ )
270
+ )
271
+ )
272
+
273
+ fig.update_layout(
274
+ title=f"{metric_name} by {category.replace('_', ' ').title()}",
275
+ xaxis_title="Booking Vintage",
276
+ yaxis_title="Rate (%)",
277
+ hovermode="x unified",
278
+ plot_bgcolor="rgba(240,240,240,0.5)",
279
+ xaxis=dict(
280
+ showgrid=True,
281
+ gridwidth=1,
282
+ gridcolor="white"
283
+ ),
284
+ yaxis=dict(
285
+ showgrid=True,
286
+ gridwidth=1,
287
+ gridcolor="white"
288
+ ),
289
+ height=500,
290
+ template="plotly_white",
291
+ legend=dict(
292
+ x=1.05,
293
+ y=1,
294
+ xanchor="left",
295
+ yanchor="top"
296
+ )
297
+ )
298
+
299
+ return fig