rntc commited on
Commit
1c5482c
·
verified ·
1 Parent(s): 04017d9

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +108 -439
app.py CHANGED
@@ -1,493 +1,162 @@
1
  """
2
  Gradio app to explore pancreas cancer clinical report annotations.
3
- Loads data from rntc/biomed-fr-pancreas-annotations on HuggingFace.
4
  """
5
 
6
  import gradio as gr
7
  from datasets import load_dataset
8
- from difflib import SequenceMatcher
9
 
10
  # Load the dataset
11
- print("Loading dataset from HuggingFace...")
12
  dataset = load_dataset("rntc/biomed-fr-pancreas-annotations", split="train")
13
  print(f"Loaded {len(dataset)} samples")
14
 
15
-
16
- def fuzzy_find_span(text: str, span: str, threshold: float = 0.85) -> tuple:
17
- """
18
- Find a span in text with fuzzy matching.
19
- Returns (start, end) or None if not found.
20
- """
21
- # First try exact match
22
- idx = text.find(span)
23
- if idx != -1:
24
- return (idx, idx + len(span))
25
-
26
- # Try fuzzy match with sliding window
27
- span_len = len(span)
28
- if span_len < 10 or span_len > len(text):
29
- return None
30
-
31
- best_ratio = 0
32
- best_pos = None
33
-
34
- # Use a window slightly larger than span
35
- window_size = min(span_len + 20, len(text))
36
-
37
- for i in range(0, len(text) - span_len + 1, max(1, span_len // 4)):
38
- window = text[i:i + window_size]
39
- ratio = SequenceMatcher(None, span, window[:span_len]).ratio()
40
- if ratio > best_ratio and ratio >= threshold:
41
- best_ratio = ratio
42
- best_pos = i
43
-
44
- if best_pos is not None:
45
- return (best_pos, best_pos + span_len)
46
-
47
- return None
48
 
49
 
50
- def escape_html(text: str) -> str:
51
- """Escape HTML special characters."""
52
  if not text:
53
  return ""
54
- return (str(text)
55
- .replace("&", "&amp;")
56
- .replace("<", "&lt;")
57
- .replace(">", "&gt;")
58
- .replace('"', "&quot;"))
59
-
60
-
61
- # Soft pastel colors for better readability
62
- COLORS = [
63
- "#FFE082", # amber
64
- "#A5D6A7", # green
65
- "#90CAF9", # blue
66
- "#FFAB91", # deep orange
67
- "#CE93D8", # purple
68
- "#80DEEA", # cyan
69
- "#C5E1A5", # light green
70
- "#FFCC80", # orange
71
- "#B39DDB", # deep purple
72
- "#81D4FA", # light blue
73
- "#EF9A9A", # red
74
- "#FFF59D", # yellow
75
- "#F48FB1", # pink
76
- "#80CBC4", # teal
77
- "#BCAAA4", # brown
78
- ]
79
 
80
 
81
- def highlight_spans_in_text(cr_text: str, annotation: dict) -> str:
82
- """
83
- Highlight spans in the CR text based on annotations.
84
- Returns HTML with highlighted spans.
85
- """
86
  if not cr_text or not annotation:
87
- return f"<div class='cr-text'>{escape_html(cr_text)}</div>"
88
 
89
- # Collect all spans with their variable names
90
- spans_to_highlight = []
91
  for var_name, var_data in annotation.items():
92
  if var_data and isinstance(var_data, dict):
93
  span = var_data.get("span")
94
  value = var_data.get("value")
95
- if span and value and len(span) >= 5: # Skip very short spans
96
- spans_to_highlight.append({
97
- "span": span,
98
- "var_name": var_name,
 
99
  "value": str(value)
100
  })
101
 
102
- if not spans_to_highlight:
103
- return f"<div class='cr-text'>{escape_html(cr_text)}</div>"
104
-
105
- # Sort spans by length (longest first) to prioritize longer matches
106
- spans_to_highlight.sort(key=lambda x: len(x["span"]), reverse=True)
107
-
108
- # Find spans in text (with fuzzy matching)
109
- found_spans = []
110
- for item in spans_to_highlight:
111
- result = fuzzy_find_span(cr_text, item["span"])
112
- if result:
113
- start, end = result
114
- found_spans.append({
115
- "start": start,
116
- "end": end,
117
- "var_name": item["var_name"],
118
- "value": item["value"],
119
- "span": cr_text[start:end] # Use actual text from CR
120
- })
121
-
122
- if not found_spans:
123
- return f"<div class='cr-text'>{escape_html(cr_text)}</div>"
124
-
125
- # Sort by start position
126
- found_spans.sort(key=lambda x: x["start"])
127
-
128
- # Remove overlapping spans (keep the first/longest one)
129
- non_overlapping = []
130
- for span in found_spans:
131
- if not non_overlapping:
132
- non_overlapping.append(span)
133
- elif span["start"] >= non_overlapping[-1]["end"]:
134
- non_overlapping.append(span)
135
-
136
- # Assign colors to variable names
137
- var_colors = {}
138
  color_idx = 0
139
- for span in non_overlapping:
140
- if span["var_name"] not in var_colors:
141
- var_colors[span["var_name"]] = COLORS[color_idx % len(COLORS)]
 
 
 
 
 
 
142
  color_idx += 1
143
 
144
- # Build HTML with highlights
145
- html_parts = []
146
- last_end = 0
147
-
148
- for span in non_overlapping:
149
- # Add text before this span
150
- if span["start"] > last_end:
151
- html_parts.append(escape_html(cr_text[last_end:span["start"]]))
152
-
153
- # Add highlighted span
154
- color = var_colors[span["var_name"]]
155
- var_label = span["var_name"].replace("_", " ").replace(" ", " ").title()
156
- tooltip = f"{var_label}\\n→ {span['value']}"
157
-
158
- html_parts.append(
159
- f'<mark class="entity" style="background-color: {color};" '
160
- f'title="{escape_html(tooltip)}" '
161
- f'data-var="{escape_html(var_label)}">'
162
- f'{escape_html(span["span"])}'
163
- f'<span class="entity-label">{escape_html(var_label[:20])}</span>'
164
- f'</mark>'
165
  )
166
- last_end = span["end"]
167
 
168
- # Add remaining text
169
- if last_end < len(cr_text):
170
- html_parts.append(escape_html(cr_text[last_end:]))
171
 
172
- html = "".join(html_parts)
173
- return f"<div class='cr-text'>{html}</div>"
174
 
175
 
176
- def format_annotations_table(annotation: dict) -> str:
177
- """Format annotations as an HTML table with categories."""
178
  if not annotation:
179
  return "<p>No annotations</p>"
180
 
181
- # Group variables by category (simple heuristic based on name)
182
- categories = {
183
- "Patient Info": ["date_of_birth", "age_at_cancer_diagnosis", "biological_gender", "vital_status", "date_of_death"],
184
- "Diagnosis": ["date_of_cancer_diagnostic", "primary_tumor_localisation", "ctnm_stage", "stage_as_per_ehr", "histological_type", "epithelial_tumor_subtype"],
185
- "Tumor Characteristics": ["resectability_status", "two_largest_diameters", "metastasis_localisation", "number_of_metastatic_sites"],
186
- "Lab Results": ["crp_at_diagnosis", "albumin_at_diagnosis", "alanine_transaminase", "aspartate_aminotransferase", "conjugated_bilirubin", "ca19_9"],
187
- "Treatment": ["surgery", "loco_regional_radiotherapy", "immunotherapy", "targeted_therapy", "full_course_of_initial_treatment"],
188
- "Molecular": ["germline_mutation", "tumor_molecular_profiling"],
189
- "Progression": ["date_of_first_progression", "type_of_first_progression", "treatment_at_first_progression", "best_response", "reason_for_treatment_end"],
190
- }
191
-
192
- def get_category(var_name):
193
- for cat, keywords in categories.items():
194
- for kw in keywords:
195
- if kw in var_name.lower():
196
- return cat
197
- return "Other"
198
-
199
- # Group rows by category
200
- categorized = {}
201
  for var_name, var_data in annotation.items():
202
  if var_data and isinstance(var_data, dict):
203
  value = var_data.get("value")
204
- if value:
205
- cat = get_category(var_name)
206
- if cat not in categorized:
207
- categorized[cat] = []
208
- categorized[cat].append((var_name, var_data))
209
-
210
- if not categorized:
211
- return "<p class='no-data'>No extracted values</p>"
212
-
213
- html_parts = []
214
-
215
- for category in ["Patient Info", "Diagnosis", "Tumor Characteristics", "Lab Results", "Treatment", "Molecular", "Progression", "Other"]:
216
- if category not in categorized:
217
- continue
218
-
219
- html_parts.append(f"<div class='category'><h4>{category}</h4>")
220
- html_parts.append("<table class='annotations-table'>")
221
-
222
- for var_name, var_data in categorized[category]:
223
- value = var_data.get("value", "")
224
  span = var_data.get("span", "")
225
- var_label = var_name.replace("_", " ").title()
226
-
227
- span_preview = span[:80] + "..." if span and len(span) > 80 else span
228
-
229
- html_parts.append(f"""
230
- <tr>
231
- <td class='var-name'>{escape_html(var_label)}</td>
232
- <td class='var-value'>{escape_html(str(value))}</td>
233
- <td class='var-span'>{escape_html(span_preview) if span_preview else '-'}</td>
234
- </tr>
235
- """)
236
-
237
- html_parts.append("</table></div>")
238
-
239
- return "".join(html_parts)
240
-
241
-
242
- def get_stats(annotation: dict) -> str:
243
- """Get statistics about extracted values."""
244
- if not annotation:
245
- return "No data"
246
-
247
- total = len(annotation)
248
- extracted = sum(1 for v in annotation.values() if v and isinstance(v, dict) and v.get("value"))
249
 
250
- return f"📊 Extracted: {extracted}/{total} variables ({100*extracted//total}%)"
251
-
252
-
253
- def display_sample(sample_idx: int):
254
- """Display a sample from the dataset."""
255
- if sample_idx < 0 or sample_idx >= len(dataset):
256
- return "Invalid sample index", "<p>Invalid sample index</p>", "Invalid"
257
 
258
- sample = dataset[int(sample_idx)]
259
- cr_text = sample.get("CR", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
260
  annotation = sample.get("annotation", {})
261
 
262
- highlighted_html = highlight_spans_in_text(cr_text, annotation)
263
- annotations_html = format_annotations_table(annotation)
264
- stats = get_stats(annotation)
265
-
266
- return highlighted_html, annotations_html, stats
267
-
268
-
269
- def search_samples(query: str):
270
- """Search samples by text content."""
271
- if not query or len(query) < 3:
272
- # Return first 20 samples
273
- return [[i, dataset[i]["CR"][:80] + "..."] for i in range(min(20, len(dataset)))]
274
-
275
- results = []
276
- query_lower = query.lower()
277
- for i, sample in enumerate(dataset):
278
- cr = sample.get("CR", "")
279
- if query_lower in cr.lower():
280
- results.append([i, cr[:80] + "..."])
281
- if len(results) >= 50:
282
- break
283
-
284
- if not results:
285
- return [["No results", f"No samples found containing '{query}'"]]
286
-
287
- return results
288
-
289
-
290
- # Custom CSS for better styling
291
- custom_css = """
292
- .cr-text {
293
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
294
- font-size: 14px;
295
- line-height: 1.8;
296
- padding: 20px;
297
- background: #fafafa;
298
- border-radius: 8px;
299
- white-space: pre-wrap;
300
- max-height: 500px;
301
- overflow-y: auto;
302
- }
303
-
304
- .entity {
305
- padding: 2px 6px;
306
- border-radius: 4px;
307
- cursor: help;
308
- position: relative;
309
- display: inline;
310
- transition: all 0.2s;
311
- }
312
-
313
- .entity:hover {
314
- filter: brightness(0.9);
315
- box-shadow: 0 2px 8px rgba(0,0,0,0.15);
316
- }
317
-
318
- .entity-label {
319
- display: none;
320
- position: absolute;
321
- bottom: 100%;
322
- left: 0;
323
- background: #333;
324
- color: white;
325
- padding: 4px 8px;
326
- border-radius: 4px;
327
- font-size: 11px;
328
- white-space: nowrap;
329
- z-index: 100;
330
- }
331
-
332
- .entity:hover .entity-label {
333
- display: block;
334
- }
335
-
336
- .category {
337
- margin-bottom: 20px;
338
- }
339
-
340
- .category h4 {
341
- color: #1976d2;
342
- border-bottom: 2px solid #1976d2;
343
- padding-bottom: 8px;
344
- margin-bottom: 12px;
345
- }
346
-
347
- .annotations-table {
348
- width: 100%;
349
- border-collapse: collapse;
350
- font-size: 13px;
351
- }
352
-
353
- .annotations-table tr:nth-child(even) {
354
- background: #f5f5f5;
355
- }
356
-
357
- .annotations-table td {
358
- padding: 10px 12px;
359
- border-bottom: 1px solid #e0e0e0;
360
- vertical-align: top;
361
- }
362
-
363
- .var-name {
364
- font-weight: 600;
365
- color: #333;
366
- width: 30%;
367
- }
368
-
369
- .var-value {
370
- color: #1976d2;
371
- font-weight: 500;
372
- width: 25%;
373
- }
374
-
375
- .var-span {
376
- color: #666;
377
- font-style: italic;
378
- font-size: 12px;
379
- width: 45%;
380
- }
381
-
382
- .no-data {
383
- color: #999;
384
- font-style: italic;
385
- padding: 20px;
386
- text-align: center;
387
- }
388
-
389
- .stats-badge {
390
- background: #e3f2fd;
391
- color: #1976d2;
392
- padding: 8px 16px;
393
- border-radius: 20px;
394
- font-weight: 500;
395
- display: inline-block;
396
- }
397
- """
398
 
399
 
400
- # Build the Gradio interface
401
- with gr.Blocks(
402
- title="Pancreas Cancer Annotations Explorer",
403
- theme=gr.themes.Soft(primary_hue="blue"),
404
- css=custom_css
405
- ) as demo:
406
-
407
- gr.Markdown("""
408
- # 🔬 Pancreas Cancer Clinical Report Annotations Explorer
409
-
410
- Explore structured annotations extracted from synthetic French clinical reports about pancreas cancer.
411
-
412
- **How to use:**
413
- - Use the slider or search to navigate samples
414
- - Hover over highlighted text to see extracted variables
415
- - View the complete annotation table below
416
- """)
417
 
418
  with gr.Row():
419
- with gr.Column(scale=2):
420
- sample_slider = gr.Slider(
421
- minimum=0,
422
- maximum=len(dataset) - 1,
423
- step=1,
424
- value=0,
425
- label=f"📌 Sample Index (0 - {len(dataset) - 1})",
426
- info="Drag to browse samples"
427
- )
428
- with gr.Column(scale=1):
429
- stats_display = gr.Markdown("", elem_classes=["stats-badge"])
430
 
431
  with gr.Row():
432
- with gr.Column(scale=1):
433
- search_box = gr.Textbox(
434
- label="🔍 Search",
435
- placeholder="Type to search in clinical reports...",
436
- info="Min 3 characters"
437
- )
438
- search_results = gr.Dataframe(
439
- headers=["#", "Preview"],
440
- label="Results",
441
- interactive=False,
442
- height=200
443
- )
444
-
445
- gr.Markdown("---")
446
- gr.Markdown("### 📄 Clinical Report with Entity Highlighting")
447
- gr.Markdown("*Hover over colored text to see the extracted variable and value*")
448
-
449
- cr_display = gr.HTML()
450
-
451
- gr.Markdown("---")
452
- gr.Markdown("### 📊 Extracted Annotations")
453
-
454
- annotations_display = gr.HTML()
455
-
456
- # Event handlers
457
- sample_slider.change(
458
- fn=display_sample,
459
- inputs=[sample_slider],
460
- outputs=[cr_display, annotations_display, stats_display]
461
- )
462
-
463
- search_box.change(
464
- fn=search_samples,
465
- inputs=[search_box],
466
- outputs=[search_results]
467
- )
468
-
469
- def on_select(evt: gr.SelectData, data):
470
- if data is not None and len(data) > 0:
471
- try:
472
- selected_idx = int(data[evt.index[0]][0])
473
- return selected_idx
474
- except (ValueError, IndexError, TypeError):
475
- pass
476
- return 0
477
-
478
- search_results.select(
479
- fn=on_select,
480
- inputs=[search_results],
481
- outputs=[sample_slider]
482
- )
483
-
484
- # Load first sample on start
485
- demo.load(
486
- fn=display_sample,
487
- inputs=[sample_slider],
488
- outputs=[cr_display, annotations_display, stats_display]
489
- )
490
-
491
 
492
  if __name__ == "__main__":
493
  demo.launch()
 
1
  """
2
  Gradio app to explore pancreas cancer clinical report annotations.
 
3
  """
4
 
5
  import gradio as gr
6
  from datasets import load_dataset
 
7
 
8
  # Load the dataset
9
+ print("Loading dataset...")
10
  dataset = load_dataset("rntc/biomed-fr-pancreas-annotations", split="train")
11
  print(f"Loaded {len(dataset)} samples")
12
 
13
+ # Colors for highlighting
14
+ COLORS = [
15
+ "#FFEB3B", "#4CAF50", "#2196F3", "#FF9800", "#E91E63",
16
+ "#9C27B0", "#00BCD4", "#8BC34A", "#FF5722", "#607D8B",
17
+ ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
 
20
+ def escape_html(text):
 
21
  if not text:
22
  return ""
23
+ return str(text).replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
 
26
+ def highlight_text(cr_text, annotation):
27
+ """Highlight spans in CR text."""
 
 
 
28
  if not cr_text or not annotation:
29
+ return f"<pre style='white-space:pre-wrap;'>{escape_html(cr_text)}</pre>"
30
 
31
+ # Collect valid spans (that exist in text)
32
+ spans = []
33
  for var_name, var_data in annotation.items():
34
  if var_data and isinstance(var_data, dict):
35
  span = var_data.get("span")
36
  value = var_data.get("value")
37
+ if span and value and span in cr_text:
38
+ spans.append({
39
+ "text": span,
40
+ "start": cr_text.find(span),
41
+ "var": var_name.replace("_", " ").title(),
42
  "value": str(value)
43
  })
44
 
45
+ if not spans:
46
+ return f"<pre style='white-space:pre-wrap;'>{escape_html(cr_text)}</pre>"
47
+
48
+ # Sort by position and remove overlaps
49
+ spans.sort(key=lambda x: x["start"])
50
+ filtered = []
51
+ for s in spans:
52
+ s["end"] = s["start"] + len(s["text"])
53
+ if not filtered or s["start"] >= filtered[-1]["end"]:
54
+ filtered.append(s)
55
+
56
+ # Build HTML
57
+ html = []
58
+ pos = 0
59
+ color_map = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  color_idx = 0
61
+
62
+ for s in filtered:
63
+ # Text before span
64
+ if s["start"] > pos:
65
+ html.append(escape_html(cr_text[pos:s["start"]]))
66
+
67
+ # Assign color
68
+ if s["var"] not in color_map:
69
+ color_map[s["var"]] = COLORS[color_idx % len(COLORS)]
70
  color_idx += 1
71
 
72
+ # Highlighted span
73
+ color = color_map[s["var"]]
74
+ html.append(
75
+ f'<mark style="background:{color};padding:1px 3px;border-radius:3px;" '
76
+ f'title="{escape_html(s["var"])}: {escape_html(s["value"])}">'
77
+ f'{escape_html(s["text"])}</mark>'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  )
79
+ pos = s["end"]
80
 
81
+ # Remaining text
82
+ if pos < len(cr_text):
83
+ html.append(escape_html(cr_text[pos:]))
84
 
85
+ return f"<pre style='white-space:pre-wrap;line-height:1.6;'>{' '.join(html)}</pre>"
 
86
 
87
 
88
+ def format_table(annotation):
89
+ """Format annotations as HTML table."""
90
  if not annotation:
91
  return "<p>No annotations</p>"
92
 
93
+ rows = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  for var_name, var_data in annotation.items():
95
  if var_data and isinstance(var_data, dict):
96
  value = var_data.get("value")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  span = var_data.get("span", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ var_label = var_name.replace("_", " ").title()
 
 
 
 
 
 
100
 
101
+ if value:
102
+ # Check if span is a "not found" explanation
103
+ if span and ("pas de mention" in span.lower() or "not performed" in str(value).lower()):
104
+ display_value = "/"
105
+ display_span = ""
106
+ else:
107
+ display_value = str(value)
108
+ display_span = span[:60] + "..." if span and len(span) > 60 else (span or "")
109
+ else:
110
+ display_value = "/"
111
+ display_span = ""
112
+
113
+ rows.append(f"""<tr>
114
+ <td style="padding:6px 10px;border-bottom:1px solid #ddd;font-weight:500;">{escape_html(var_label)}</td>
115
+ <td style="padding:6px 10px;border-bottom:1px solid #ddd;color:#1565C0;">{escape_html(display_value)}</td>
116
+ <td style="padding:6px 10px;border-bottom:1px solid #ddd;color:#666;font-size:12px;font-style:italic;">{escape_html(display_span)}</td>
117
+ </tr>""")
118
+
119
+ return f"""<table style="width:100%;border-collapse:collapse;font-size:13px;">
120
+ <thead><tr style="background:#f5f5f5;">
121
+ <th style="padding:8px 10px;text-align:left;border-bottom:2px solid #ddd;">Variable</th>
122
+ <th style="padding:8px 10px;text-align:left;border-bottom:2px solid #ddd;">Value</th>
123
+ <th style="padding:8px 10px;text-align:left;border-bottom:2px solid #ddd;">Source</th>
124
+ </tr></thead>
125
+ <tbody>{"".join(rows)}</tbody>
126
+ </table>"""
127
+
128
+
129
+ def display_sample(idx):
130
+ """Display a sample."""
131
+ idx = int(idx)
132
+ if idx < 0 or idx >= len(dataset):
133
+ return "Invalid index", "Invalid index"
134
+
135
+ sample = dataset[idx]
136
+ cr = sample.get("CR", "")
137
  annotation = sample.get("annotation", {})
138
 
139
+ return highlight_text(cr, annotation), format_table(annotation)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
 
141
 
142
+ # Build UI
143
+ with gr.Blocks(title="Pancreas Annotations", theme=gr.themes.Base()) as demo:
144
+ gr.Markdown("# 🔬 Pancreas Cancer Annotations Explorer")
145
+ gr.Markdown("Hover over highlighted text to see extracted values. `/` means not found.")
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  with gr.Row():
148
+ slider = gr.Slider(0, len(dataset) - 1, value=0, step=1, label="Sample")
 
 
 
 
 
 
 
 
 
 
149
 
150
  with gr.Row():
151
+ with gr.Column():
152
+ gr.Markdown("### Clinical Report")
153
+ cr_html = gr.HTML()
154
+ with gr.Column():
155
+ gr.Markdown("### Extracted Variables")
156
+ table_html = gr.HTML()
157
+
158
+ slider.change(display_sample, inputs=[slider], outputs=[cr_html, table_html])
159
+ demo.load(display_sample, inputs=[slider], outputs=[cr_html, table_html])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
  if __name__ == "__main__":
162
  demo.launch()