Skip to content

Commit f96bef1

Browse files
authored
Fix report formatting (#531)
* fix layout * fix min-width of plotly plots * merge main and add axes * fix gap in selections * fix plot width in collaps. sections * plot slices w/ one pint
1 parent f64f0e0 commit f96bef1

File tree

2 files changed

+99
-46
lines changed

2 files changed

+99
-46
lines changed

cyclops/report/templates/cyclops_generic_template.jinja

Lines changed: 99 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@
137137
<div>
138138
<span style="font-size: 18px; font-weight:bold;">
139139
<div class="tooltip">
140-
{{ card.name }}
140+
{{ card.name|regex_replace('(?<!^)(?=[A-Z][a-z])', ' ') }}
141141
<div class="tooltiptext">
142142
{{ card.tooltip }}
143143
<div class="arrow-up"></div>
@@ -161,8 +161,8 @@
161161
<span style="font-size: 40px; color: gray;">&#9472;</span>
162162
{% endif %}
163163
</span>
164-
<span style="display: inline-block; line-height:1;">
165-
<span style="font-size: 14px">{{card.threshold}}<br>minimum<br>threshold</span>
164+
<span style="display: inline-block; line-height:1; margin-bottom: 10px;">
165+
<span style="font-size: 14px;">{{card.threshold}}<br>minimum<br>threshold</span>
166166
</span>
167167
<div id="model-card-plot-{{idx}}">
168168
</div>
@@ -184,7 +184,7 @@
184184
{% endmacro %}
185185

186186
{% macro render_perf_over_time(name, comp)%}
187-
<div class="card" id={{name}} style="display: block; flex-basis: 100%">
187+
<div class="card" id={{name}} style="display: block;">
188188
<h3 style="color: black; font-weight:normal;">How is your model doing over time?</h3><br>
189189
<h3 style="color: gray; font-weight:normal;">See how your model is performing over several metrics and subgroups over time.</h3>
190190
<div style="display: flex; align-items: center; justify-content: center; padding: 10px; margin-bottom: 20px;">
@@ -197,7 +197,7 @@
197197
</div>
198198
</div>
199199
<div class="column" style="float: left;">
200-
<div class="subcard_overview" style="padding:10px; margin-bottom:20px; max-width:500px;">
200+
<div class="subcard_overview" style="padding:10px; margin-bottom:20px; width:15vw;">
201201
<h4>Metrics</h4>
202202
<div class="radio-buttons" id="slice-selection">
203203
<input type="radio" id="{{comp.metric_cards.metrics[0]}}" name="metric" value="{{comp.metric_cards.metrics[0]}}" checked>
@@ -212,7 +212,7 @@
212212
</div>
213213
{% else %}
214214
<div class="tooltip">
215-
<label for="{{comp.metric_cards.metrics[0]}}">{{comp.metric_cards.metrics[0]}}</label>
215+
<label for="{{comp.metric_cards.metrics[0]}}">{{comp.metric_cards.metrics[0]|regex_replace('(?<!^)(?=[A-Z][a-z])', ' ')}}</label>
216216
<div class="tooltiptext">
217217
{{ comp.metric_cards.tooltips[0] }}
218218
<div class="arrow-up"></div>
@@ -233,7 +233,7 @@
233233
</div>
234234
{% else %}
235235
<div class="tooltip">
236-
<label for="{{metric}}">{{metric}}</label>
236+
<label for="{{metric}}">{{metric|regex_replace('(?<!^)(?=[A-Z][a-z])', ' ')}}</label>
237237
<div class="tooltiptext">
238238
{{ tooltip }}
239239
<div class="arrow-up"></div>
@@ -244,36 +244,41 @@
244244
</div>
245245
</div>
246246
{% for slice, values in comp.metric_cards.slices|zip(comp.metric_cards.values) %}
247-
<div class="subcard_overview" style="padding:10px; margin-bottom:10px; max-width:500px;">
248-
<h4>{{slice|regex_replace('(?<!^)(?=[A-Z])', ' ')|title}}</h4>
247+
<div class="subcard_overview" style="padding:10px; margin-bottom:10px; width:15vw;">
248+
<h4>{{slice|regex_replace('(?<!^)(?=[A-Z][a-z])', ' ')}}</h4>
249249
<div class="radio-buttons" id="slice-selection">
250250
<input type="radio" id="overall_{{slice}}" name="{{slice}}" value="overall_{{slice}}" checked>
251251
<label for="overall_{{slice}}">All</label>
252252
{% for value in values %}
253253
<input type="radio" id="{{value}}" name="{{slice}}" value="{{value}}">
254-
<label for="{{value}}">{{value|regex_replace('_+', ' ')|title}}</label>
254+
{% if value|regex_search("\((.*?)\)")|length != 0 %}
255+
{% set acronym = value|regex_search("\((.*?)\)") %}
256+
<label for="{{value}}">{{acronym[0]}}</label>
257+
{% else %}
258+
<label for="{{value}}">{{value|regex_replace('(?<!^)(?=[A-Z][a-z])', ' ')}}</label>
259+
{% endif %}
255260
{% endfor %}
256261
</div>
257262
</div>
258263
{% endfor %}
259264
</div>
260-
<div class="subcard_overview" style="margin:auto; float:right;">
265+
<div class="subcard_overview" style="margin: 0px; margin-left:25px; float:left; min-width:500px; max-width:100%;">
261266
<div id="plot"></div>
262267
</div>
263268
</div>
264269
{% endmacro %}
265270

266271
{% macro render_overview(name, comp) %}
267272
{{ render_perf(name, comp) }}
268-
{% set val = {'history': false} %}
273+
{# {% set val = {'history': false} %}
269274
{% for metric_card in comp.metric_cards.collection%}
270275
{% if metric_card.history|length > 1 %}
271276
{% set _ = val.update({'history':true}) %}
272277
{% endif %}
273278
{% endfor %}
274-
{% if val['history'] %}
275-
{{ render_perf_over_time(name, comp) }}
276-
{% endif %}
279+
{% if val['history'] %} #}
280+
{{ render_perf_over_time(name, comp) }}
281+
{# {% endif %} #}
277282
{% endmacro %}
278283

279284
{% macro render_objects(section)%}
@@ -326,7 +331,7 @@
326331
{% endfor %}
327332
{# Display global plots from GraphicsCollection in Datasets, temporary fix #}
328333
{% if section is hasattr "graphics" %}
329-
<div class="subcard" style="min-width:1400px;">
334+
<div class="subcard" style="flex-basis:100%;">
330335
<h3>{{ "Graphics" }}</h3>
331336
{{ render_if_exist_list(section.graphics) }}
332337
</div>
@@ -339,7 +344,7 @@
339344
{% endfor %}
340345
{% if section is hasattr "graphics"%}
341346
{% if section.graphics is not none%}
342-
<div class="subcard" style="min-width:1400px;">
347+
<div class="subcard" style="flex-basis:100%;">
343348
<h3>{{ "Graphics" }}</h3>
344349
{{ render_if_exist_list(section.graphics) }}
345350
</div>
@@ -401,6 +406,7 @@
401406
402407
body {
403408
font-family: Arial, Helvetica, sans-serif;
409+
min-width: 1150px;
404410
}
405411
406412
h2 {
@@ -412,7 +418,6 @@
412418
color: #0073e4;
413419
flex-basis: 100%;
414420
}
415-
416421
.card {
417422
display: flex;
418423
flex-wrap: wrap;
@@ -432,7 +437,6 @@
432437
background-color: #f3f4f6;
433438
border-radius: 5px;
434439
box-shadow: 4px 4px 4px 0px rgba(0, 0, 0, 0.1);
435-
{# display: none; #}
436440
}
437441
438442
.subcard_overview {
@@ -450,9 +454,6 @@
450454
}
451455
452456
.img-item {
453-
min-width: 1024px;
454-
width: 100%;
455-
height: auto;
456457
flex-basis: 100%;
457458
margin-top: 10px;
458459
margin-bottom: 10px;
@@ -725,6 +726,7 @@
725726
726727
.radio-buttons {
727728
display: flex;
729+
row-gap: 15px;
728730
flex-direction: row;
729731
flex-wrap: wrap;
730732
justify-content: left;
@@ -747,6 +749,7 @@
747749
transition: background-color 0.3s ease;
748750
background-color: #ffffff;
749751
border: 2px solid #DADCE0;
752+
white-space: nowrap;
750753
}
751754
752755
.radio-buttons label:not(:first-child):not(:last-child) {
@@ -848,6 +851,15 @@
848851
</body>
849852
</html>
850853
<script>
854+
function multipleStringLines(title) {
855+
if (title.length > 50) { // check if greater than threshold!
856+
let y_axis = title.split(' '); //break into words
857+
let interval = title.split(' ').length / 2; //2-lines
858+
return y_axis.slice(0, interval).join(' ') + '<br>' + y_axis.slice(interval, y_axis.length).join(' ');
859+
}
860+
return title;
861+
}
862+
851863
function isInView(element) {
852864
// return true if element is in view
853865
var rect = element.getBoundingClientRect(),
@@ -934,15 +946,21 @@
934946
plot_bgcolor: "rgba(0,0,0,0)",
935947
xaxis: {
936948
zeroline: false,
937-
showticklabels: false,
938-
showgrid: false
949+
showticklabels: true,
950+
showgrid: false,
951+
tickformat: '%b\n %Y'
939952
},
940953
yaxis: {
941-
gridcolor: "#ffffff"
954+
gridcolor: "#ffffff",
955+
zeroline: false,
956+
showticklabels: true,
957+
showgrid: true,
958+
range: [-0.10, 1.10],
942959
},
943-
margin: { l: 0, r: 0, t: 0, b: 0 },
944-
height: 125,
945-
width: 250
960+
margin: { l: 30, r: 0, t: 0, b: 30 },
961+
padding: { l: 0, r: 0, t: 0, b: 0 },
962+
height: 150,
963+
width: 300
946964
}
947965
};
948966
if (history.length > 0) {
@@ -1261,7 +1279,6 @@
12611279
12621280
// create title for plot: Current {metric name} is trending {trend_keyword} and is {passed_keyword} the threshold.
12631281
// get number of nulls in selections, if 9 then plot title, else don't plot title
1264-
console.log(selections)
12651282
var nulls = 0;
12661283
for (let i = 0; i < selections.length; i++) {
12671284
if (selections[i] === null) {
@@ -1270,6 +1287,7 @@
12701287
}
12711288
if (nulls === 10) {
12721289
var plot_title = "Current " + name + " is trending " + trend_keyword + " and is " + passed_keyword + " the threshold.";
1290+
var plot_title = multipleStringLines(plot_title);
12731291
var showlegend = false;
12741292
}
12751293
else {
@@ -1324,6 +1342,7 @@
13241342
};
13251343
traces.push(threshold_trace);
13261344
}
1345+
var width = Math.max(parent.innerWidth - 900, 500);
13271346
var layout = {
13281347
title: {
13291348
text: plot_title,
@@ -1336,13 +1355,26 @@
13361355
plot_bgcolor: 'rgba(0,0,0,0)',
13371356
xaxis: {
13381357
zeroline: false,
1339-
showticklabels: false,
1358+
showticklabels: true,
13401359
showgrid: false,
1360+
tickformat: '%b\n %Y'
13411361
},
13421362
yaxis: {
13431363
gridcolor: '#ffffff',
1364+
zeroline: false,
1365+
showticklabels: true,
1366+
showgrid: true,
1367+
range: [-0.10, 1.10],
13441368
},
13451369
showlegend: showlegend,
1370+
// show legend at top
1371+
legend: {
1372+
orientation: "h",
1373+
yanchor: "top",
1374+
y: 1.1,
1375+
xanchor: "left",
1376+
x: 0.1
1377+
},
13461378
margin: {
13471379
l: 50,
13481380
r: 50,
@@ -1352,7 +1384,8 @@
13521384
},
13531385
// set height and width of plot to extra-wide to fit the plot
13541386
height: 500,
1355-
width: 900,
1387+
// get size of window and set width of plot to size of window
1388+
width: width,
13561389
}
13571390
Plotly.newPlot(plot, traces, layout, {displayModeBar: false});
13581391
}
@@ -1565,6 +1598,7 @@
15651598
}
15661599
if (nulls === 10) {
15671600
var plot_title = "Current " + name + " is trending " + trend_keyword + " and is " + passed_keyword + " the threshold.";
1601+
var plot_title = multipleStringLines(plot_title);
15681602
var showlegend = false;
15691603
}
15701604
else {
@@ -1619,6 +1653,7 @@
16191653
};
16201654
traces.push(threshold_trace);
16211655
}
1656+
var width = Math.max(parent.innerWidth - 900, 500);
16221657
var layout = {
16231658
title: {
16241659
text: plot_title,
@@ -1631,30 +1666,45 @@
16311666
plot_bgcolor: 'rgba(0,0,0,0)',
16321667
xaxis: {
16331668
zeroline: false,
1634-
showticklabels: false,
1669+
showticklabels: true,
16351670
showgrid: false,
1671+
tickformat: '%b\n %Y'
16361672
},
16371673
yaxis: {
16381674
gridcolor: '#ffffff',
1675+
zeroline: false,
1676+
showticklabels: true,
1677+
showgrid: true,
1678+
range: [-0.10, 1.10],
16391679
},
16401680
showlegend: showlegend,
1681+
// show legend at top
1682+
legend: {
1683+
orientation: "h",
1684+
yanchor: "top",
1685+
y: 1.1,
1686+
xanchor: "left",
1687+
x: 0.1
1688+
},
16411689
margin: {
16421690
l: 50,
16431691
r: 50,
16441692
b: 50,
16451693
t: 50,
16461694
pad: 4
16471695
},
1648-
// set height and width of plot to extra-wide to fit the plot
1696+
// set height and width of plot to width of card minus 500px
16491697
height: 500,
1650-
width: 900,
1698+
width: width,
16511699
}
16521700
Plotly.newPlot(plot, traces, layout, {displayModeBar: false});
16531701
}
16541702
// Add event listeners to radio buttons
16551703
for (let input of inputs_all) {
16561704
input.addEventListener('change', updatePlot);
16571705
}
1706+
// Add event listener to update plot when window is resized
1707+
window.addEventListener('resize', updatePlot);
16581708
for (let selection of plot_selection) {
16591709
selection.addEventListener('change', updatePlotSelection);
16601710
}
@@ -1673,7 +1723,6 @@
16731723
for (let i = 0; i < collapsible.length; i++) {
16741724
collapsible[i].addEventListener("click", function() {
16751725
let arrow = collapsible[i];
1676-
16771726
if (arrow.classList.contains('down-arrow')) {
16781727
arrow.classList.add('right-arrow');
16791728
arrow.classList.remove('down-arrow');
@@ -1691,9 +1740,24 @@
16911740
16921741
let collapsible_bar = card.getElementsByClassName("collapsible-bar")[0];
16931742
collapsible_bar.style.display = collapsible_bar.style.display === 'block' ? 'none' : 'block';
1743+
refreshPlotlyPlots();
16941744
});
16951745
}
16961746
}
16971747
document.addEventListener('DOMContentLoaded', setCollapseButton);
16981748
1749+
// function to refresh plotly plots
1750+
function refreshPlotlyPlots() {
1751+
const img_items = document.getElementsByClassName("img-item");
1752+
for (let i = 0; i < img_items.length; i++) {
1753+
if (img_items[i].getElementsByTagName("div").length > 0) {
1754+
id = img_items[i].getElementsByClassName("plotly-graph-div")[0].id;
1755+
var gd = document.getElementById(id);
1756+
data = gd.data;
1757+
layout = gd.layout;
1758+
Plotly.update(id, data, layout);
1759+
}
1760+
}
1761+
}
1762+
16991763
</script>

docs/source/tutorials/kaggle/heart_failure_prediction.ipynb

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,17 +1126,6 @@
11261126
"fairness_plot.show()"
11271127
]
11281128
},
1129-
{
1130-
"cell_type": "markdown",
1131-
"metadata": {},
1132-
"source": [
1133-
"### Performance over time\n",
1134-
"\n",
1135-
"We can monitor the change of performance metrics over time by leveraging historical reports. This involves using the `get_metrics_trends` function to gather prior metrics and merge them with recent results. We can specify which metrics and slices we wish to observe. Once the data is collected, we generate the plot using the `metrics_trends` method from the plotter, which can then be integrated into the report.\n",
1136-
"\n",
1137-
"Please note, for the purpose of this tutorial, we will create three dummy reports to demonstrate the process of plotting these metric trends."
1138-
]
1139-
},
11401129
{
11411130
"cell_type": "markdown",
11421131
"metadata": {},

0 commit comments

Comments
 (0)