Skip to content

Commit 71cc969

Browse files
authored
Merge pull request #1165 from Giskard-AI/feature/gsk-1236-add-spinner-to-text-explanation-while-its-computing
[GSK-1236] Add spinner to explanations while its computing
2 parents 7c50c97 + 03afd26 commit 71cc969

3 files changed

Lines changed: 127 additions & 162 deletions

File tree

frontend/src/views/main/project/PredictionExplanations.vue

Lines changed: 42 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,14 @@
11
<template>
22
<div class="main">
3-
<OverlayLoader :show="loading" solid absolute no-fade/>
4-
<v-container class="text-center">
5-
<v-row
6-
v-if="
7-
Object.keys(fullExplanations).length !== 0 &&
8-
fullExplanations.constructor === Object
9-
"
10-
>
3+
<LoadingFullscreen v-show="loading" name="explanation" class="pb-6" />
4+
<v-container class="text-center" v-show="!loading">
5+
<v-row v-if="Object.keys(fullExplanations).length !== 0 &&
6+
fullExplanations.constructor === Object
7+
">
118
<v-col>
12-
<v-chart
13-
v-if="predictionTask === ModelType.REGRESSION"
14-
class="chart"
15-
:option="chartOptionsRegression"
16-
autoresize
17-
/>
18-
<v-chart
19-
v-if="predictionTask === ModelType.CLASSIFICATION && classificationLabels.length === 2"
20-
class="chart"
21-
:option="chartOptionsBinaryClassification"
22-
autoresize
23-
/>
24-
<v-chart
25-
v-if="predictionTask === ModelType.CLASSIFICATION && classificationLabels.length > 2"
26-
class="chart"
27-
:option="chartOptionsMultiClassification"
28-
autoresize
29-
/>
9+
<v-chart v-if="predictionTask === ModelType.REGRESSION" class="chart" :option="chartOptionsRegression" autoresize />
10+
<v-chart v-if="predictionTask === ModelType.CLASSIFICATION && classificationLabels.length === 2" class="chart" :option="chartOptionsBinaryClassification" autoresize />
11+
<v-chart v-if="predictionTask === ModelType.CLASSIFICATION && classificationLabels.length > 2" class="chart" :option="chartOptionsMultiClassification" autoresize />
3012
</v-col>
3113
</v-row>
3214
<p v-if="errorMsg" class="error--text">
@@ -37,34 +19,34 @@
3719
</template>
3820

