You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

272 lines
11 KiB

1 year ago
<template>
<w-dialog ref="dialogRef" :title="detailRef?.modelName + '(' + detailRef?.modelId + ')'" :can-maximize="false" :maximized="true">
<div class="px-2">
<q-tabs v-model="selectedTabRef" inline-label align="left" :breakpoint="0" no-caps @update:model-value="tabChanged">
<q-tab name="discrimination" icon="bi-people" :label="$t('io.sc.engine.mv.result.grid.entity.discrimination')" />
<q-tab name="stability" icon="bi-diagram-3" :label="$t('io.sc.engine.mv.result.grid.entity.stability')" />
<q-tab name="scaleValidate" icon="bi-diagram-3" :label="$t('io.sc.engine.mv.result.grid.entity.scaleValidate')" />
</q-tabs>
<q-tab-panels v-model="selectedTabRef" animated swipeable keep-alive>
<q-tab-panel name="discrimination" class="px-0">
<div style="height: 50px"></div>
<div class="row">
<div class="col-4">
<div id="rocEchart" style="width: 100%; height: 300px"></div>
<div class="row justify-center">
<q-btn color="primary" no-caps flat :label="$t('io.sc.engine.mv.result.curve.viewData', { type: 'ROC' })" @click="showData('ROC')" />
</div>
</div>
<div class="col-4">
<div id="capEchart" style="width: 100%; height: 300px"></div>
<div class="row justify-center">
<q-btn color="primary" no-caps flat :label="$t('io.sc.engine.mv.result.curve.viewData', { type: 'CAP' })" @click="showData('CAP')" />
</div>
</div>
<div class="col-4">
<div id="ksEchart" style="width: 100%; height: 300px"></div>
<div class="row justify-center">
<q-btn color="primary" no-caps flat :label="$t('io.sc.engine.mv.result.curve.viewData', { type: 'KS' })" @click="showData('KS')" />
</div>
</div>
</div>
<h3>{{ $t('io.sc.engine.mv.result.curve.references') }}:</h3>
<table width="100%" style="border-collapse: collapse">
<tr>
<th class="referenceTh">{{ $t('io.sc.engine.mv.performance') }}</th>
<th class="referenceTh">ROC (AUC = {{ detailRef.auc }})</th>
<th class="referenceTh">CAP (AR = {{ detailRef.ar }})</th>
<th class="referenceTh">KS (KS = {{ detailRef.ks }})</th>
</tr>
<tr v-for="(level, index) in referenceLevels" :key="index">
<td class="referenceTd">{{ level }}</td>
<td :class="valueInRange(detailRef.auc, referenceValues.auc[index]) ? 'referenceTd highlight' : 'referenceTd'">
{{ referenceValues.auc[index].label }}
</td>
<td :class="valueInRange(detailRef.ar, referenceValues.ar[index]) ? 'referenceTd highlight' : 'referenceTd'">
{{ referenceValues.ar[index].label }}
</td>
<td :class="valueInRange(detailRef.ks, referenceValues.ks[index]) ? 'referenceTd highlight' : 'referenceTd'">
{{ referenceValues.ks[index].label }}
</td>
</tr>
</table>
</q-tab-panel>
<q-tab-panel name="stability" class="px-0">
<div style="height: 50px"></div>
<div class="row">
<div class="col-4">
<div id="psiEchart" style="width: 100%; height: 300px"></div>
<div class="row justify-center">
<q-btn color="primary" no-caps flat :label="$t('io.sc.engine.mv.result.curve.viewData', { type: 'ROC' })" @click="showData('ROC')" />
</div>
</div>
</div>
</q-tab-panel>
<q-tab-panel name="scaleValidate" class="px-0">
<div class="row">
<div class="col-5 pr-2">
<w-grid
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/mv/sc/roc?modelId=' + detailRef.modelId + '&validateDate=' + detailRef.validateDate)"
:pageable="true"
:toolbar-actions="['refresh', 'separator', 'export']"
:columns="[
{ width: 100, name: 'level', label: $t('io.sc.engine.mv.result.chiSquare.level'), align: 'right' },
{ width: 100, name: 'pd', label: $t('io.sc.engine.mv.result.chiSquare.pd'), align: 'right' },
{ width: 100, name: 'count', label: $t('io.sc.engine.mv.result.chiSquare.count'), align: 'right' },
{ width: 100, name: 'defaultCount', label: $t('io.sc.engine.mv.result.chiSquare.defaultCount'), align: 'right' },
{ width: 100, name: 'chiSquare', label: $t('io.sc.engine.mv.result.chiSquare.chiSquare'), align: 'right' },
]"
></w-grid>
</div>
<div class="col-7 pl-2">
<w-grid
:checkbox-selection="false"
:fetch-data-url="Environment.apiContextPath('/api/mv/sc/roc?modelId=' + detailRef.modelId + '&validateDate=' + detailRef.validateDate)"
:pageable="true"
:toolbar-actions="['refresh', 'separator', 'export']"
:columns="[
{ width: 100, name: 'level', label: $t('io.sc.engine.mv.result.binomial.level'), align: 'right' },
{ width: 100, name: 'pd', label: $t('io.sc.engine.mv.result.binomial.pd'), align: 'right' },
{ width: 100, name: 'count', label: $t('io.sc.engine.mv.result.binomial.count'), align: 'right' },
{ width: 100, name: 'defaultCount', label: $t('io.sc.engine.mv.result.binomial.defaultCount'), align: 'right' },
{ width: 100, name: 'ndAvg', label: $t('io.sc.engine.mv.result.binomial.ndAvg'), align: 'right' },
{ width: 100, name: 'ndSd', label: $t('io.sc.engine.mv.result.binomial.ndSd'), align: 'right' },
{ width: 100, name: 'sl', label: $t('io.sc.engine.mv.result.binomial.sl'), align: 'right' },
{ width: 100, name: 'cl', label: $t('io.sc.engine.mv.result.binomial.cl'), align: 'right' },
{ width: 100, name: 'zUpper', label: $t('io.sc.engine.mv.result.binomial.zUpper'), align: 'right' },
{ width: 100, name: 'zLower', label: $t('io.sc.engine.mv.result.binomial.zLower'), align: 'right' },
{ width: 100, name: 'dUpper', label: $t('io.sc.engine.mv.result.binomial.dUpper'), align: 'right' },
{ width: 100, name: 'dLower', label: $t('io.sc.engine.mv.result.binomial.dLower'), align: 'right' },
{ width: 100, name: 'leUpper', label: $t('io.sc.engine.mv.result.binomial.leUpper'), align: 'right' },
{ width: 100, name: 'geLower', label: $t('io.sc.engine.mv.result.binomial.geLower'), align: 'right' },
]"
></w-grid>
</div>
</div>
</q-tab-panel>
</q-tab-panels>
</div>
<CurveDataDialog ref="curveDataDialogRef"></CurveDataDialog>
</w-dialog>
</template>
<script setup lang="ts">
import { ref, nextTick, computed } from 'vue';
import { useI18n } from 'vue-i18n';
import * as echarts from 'echarts';
import { axios, Environment } from 'platform-core';
import CurveDataDialog from './CurveDataDialog.vue';
const { t } = useI18n();
const referenceValues = {
auc: [
{ label: '[0.00 - 0.65)', from: 0, to: 0.65 },
{ label: '[0.65 - 0.75)', from: 0.65, to: 0.75 },
{ label: '[0.75 - 0.80)', from: 0.75, to: 0.8 },
{ label: '[0.80 - 0.85)', from: 0.8, to: 0.85 },
{ label: '[0.85 - 0.90)', from: 0.85, to: 0.9 },
{ label: '[0.90 - 1.00)', from: 0.9, to: 1 },
],
ar: [
{ label: '[0.0 - 0.3)', from: 0, to: 0.3 },
{ label: '[0.3 - 0.5)', from: 0.3, to: 0.5 },
{ label: '[0.5 - 0.6)', from: 0.5, to: 0.6 },
{ label: '[0.6 - 0.7)', from: 0.6, to: 0.7 },
{ label: '[0.7 - 0.8)', from: 0.7, to: 0.8 },
{ label: '[0.8 - 1.0)', from: 0.8, to: 1 },
],
ks: [
{ label: '[0.00 - 0.20)', from: 0, to: 0.2 },
{ label: '[0.20 - 0.40)', from: 0.2, to: 0.4 },
{ label: '[0.40 - 0.50)', from: 0.4, to: 0.5 },
{ label: '[0.50 - 0.60)', from: 0.5, to: 0.6 },
{ label: '[0.60 - 0.75)', from: 0.6, to: 0.75 },
{ label: '[0.75 - 1.00)', from: 0.75, to: 1 },
],
};
const referenceLevels = computed(() => {
return [
t('io.sc.platform.core.enums.GoodLevel.POOR'),
t('io.sc.platform.core.enums.GoodLevel.MEDIUM'),
t('io.sc.platform.core.enums.GoodLevel.GOOD'),
t('io.sc.platform.core.enums.GoodLevel.VERY_GOOD'),
t('io.sc.platform.core.enums.GoodLevel.EXCELLENT'),
t('io.sc.platform.core.enums.GoodLevel.PERFECT'),
];
});
const dialogRef = ref();
const detailRef = ref();
const selectedTabRef = ref('discrimination');
const curveDataDialogRef = ref();
let rocEchart;
let capEchart;
let ksEchart;
let psiEchart;
const open = (detail: any) => {
detailRef.value = detail;
dialogRef.value.show();
tabChanged();
};
const tabChanged = () => {
nextTick(() => {
if ('discrimination' === selectedTabRef.value) {
if (!rocEchart) {
rocEchart = echarts.init(document.getElementById('rocEchart'));
}
axios
.get(Environment.apiContextPath('/api/mv/sc/cap/option?modelId=' + detailRef.value.modelId + '&validateDate=' + detailRef.value.validateDate))
.then((response) => {
rocEchart?.setOption(response.data);
rocEchart?.resize();
});
if (!capEchart) {
capEchart = echarts.init(document.getElementById('capEchart'));
}
axios
.get(Environment.apiContextPath('/api/mv/sc/cap/option?modelId=' + detailRef.value.modelId + '&validateDate=' + detailRef.value.validateDate))
.then((response) => {
capEchart?.setOption(response.data);
capEchart?.resize();
});
if (!ksEchart) {
ksEchart = echarts.init(document.getElementById('ksEchart'));
}
axios
.get(Environment.apiContextPath('/api/mv/sc/ks/option?modelId=' + detailRef.value.modelId + '&validateDate=' + detailRef.value.validateDate))
.then((response) => {
ksEchart?.setOption(response.data);
ksEchart?.resize();
});
} else if ('stability' === selectedTabRef.value) {
if (!psiEchart) {
psiEchart = echarts.init(document.getElementById('psiEchart'));
}
axios
.get(Environment.apiContextPath('/api/mv/st/psi/option?modelId=' + detailRef.value.modelId + '&validateDate=' + detailRef.value.validateDate))
.then((response) => {
psiEchart?.setOption(response.data);
psiEchart?.resize();
});
}
});
};
const close = () => {
dialogRef.value.hide();
if (rocEchart) {
rocEchart.dispose();
}
if (capEchart) {
capEchart.dispose();
}
if (ksEchart) {
ksEchart.dispose();
}
};
const valueInRange = (value, config) => {
if (value >= config.from && value < config.to) {
return true;
}
return false;
};
const showData = (type: string) => {
curveDataDialogRef.value.open(type, detailRef.value);
};
defineExpose({
open,
close,
});
</script>
<style>
.referenceTh {
border: 1px solid gray;
padding-top: 0px;
padding-right: 10px;
padding-bottom: 0px;
padding-left: 10px;
}
.referenceTd {
border: 1px solid gray;
padding-top: 0px;
padding-right: 10px;
padding-bottom: 0px;
padding-left: 10px;
}
.highlight {
background-color: bisque;
}
</style>