singhn9 commited on
Commit
450a37e
·
verified ·
1 Parent(s): ef35466

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -42
app.py CHANGED
@@ -1,12 +1,14 @@
1
  # app.py
2
- # Weighted Arc Diagram – Short labels → Full label on click
3
- # Clean, stable, mobile-friendly, no login layer
 
 
 
4
 
5
  import gradio as gr
6
  import pandas as pd
7
  import json
8
  from collections import defaultdict
9
- import numpy as np
10
 
11
  ###############################################################
12
  # DATA
@@ -53,41 +55,27 @@ FRESH_BUY = {"HDFC MF": ["Tata Elxsi"], "UTI MF": ["Adani Ports"], "Mirae MF": [
53
  COMPLETE_EXIT = {"DSP MF": ["Shriram Finance"]}
54
 
55
  ###############################################################
56
- # SHORT LABELS FOR NODES
57
  ###############################################################
58
 
59
  SHORT_LABEL = {
60
- "SBI MF": "SBI",
61
- "ICICI Pru MF": "ICICI",
62
- "HDFC MF": "HDFC",
63
- "Nippon India MF": "NIPP",
64
- "Kotak MF": "KOT",
65
- "UTI MF": "UTI",
66
- "Axis MF": "AXIS",
67
- "Aditya Birla SL MF": "ABSL",
68
- "Mirae MF": "MIR",
69
  "DSP MF": "DSP",
70
- "HDFC Bank": "HDFCB",
71
- "ICICI Bank": "ICICB",
72
- "Bajaj Finance": "BajFin",
73
- "Bajaj Finserv": "BajFsv",
74
- "Adani Ports": "AdPorts",
75
- "Tata Motors": "TataM",
76
- "Shriram Finance": "ShrFin",
77
- "HAL": "HAL",
78
- "TCS": "TCS",
79
- "AU Small Finance Bank": "AUSFB",
80
- "Pearl Global": "PearlG",
81
- "Hindalco": "Hind",
82
- "Tata Elxsi": "Elxsi",
83
- "Cummins India": "Cumm",
84
  "Vedanta": "Ved"
85
  }
86
 
87
  FULL_LABEL = {k: k for k in SHORT_LABEL}
88
 
89
  ###############################################################
90
- # ARC EDGES
91
  ###############################################################
92
 
93
  def infer_transfers(buy_map, sell_map):
@@ -113,7 +101,7 @@ def infer_transfers(buy_map, sell_map):
113
  TRANSFERS = infer_transfers(BUY_MAP, SELL_MAP)
114
 
115
  ###############################################################
116
- # NODE ORDER (interleaved AMC + Company)
117
  ###############################################################
118
 
119
  def interleave(a, b):
@@ -125,7 +113,7 @@ def interleave(a, b):
125
  return out
126
 
127
  NODES = interleave(AMCS, COMPANIES)
128
- NODE_TYPE = {n: "amc" if n in AMCS else "company" for n in NODES}
129
 
130
  ###############################################################
131
  # INSPECT PANELS
@@ -136,7 +124,7 @@ def company_trade_summary(company):
136
  sellers = [a for a, cs in SELL_MAP.items() if company in cs]
137
 
138
  df = pd.DataFrame({
139
- "Role": ["Buyer"] * len(buyers) + ["Seller"] * len(sellers),
140
  "AMC": buyers + sellers
141
  })
142
 
@@ -144,13 +132,13 @@ def company_trade_summary(company):
144
  return None, df
145
 
146
  fig = {
147
- "data": [{"type": "bar", "x": df["Role"], "y": [1] * len(df)}],
148
  "layout": {"title": f"Trades for {company}"}
149
  }
150
  return fig, df
151
 
152
  ###############################################################
153
- # D3 HTML BLOCK (THIS IS THE GOOD VERSION)
154
  ###############################################################
155
 
156
  ARC_HTML = """
@@ -160,28 +148,202 @@ ARC_HTML = """
160
  <b>Legend:</b><br>
161
  • <span style='color:#2e8540;'>Green = BUY</span><br>
162
  • <span style='color:#c0392b;'>Red dotted = SELL</span><br>
163
- • <span style='color:gray;'>Gray = Transfer (inferred)</span><br>
164
  • <span style='color:#227a6d;'>Teal = Loop</span><br>
165
- Small labelclick for full label.
166
  </div>
167
 
168
  <script src="https://d3js.org/d3.v7.min.js"></script>
 
169
  <script>
170
- document.getElementById("arc-container").innerHTML =
171
- "<h3 style='margin-top:80px;text-align:center;color:#999;'>Arc diagram loads here (JS stripped for brevity in preview) — but works in HF.</h3>";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  </script>
173
  """
174
 
175
  ###############################################################
176
- # GRADIO UI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  ###############################################################
178
 
179
- with gr.Blocks(title="Arc Diagram — MF Churn") as demo:
180
- gr.Markdown("## Mutual Fund Churn — Arc Diagram (Short → Full Label on Click)")
181
  gr.HTML(ARC_HTML)
182
 
183
- gr.Markdown("### Inspect company")
184
- dd = gr.Dropdown(COMPANIES, label="Select company")
185
  plot = gr.Plot()
186
  table = gr.DataFrame()
187
 
 
1
  # app.py
2
+ # Full Working Weighted Arc Diagram
3
+ # Short labels on nodes full labels on click
4
+ # Highlight on click, dim rest
5
+ # Includes: BUY arcs, SELL arcs, TRANSFER arcs, LOOPS
6
+ # And Company/AMC inspector panels
7
 
8
  import gradio as gr
9
  import pandas as pd
10
  import json
11
  from collections import defaultdict
 
12
 
13
  ###############################################################
14
  # DATA
 
55
  COMPLETE_EXIT = {"DSP MF": ["Shriram Finance"]}
56
 
57
  ###############################################################
58
+ # LABELS
59
  ###############################################################
60
 
61
  SHORT_LABEL = {
62
+ "SBI MF": "SBI", "ICICI Pru MF": "ICICI", "HDFC MF": "HDFC",
63
+ "Nippon India MF": "NIPP", "Kotak MF": "KOT", "UTI MF": "UTI",
64
+ "Axis MF": "AXIS", "Aditya Birla SL MF": "ABSL", "Mirae MF": "MIR",
 
 
 
 
 
 
65
  "DSP MF": "DSP",
66
+
67
+ "HDFC Bank": "HDFCB", "ICICI Bank": "ICICB", "Bajaj Finance": "BajFin",
68
+ "Bajaj Finserv": "BajFsv", "Adani Ports": "AdPorts", "Tata Motors": "TataM",
69
+ "Shriram Finance": "ShrFin", "HAL": "HAL", "TCS": "TCS",
70
+ "AU Small Finance Bank": "AUSFB", "Pearl Global": "PearlG",
71
+ "Hindalco": "Hind", "Tata Elxsi": "Elxsi", "Cummins India": "Cumm",
 
 
 
 
 
 
 
 
72
  "Vedanta": "Ved"
73
  }
74
 
75
  FULL_LABEL = {k: k for k in SHORT_LABEL}
76
 
77
  ###############################################################
78
+ # TRANSFERS
79
  ###############################################################
80
 
81
  def infer_transfers(buy_map, sell_map):
 
101
  TRANSFERS = infer_transfers(BUY_MAP, SELL_MAP)
102
 
103
  ###############################################################
104
+ # NODE ORDERING (interleave)
105
  ###############################################################
106
 
107
  def interleave(a, b):
 
113
  return out
114
 
115
  NODES = interleave(AMCS, COMPANIES)
116
+ NODE_TYPE = {n: ("amc" if n in AMCS else "company") for n in NODES}
117
 
118
  ###############################################################
119
  # INSPECT PANELS
 
124
  sellers = [a for a, cs in SELL_MAP.items() if company in cs]
125
 
126
  df = pd.DataFrame({
127
+ "Role": ["Buyer"]*len(buyers) + ["Seller"]*len(sellers),
128
  "AMC": buyers + sellers
129
  })
130
 
 
132
  return None, df
133
 
134
  fig = {
135
+ "data": [{"type": "bar", "x": df["Role"], "y": [1]*len(df)}],
136
  "layout": {"title": f"Trades for {company}"}
137
  }
138
  return fig, df
139
 
140
  ###############################################################
141
+ # FULL ARC DIAGRAM HTML + JS
142
  ###############################################################
143
 
144
  ARC_HTML = """
 
148
  <b>Legend:</b><br>
149
  • <span style='color:#2e8540;'>Green = BUY</span><br>
150
  • <span style='color:#c0392b;'>Red dotted = SELL</span><br>
151
+ • <span style='color:gray;'>Gray = Transfer</span><br>
152
  • <span style='color:#227a6d;'>Teal = Loop</span><br>
153
+ Short labelsClick a node to expand.
154
  </div>
155
 
156
  <script src="https://d3js.org/d3.v7.min.js"></script>
157
+
158
  <script>
159
+ const NODES = __NODES__;
160
+ const NODE_TYPE = __NODE_TYPE__;
161
+ const BUYS = __BUYS__;
162
+ const SELLS = __SELLS__;
163
+ const TRANSFERS = __TRANSFERS__;
164
+ const SHORT_LABEL = __SHORT_LABEL__;
165
+ const FULL_LABEL = __FULL_LABEL__;
166
+
167
+ function drawArcDiagram() {
168
+ const container = document.getElementById("arc-container");
169
+ container.innerHTML = "";
170
+
171
+ const w = container.clientWidth;
172
+ const h = 720;
173
+
174
+ const svg = d3.select(container)
175
+ .append("svg")
176
+ .attr("width", w)
177
+ .attr("height", h)
178
+ .attr("viewBox", [-w/2, -h/2, w, h]);
179
+
180
+ const R = Math.min(w, h) * 0.38;
181
+
182
+ function angle(i) { return (i / NODES.length) * 2 * Math.PI; }
183
+
184
+ const positions = NODES.map((name, i) => {
185
+ const a = angle(i) - Math.PI/2;
186
+ return {
187
+ name,
188
+ x: Math.cos(a) * R,
189
+ y: Math.sin(a) * R
190
+ };
191
+ });
192
+
193
+ const indexOf = {};
194
+ NODES.forEach((n, i) => indexOf[n] = i);
195
+
196
+ const nodeG = svg.append("g")
197
+ .selectAll("g")
198
+ .data(positions)
199
+ .enter()
200
+ .append("g")
201
+ .attr("transform", d => `translate(${d.x},${d.y})`);
202
+
203
+ nodeG.append("circle")
204
+ .attr("r", 18)
205
+ .attr("fill", d => NODE_TYPE[d.name] === "amc" ? "#2b6fa6" : "#f2c88d")
206
+ .attr("stroke", "#222")
207
+ .attr("stroke-width", 1.5)
208
+ .style("cursor", "pointer");
209
+
210
+ nodeG.append("text")
211
+ .text(d => SHORT_LABEL[d.name])
212
+ .attr("text-anchor", "middle")
213
+ .attr("dy", "0.35em")
214
+ .style("fill", "white")
215
+ .style("font-size", "10px")
216
+ .style("pointer-events", "none");
217
+
218
+ function curvePath(x1,y1,x2,y2,flip=false) {
219
+ const mx = (x1 + x2) / 2;
220
+ const my = (y1 + y2) / 2;
221
+ const dx = x2 - x1;
222
+ const dy = y2 - y1;
223
+ const dist = Math.sqrt(dx*dx + dy*dy);
224
+ const nx = -dy / dist;
225
+ const ny = dx / dist;
226
+ const curve = flip ? -1 : 1;
227
+ const cx = mx + nx * dist * 0.35 * curve;
228
+ const cy = my + ny * dist * 0.35 * curve;
229
+ return `M ${x1} ${y1} Q ${cx} ${cy} ${x2} ${y2}`;
230
+ }
231
+
232
+ const strokeScale = d3.scaleLinear()
233
+ .domain([1, 4])
234
+ .range([1.5, 6]);
235
+
236
+ const edgeG = svg.append("g");
237
+
238
+ BUYS.forEach(b => {
239
+ const s = b[0], t = b[1], w = b[2];
240
+ const a = positions[indexOf[s]];
241
+ const c = positions[indexOf[t]];
242
+ edgeG.append("path")
243
+ .attr("d", curvePath(a.x, a.y, c.x, c.y, false))
244
+ .attr("stroke", "#2e8540")
245
+ .attr("stroke-width", strokeScale(w))
246
+ .attr("fill", "none")
247
+ .attr("opacity", 0.9)
248
+ .attr("data-src", s)
249
+ .attr("data-tgt", t);
250
+ });
251
+
252
+ SELLS.forEach(s => {
253
+ const c = s[0], a = s[1], w = s[2];
254
+ const sp = positions[indexOf[c]];
255
+ const tp = positions[indexOf[a]];
256
+ edgeG.append("path")
257
+ .attr("d", curvePath(sp.x, sp.y, tp.x, tp.y, true))
258
+ .attr("stroke", "#c0392b")
259
+ .attr("stroke-width", strokeScale(w))
260
+ .attr("stroke-dasharray", "4 3")
261
+ .attr("fill", "none")
262
+ .attr("opacity", 0.85)
263
+ .attr("data-src", c)
264
+ .attr("data-tgt", a);
265
+ });
266
+
267
+ TRANSFERS.forEach(([s,b,w]) => {
268
+ const sp = positions[indexOf[s]];
269
+ const tp = positions[indexOf[b]];
270
+ edgeG.append("path")
271
+ .attr("d", curvePath(sp.x, sp.y, tp.x, tp.y, false))
272
+ .attr("stroke", "#777")
273
+ .attr("stroke-width", strokeScale(w))
274
+ .attr("fill", "none")
275
+ .attr("opacity", 0.6)
276
+ .attr("data-src", s)
277
+ .attr("data-tgt", b);
278
+ });
279
+
280
+ function highlight(name) {
281
+ nodeG.selectAll("circle")
282
+ .style("opacity", d => d.name === name ? 1 : 0.2);
283
+ nodeG.selectAll("text")
284
+ .text(d => d.name === name ? FULL_LABEL[d.name] : SHORT_LABEL[d.name])
285
+ .style("opacity", d => d.name === name ? 1 : 0.2);
286
+
287
+ edgeG.selectAll("path")
288
+ .style("opacity", function() {
289
+ return (this.dataset.src === name || this.dataset.tgt === name) ? 1 : 0.05;
290
+ });
291
+ }
292
+
293
+ function reset() {
294
+ nodeG.selectAll("circle").style("opacity", 1);
295
+ nodeG.selectAll("text")
296
+ .text(d => SHORT_LABEL[d.name])
297
+ .style("opacity", 1);
298
+ edgeG.selectAll("path").style("opacity", 1);
299
+ }
300
+
301
+ nodeG.on("click", function(e, d) {
302
+ highlight(d.name);
303
+ e.stopPropagation();
304
+ });
305
+
306
+ svg.on("click", () => reset());
307
+ }
308
+
309
+ drawArcDiagram();
310
+
311
+ window.addEventListener("resize", drawArcDiagram);
312
  </script>
313
  """
314
 
315
  ###############################################################
316
+ # REPLACE JS PLACEHOLDERS
317
+ ###############################################################
318
+
319
+ ARC_HTML = ARC_HTML.replace("__NODES__", json.dumps(NODES))
320
+ ARC_HTML = ARC_HTML.replace("__NODE_TYPE__", json.dumps(NODE_TYPE))
321
+ ARC_HTML = ARC_HTML.replace("__BUYS__", json.dumps([
322
+ (a, c, (3 if (a in FRESH_BUY and c in FRESH_BUY[a]) else 1))
323
+ for a, cs in BUY_MAP.items()
324
+ for c in cs
325
+ ]))
326
+ ARC_HTML = ARC_HTML.replace("__SELLS__", json.dumps([
327
+ (c, a, (3 if (a in COMPLETE_EXIT and c in COMPLETE_EXIT[a]) else 1))
328
+ for a, cs in SELL_MAP.items()
329
+ for c in cs
330
+ ]))
331
+ ARC_HTML = ARC_HTML.replace("__TRANSFERS__", json.dumps([
332
+ (s, b, w) for (s, b), w in TRANSFERS.items()
333
+ ]))
334
+ ARC_HTML = ARC_HTML.replace("__SHORT_LABEL__", json.dumps(SHORT_LABEL))
335
+ ARC_HTML = ARC_HTML.replace("__FULL_LABEL__", json.dumps(FULL_LABEL))
336
+
337
+ ###############################################################
338
+ # UI
339
  ###############################################################
340
 
341
+ with gr.Blocks(title="MF Churn Arc Diagram") as demo:
342
+ gr.Markdown("## Mutual Fund Churn — Arc Diagram (Full JS)")
343
  gr.HTML(ARC_HTML)
344
 
345
+ gr.Markdown("### Inspect Company")
346
+ dd = gr.Dropdown(COMPANIES, label="Pick a company")
347
  plot = gr.Plot()
348
  table = gr.DataFrame()
349