3921
<script lang="ts">
40-
import {Component, Prop, Vue, Watch} from "vue-property-decorator";
41-
import OverlayLoader from "@/components/OverlayLoader.vue";
42-
import {api} from "@/api";
22+
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
23+
import LoadingFullscreen from "@/components/LoadingFullscreen.vue";
24+
import { api } from "@/api";
4325
import ECharts from "vue-echarts";
44-
import {use} from "echarts/core";
45-
import {BarChart} from "echarts/charts";
46-
import {CanvasRenderer} from "echarts/renderers";
47-
import {GridComponent} from "echarts/components";
26+
import { use } from "echarts/core";
27+
import { BarChart } from "echarts/charts";
28+
import { CanvasRenderer } from "echarts/renderers";
29+
import { GridComponent } from "echarts/components";
4830
import "echarts/lib/component/legend";
49-
import {ModelType} from "@/generated-sources";
31+
import { ModelType } from "@/generated-sources";
5032
import _ from "lodash";
51-
import {CanceledError} from "axios";
33+
import { CanceledError } from "axios";
5234
5335
use([CanvasRenderer, BarChart, GridComponent]);
5436
Vue.component("v-chart", ECharts);
5537
5638
@Component({
57-
components: {OverlayLoader},
39+
components: { LoadingFullscreen },
5840
})
5941
export default class PredictionExplanations extends Vue {
60-
@Prop({required: true}) modelId!: string;
61-
@Prop({required: true}) datasetId!: string;
62-
@Prop({required: true}) predictionTask!: string;
42+
@Prop({ required: true }) modelId!: string;
43+
@Prop({ required: true }) datasetId!: string;
44+
@Prop({ required: true }) predictionTask!: string;
6345
@Prop() targetFeature!: string;
6446
@Prop() classificationLabels!: string[];
65-
@Prop({default: {}}) inputData!: object;
47+
@Prop({ default: {} }) inputData!: object;
6648
@Prop() modelFeatures!: string[];
67-
@Prop({default: 250}) debounceTime!: number;
49+
@Prop({ default: 250 }) debounceTime!: number;
6850
6951
7052
loading: boolean = false;
@@ -77,7 +59,7 @@ export default class PredictionExplanations extends Vue {
7759
await this.getExplanation()
7860
}
7961
80-
@Watch("inputData", {deep: true})
62+
@Watch("inputData", { deep: true })
8163
private async onInputDataChange() {
8264
await this.debouncedGetExplanation();
8365
}
@@ -96,10 +78,10 @@ export default class PredictionExplanations extends Vue {
9678
this.loading = true;
9779
this.errorMsg = "";
9880
const explainResponse = await api.explain(
99-
this.modelId,
100-
this.datasetId,
101-
_.pick(this.inputData, this.modelFeatures),
102-
this.controller
81+
this.modelId,
82+
this.datasetId,
83+
_.pick(this.inputData, this.modelFeatures),
84+
this.controller
10385
)
10486
this.fullExplanations = explainResponse.explanations;
10587
this.loading = false;
@@ -138,9 +120,9 @@ export default class PredictionExplanations extends Vue {
138120
show: true,
139121
position: "right",
140122
formatter: (params) =>
141-
params.value > 0.02
142-
? params.value.toFixed(2).toLocaleString()
143-
: "",
123+
params.value > 0.02
124+
? params.value.toFixed(2).toLocaleString()
125+
: "",
144126
},
145127
labelLayout: {
146128
hideOverlap: true,
@@ -166,30 +148,30 @@ export default class PredictionExplanations extends Vue {
166148
167149
get chartOptionsBinaryClassification() {
168150
const lastExplanations =
169-
this.fullExplanations[Object.keys(this.fullExplanations)[Object.keys(this.fullExplanations).length - 1]];
151+
this.fullExplanations[Object.keys(this.fullExplanations)[Object.keys(this.fullExplanations).length - 1]];
170152
return this.createSimpleExplanationChart(lastExplanations);
171153
}
172154
173155
get chartOptionsMultiClassification() {
174156
const explanationSumByFeature: { [name: string]: number; } = _.reduce(
175-
_.values(this.fullExplanations),
176-
(acc, labelExplanations) => {
177-
_.forOwn(labelExplanations, (featureName, explainValue) => {
178-
acc[explainValue] = (acc[explainValue] || 0) + featureName;
179-
});
180-
return acc;
181-
}, {});
157+
_.values(this.fullExplanations),
158+
(acc, labelExplanations) => {
159+
_.forOwn(labelExplanations, (featureName, explainValue) => {
160+
acc[explainValue] = (acc[explainValue] || 0) + featureName;
161+
});
162+
return acc;
163+
}, {});
182164
183165
// Array of features sorted by sum of SHAP explanations
184166
// Bonus: sort by feature name to guarantee same order if the explanation sum is the same
185167
const sortedTopFeatures: Array<string> = Object.entries(
186-
explanationSumByFeature
168+
explanationSumByFeature
187169
).sort((a, b) => a[1] - b[1] || b[0].localeCompare(a[0])
188170
).map(el => el[0])
189171
let chartSeries: object[] = [];
190172
191173
for (const [className, explanation] of Object.entries(
192-
this.fullExplanations
174+
this.fullExplanations
193175
)) {
194176
// Guarantee that the explanation object follows the same feature order
195177
let explanationSortedByFeature: object = {}
@@ -209,7 +191,7 @@ export default class PredictionExplanations extends Vue {
209191
fontSize: "10",
210192
},
211193
formatter: (params) =>
212-
params.value > 0.02 ? params.value.toFixed(2).toLocaleString() : "",
194+
params.value > 0.02 ? params.value.toFixed(2).toLocaleString() : "",
213195
},
214196
labelLayout: {
215197
hideOverlap: true,

0 commit comments

Comments
 (0)