Skip to content
This repository has been archived by the owner on Sep 18, 2024. It is now read-only.

add dataZoom for graph #1736

Merged
merged 4 commits into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"copy-to-clipboard": "^3.0.8",
"css-loader": "0.28.7",
"dotenv": "^8.0.0",
"echarts": "^4.1.0",
"echarts": "^4.5.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to commit yarn.lock.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Thanks.

"echarts-for-react": "^2.0.14",
"file-loader": "^4.1.0",
"fork-ts-checker-webpack-plugin": "^1.5.0",
Expand Down
109 changes: 69 additions & 40 deletions src/webui/src/components/trial-detail/DefaultMetricPoint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Switch } from 'antd';
import ReactEcharts from 'echarts-for-react';
import { EXPERIMENT, TRIALS } from '../../static/datamodel';
import { Trial } from '../../static/model/trial';
import { TooltipForAccuracy } from '../../static/interface';
import { TooltipForAccuracy, EventMap } from '../../static/interface';
require('echarts/lib/chart/scatter');
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
Expand All @@ -16,12 +16,18 @@ interface DefaultPointProps {

interface DefaultPointState {
bestCurveEnabled: boolean;
startY: number; // dataZoomY
endY: number;
}

class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState> {
constructor(props: DefaultPointProps) {
super(props);
this.state = { bestCurveEnabled: false };
this.state = {
bestCurveEnabled: false,
startY: 0, // dataZoomY
endY: 100,
};
}

loadDefault = (checked: boolean) => {
Expand All @@ -35,6 +41,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
render() {
const graph = this.generateGraph();
const accNodata = (graph === EmptyGraph ? 'No data' : '');
const onEvents = { 'dataZoom': this.metricDataZoom };

return (
<div>
Expand All @@ -53,6 +60,7 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
}}
theme="my_theme"
notMerge={true} // update now
onEvents={onEvents}
/>
<div className="showMess">{accNodata}</div>
</div>
Expand All @@ -64,14 +72,66 @@ class DefaultPoint extends React.Component<DefaultPointProps, DefaultPointState>
if (trials.length === 0) {
return EmptyGraph;
}
const graph = generateGraphConfig(trials[trials.length - 1].sequenceId);
const graph = this.generateGraphConfig(trials[trials.length - 1].sequenceId);
if (this.state.bestCurveEnabled) {
(graph as any).series = [ generateBestCurveSeries(trials), generateScatterSeries(trials) ];
(graph as any).series = [generateBestCurveSeries(trials), generateScatterSeries(trials)];
} else {
(graph as any).series = [ generateScatterSeries(trials) ];
(graph as any).series = [generateScatterSeries(trials)];
}
return graph;
}

private generateGraphConfig(maxSequenceId: number) {
const { startY, endY } = this.state;
return {
grid: {
left: '8%',
},
tooltip: {
trigger: 'item',
enterable: true,
position: (point: Array<number>, data: TooltipForAccuracy) => (
[(data.data[0] < maxSequenceId ? point[0] : (point[0] - 300)), 80]
),
formatter: (data: TooltipForAccuracy) => (
'<div class="tooldetailAccuracy">' +
'<div>Trial No.: ' + data.data[0] + '</div>' +
'<div>Default metric: ' + data.data[1] + '</div>' +
'<div>Parameters: <pre>' + JSON.stringify(data.data[2], null, 4) + '</pre></div>' +
'</div>'
),
},
dataZoom: [
{
id: 'dataZoomY',
type: 'inside',
yAxisIndex: [0],
filterMode: 'empty',
start: startY,
end: endY
}
],
xAxis: {
name: 'Trial',
type: 'category',
},
yAxis: {
name: 'Default metric',
type: 'value',
scale: true,
},
series: undefined,
};
}

private metricDataZoom = (e: EventMap) => {
if (e.batch !== undefined) {
this.setState(() => ({
startY: (e.batch[0].start !== null ? e.batch[0].start : 0),
endY: (e.batch[0].end !== null ? e.batch[0].end : 100)
}));
}
}
}

const EmptyGraph = {
Expand All @@ -85,41 +145,10 @@ const EmptyGraph = {
yAxis: {
name: 'Default metric',
type: 'value',
scale: true,
}
};

function generateGraphConfig(maxSequenceId: number) {
return {
grid: {
left: '8%',
},
tooltip: {
trigger: 'item',
enterable: true,
position: (point: Array<number>, data: TooltipForAccuracy) => (
[ (data.data[0] < maxSequenceId ? point[0] : (point[0] - 300)), 80 ]
),
formatter: (data: TooltipForAccuracy) => (
'<div class="tooldetailAccuracy">' +
'<div>Trial No.: ' + data.data[0] + '</div>' +
'<div>Default metric: ' + data.data[1] + '</div>' +
'<div>Parameters: <pre>' + JSON.stringify(data.data[2], null, 4) + '</pre></div>' +
'</div>'
),
},
xAxis: {
name: 'Trial',
type: 'category',
},
yAxis: {
name: 'Default metric',
type: 'value',
scale: true,
},
series: undefined,
};
}

function generateScatterSeries(trials: Trial[]) {
const data = trials.map(trial => [
trial.sequenceId,
Expand All @@ -135,17 +164,17 @@ function generateScatterSeries(trials: Trial[]) {

function generateBestCurveSeries(trials: Trial[]) {
let best = trials[0];
const data = [[ best.sequenceId, best.accuracy, best.description.parameters ]];
const data = [[best.sequenceId, best.accuracy, best.description.parameters]];

for (let i = 1; i < trials.length; i++) {
const trial = trials[i];
const delta = trial.accuracy! - best.accuracy!;
const better = (EXPERIMENT.optimizeMode === 'minimize') ? (delta < 0) : (delta > 0);
if (better) {
data.push([ trial.sequenceId, trial.accuracy, trial.description.parameters ]);
data.push([trial.sequenceId, trial.accuracy, trial.description.parameters]);
best = trial;
} else {
data.push([ trial.sequenceId, best.accuracy, trial.description.parameters ]);
data.push([trial.sequenceId, best.accuracy, trial.description.parameters]);
}
}

Expand Down
117 changes: 32 additions & 85 deletions src/webui/src/components/trial-detail/Duration.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react';
import ReactEcharts from 'echarts-for-react';
import { TableObj } from 'src/static/interface';
import { TableObj, EventMap } from 'src/static/interface';
import { filterDuration } from 'src/static/function';
require('echarts/lib/chart/bar');
require('echarts/lib/component/tooltip');
Expand All @@ -17,7 +17,8 @@ interface DurationProps {
}

interface DurationState {
durationSource: {};
startDuration: number; // for record data zoom
endDuration: number;
}

class Duration extends React.Component<DurationProps, DurationState> {
Expand All @@ -26,63 +27,13 @@ class Duration extends React.Component<DurationProps, DurationState> {

super(props);
this.state = {
durationSource: this.initDuration(this.props.source),
};

}

initDuration = (source: Array<TableObj>) => {
const trialId: Array<string> = [];
const trialTime: Array<number> = [];
const trialJobs = source.filter(filterDuration);
Object.keys(trialJobs).map(item => {
const temp = trialJobs[item];
trialId.push(temp.sequenceId);
trialTime.push(temp.duration);
});
return {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
bottom: '3%',
containLabel: true,
left: '1%',
right: '4%'
},

dataZoom: [{
type: 'slider',
name: 'trial',
filterMode: 'filter',
yAxisIndex: 0,
orient: 'vertical'
}, {
type: 'slider',
name: 'trial',
filterMode: 'filter',
xAxisIndex: 0
}],
xAxis: {
name: 'Time',
type: 'value',
},
yAxis: {
name: 'Trial',
type: 'category',
data: trialId
},
series: [{
type: 'bar',
data: trialTime
}]
startDuration: 0, // for record data zoom
endDuration: 100,
};
}

getOption = (dataObj: Runtrial) => {
const { startDuration, endDuration } = this.state;
return {
tooltip: {
trigger: 'axis',
Expand All @@ -96,19 +47,16 @@ class Duration extends React.Component<DurationProps, DurationState> {
left: '1%',
right: '4%'
},

dataZoom: [{
type: 'slider',
name: 'trial',
filterMode: 'filter',
yAxisIndex: 0,
orient: 'vertical'
}, {
type: 'slider',
name: 'trial',
filterMode: 'filter',
xAxisIndex: 0
}],
dataZoom: [
{
id: 'dataZoomY',
type: 'inside',
yAxisIndex: [0],
filterMode: 'empty',
start: startDuration,
end: endDuration
},
],
xAxis: {
name: 'Time',
type: 'value',
Expand Down Expand Up @@ -140,21 +88,7 @@ class Duration extends React.Component<DurationProps, DurationState> {
trialId: trialId,
trialTime: trialTime
});
this.setState({
durationSource: this.getOption(trialRun[0])
});
}

componentDidMount() {
const { source } = this.props;
this.drawDurationGraph(source);
}

componentWillReceiveProps(nextProps: DurationProps) {
const { whichGraph, source } = nextProps;
if (whichGraph === '3') {
this.drawDurationGraph(source);
}
return this.getOption(trialRun[0]);
}

shouldComponentUpdate(nextProps: DurationProps, nextState: DurationState) {
Expand Down Expand Up @@ -183,18 +117,31 @@ class Duration extends React.Component<DurationProps, DurationState> {
}

render() {
const { durationSource } = this.state;

const { source } = this.props;
const graph = this.drawDurationGraph(source);
const onEvents = { 'dataZoom': this.durationDataZoom };
return (
<div>
<ReactEcharts
option={durationSource}
option={graph}
style={{ width: '95%', height: 412, margin: '0 auto' }}
theme="my_theme"
notMerge={true} // update now
onEvents={onEvents}
/>
</div>
);
}

private durationDataZoom = (e: EventMap) => {
if (e.batch !== undefined) {
this.setState(() => ({
startDuration: (e.batch[0].start !== null ? e.batch[0].start : 0),
endDuration: (e.batch[0].end !== null ? e.batch[0].end : 100)
}));
}
}
}

export default Duration;
Loading