未验证 提交 e8c58871 编写于 作者: P Peter Pan 提交者: GitHub

minor bug fix (#723)

* fix: do not fetch unused steps of runs in pr-curve page

* fix: error when switch to overlay mode in histogram page

* feat: pr-curve show all matched points in tooltip
上级 a31d235d
...@@ -60,7 +60,7 @@ const LineChart = React.forwardRef<LineChartRef, LineChartProps & WithStyled>( ...@@ -60,7 +60,7 @@ const LineChart = React.forwardRef<LineChartRef, LineChartProps & WithStyled>(
useEffect(() => { useEffect(() => {
if (process.browser) { if (process.browser) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
const {color, colorAlt, ...defaults} = chart; const {color, colorAlt, series, ...defaults} = chart;
let chartOptions: EChartOption = defaultsDeep( let chartOptions: EChartOption = defaultsDeep(
{ {
...@@ -75,7 +75,7 @@ const LineChart = React.forwardRef<LineChartRef, LineChartProps & WithStyled>( ...@@ -75,7 +75,7 @@ const LineChart = React.forwardRef<LineChartRef, LineChartProps & WithStyled>(
type: 'line' type: 'line'
}, },
item, item,
chart.series series
) )
) )
}, },
......
...@@ -140,17 +140,26 @@ const PRCurveChart: FunctionComponent<PRCurveChartProps> = ({cid, runs, tag, run ...@@ -140,17 +140,26 @@ const PRCurveChart: FunctionComponent<PRCurveChartProps> = ({cid, runs, tag, run
label: t('pr-curve:false-negatives') label: t('pr-curve:false-negatives')
} }
]; ];
const data = points.map(([precision, recall, tp, fp, tn, fn, threshold]) => [ const runData = points.reduce<Run[]>((m, runPoints, index) => {
valueFormatter(threshold), m.push(...new Array(runPoints.length).fill(runs[index]));
axisFormatter(precision), return m;
axisFormatter(recall), }, []);
tp, const data = points.reduce<(string | number)[][]>((m, runPoints) => {
fp, m.push(
tn, ...runPoints.map(([precision, recall, tp, fp, tn, fn, threshold]) => [
fn valueFormatter(threshold),
]); axisFormatter(precision),
axisFormatter(recall),
tp,
fp,
tn,
fn
])
);
return m;
}, []);
return renderToStaticMarkup( return renderToStaticMarkup(
<TooltipTable run={t('common:runs')} runs={runs} columns={columns} data={data} /> <TooltipTable run={t('common:runs')} runs={runData} columns={columns} data={data} />
); );
}, },
[selectedData, runs, t] [selectedData, runs, t]
......
...@@ -232,7 +232,7 @@ const ScalarChart: FunctionComponent<ScalarChartProps> = ({ ...@@ -232,7 +232,7 @@ const ScalarChart: FunctionComponent<ScalarChartProps> = ({
}, },
{ {
icon: 'log-axis', icon: 'log-axis',
tooltip: t('scalar:axis'), tooltip: t('scalar:toggle-log-axis'),
toggle: true, toggle: true,
onClick: toggleYAxisType onClick: toggleYAxisType
}, },
......
...@@ -138,6 +138,7 @@ const reducer = (state: State, action: Action): State => { ...@@ -138,6 +138,7 @@ const reducer = (state: State, action: Action): State => {
} }
}; };
// TODO: refactor to improve performance
const useTagFilter = (type: string, running: boolean) => { const useTagFilter = (type: string, running: boolean) => {
const router = useRouter(); const router = useRouter();
...@@ -173,8 +174,27 @@ const useTagFilter = (type: string, running: boolean) => { ...@@ -173,8 +174,27 @@ const useTagFilter = (type: string, running: boolean) => {
useEffect(() => dispatch({type: ActionType.setSelectedRuns, payload: runsFromQuery}), [runsFromQuery]); useEffect(() => dispatch({type: ActionType.setSelectedRuns, payload: runsFromQuery}), [runsFromQuery]);
useEffect(() => dispatch({type: ActionType.initTags, payload: tags || {}}), [tags]); useEffect(() => dispatch({type: ActionType.initTags, payload: tags || {}}), [tags]);
const tagsWithSingleRun = useMemo(
() =>
state.tags.reduce<TagWithSingleRun[]>((prev, {runs, ...item}) => {
Array.prototype.push.apply(
prev,
runs.map(run => ({...item, run, id: `${item.label}-${run.label}`}))
);
return prev;
}, []),
[state.tags]
);
const runsInTags = useMemo(() => state.selectedRuns.filter(run => !!tags?.[run.label]?.length), [
state.selectedRuns,
tags
]);
return { return {
...state, ...state,
tagsWithSingleRun,
runsInTags,
onChangeRuns, onChangeRuns,
onChangeTags, onChangeTags,
loadingRuns, loadingRuns,
...@@ -183,13 +203,3 @@ const useTagFilter = (type: string, running: boolean) => { ...@@ -183,13 +203,3 @@ const useTagFilter = (type: string, running: boolean) => {
}; };
export default useTagFilter; export default useTagFilter;
export function ungroup(tags: Tag[]) {
return tags.reduce<TagWithSingleRun[]>((prev, {runs, ...item}) => {
Array.prototype.push.apply(
prev,
runs.map(run => ({...item, run, id: `${item.label}-${run.label}`}))
);
return prev;
}, []);
}
...@@ -21,14 +21,9 @@ const Histogram: NextI18NextPage = () => { ...@@ -21,14 +21,9 @@ const Histogram: NextI18NextPage = () => {
const [running, setRunning] = useState(true); const [running, setRunning] = useState(true);
const {runs, tags, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter('histogram', running); const {runs, tagsWithSingleRun, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter(
const tagsWithSingleRun = useMemo( 'histogram',
() => running
tags.reduce<(TagWithSingleRun & {id: string})[]>((result, tag) => {
result.push(...tag.runs.map(run => ({id: `${tag.label}-${run.label}`, label: tag.label, run})));
return result;
}, []),
[tags]
); );
const [mode, setMode] = useState<Modes>(Modes.Offset); const [mode, setMode] = useState<Modes>(Modes.Offset);
......
...@@ -36,6 +36,10 @@ const StepSliderWrapper = styled.div` ...@@ -36,6 +36,10 @@ const StepSliderWrapper = styled.div`
margin-top: 0; margin-top: 0;
padding-top: ${rem(20)}; padding-top: ${rem(20)};
} }
&:empty + .run-section {
border-top: none;
}
`; `;
const PRCurve: NextI18NextPage = () => { const PRCurve: NextI18NextPage = () => {
...@@ -43,7 +47,10 @@ const PRCurve: NextI18NextPage = () => { ...@@ -43,7 +47,10 @@ const PRCurve: NextI18NextPage = () => {
const [running, setRunning] = useState(true); const [running, setRunning] = useState(true);
const {runs, tags, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter('pr-curve', running); const {runs, tags, runsInTags, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter(
'pr-curve',
running
);
const [indexes, setIndexes] = useState<Record<string, number>>({}); const [indexes, setIndexes] = useState<Record<string, number>>({});
const onChangeIndexes = useCallback( const onChangeIndexes = useCallback(
...@@ -57,31 +64,31 @@ const PRCurve: NextI18NextPage = () => { ...@@ -57,31 +64,31 @@ const PRCurve: NextI18NextPage = () => {
useEffect( useEffect(
() => () =>
setIndexes(indexes => setIndexes(indexes =>
selectedRuns.reduce<typeof indexes>((m, c) => { runsInTags.reduce<typeof indexes>((m, c) => {
if (indexes[c.label] != null) { if (indexes[c.label] != null) {
m[c.label] = indexes[c.label]; m[c.label] = indexes[c.label];
} }
return m; return m;
}, {}) }, {})
), ),
[selectedRuns] [runsInTags]
); );
const {data: stepInfo} = useRunningRequest<StepInfo[]>( const {data: stepInfo} = useRunningRequest<StepInfo[]>(
selectedRuns.map(run => `/pr-curve/steps?${queryString.stringify({run: run.label})}`), runsInTags.map(run => `/pr-curve/steps?${queryString.stringify({run: run.label})}`),
!!running, !!running,
(...urls) => cycleFetcher(urls) (...urls) => cycleFetcher(urls)
); );
const runWithInfo = useMemo<Run[]>( const runWithInfo = useMemo<Run[]>(
() => () =>
selectedRuns.map((run, i) => ({ runsInTags.map((run, i) => ({
...run, ...run,
index: indexes[run.label] ?? (stepInfo?.[i].length ?? 1) - 1, index: indexes[run.label] ?? (stepInfo?.[i].length ?? 1) - 1,
steps: stepInfo?.[i].map(j => j[1]) ?? [], steps: stepInfo?.[i].map(j => j[1]) ?? [],
wallTimes: stepInfo?.[i].map(j => Math.floor(j[0] * 1000)) ?? [], wallTimes: stepInfo?.[i].map(j => Math.floor(j[0] * 1000)) ?? [],
relatives: stepInfo?.[i].map(j => (j[0] - stepInfo[i][0][0]) * 1000) ?? [] relatives: stepInfo?.[i].map(j => (j[0] - stepInfo[i][0][0]) * 1000) ?? []
})), })),
[selectedRuns, stepInfo, indexes] [runsInTags, stepInfo, indexes]
); );
const [timeType, setTimeType] = useState<TimeType>(TimeType.Step); const [timeType, setTimeType] = useState<TimeType>(TimeType.Step);
...@@ -141,7 +148,7 @@ const PRCurve: NextI18NextPage = () => { ...@@ -141,7 +148,7 @@ const PRCurve: NextI18NextPage = () => {
return ( return (
<> <>
<Preloader url="/runs" /> <Preloader url="/runs" />
<Preloader url="/scalar/tags" /> <Preloader url="/pr-curve/tags" />
<Title>{t('common:pr-curve')}</Title> <Title>{t('common:pr-curve')}</Title>
<Content aside={aside} loading={loadingRuns}> <Content aside={aside} loading={loadingRuns}>
{!loadingRuns && !runs.length ? ( {!loadingRuns && !runs.length ? (
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
import ChartPage, {WithChart} from '~/components/ChartPage'; import ChartPage, {WithChart} from '~/components/ChartPage';
import {NextI18NextPage, useTranslation} from '~/utils/i18n'; import {NextI18NextPage, useTranslation} from '~/utils/i18n';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import useTagFilter, {ungroup} from '~/hooks/useTagFilter';
import AudioChart from '~/components/SamplePage/AudioChart'; import AudioChart from '~/components/SamplePage/AudioChart';
import Content from '~/components/Content'; import Content from '~/components/Content';
...@@ -12,6 +11,7 @@ import Preloader from '~/components/Preloader'; ...@@ -12,6 +11,7 @@ import Preloader from '~/components/Preloader';
import RunAside from '~/components/RunAside'; import RunAside from '~/components/RunAside';
import Title from '~/components/Title'; import Title from '~/components/Title';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import useTagFilter from '~/hooks/useTagFilter';
const chartSize = { const chartSize = {
height: rem(244) height: rem(244)
...@@ -35,9 +35,10 @@ const Audio: NextI18NextPage = () => { ...@@ -35,9 +35,10 @@ const Audio: NextI18NextPage = () => {
const [running, setRunning] = useState(true); const [running, setRunning] = useState(true);
const {runs, tags, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter('audio', running); const {runs, tagsWithSingleRun, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter(
'audio',
const ungroupedSelectedTags = useMemo(() => ungroup(tags), [tags]); running
);
const aside = useMemo( const aside = useMemo(
() => () =>
...@@ -53,7 +54,7 @@ const Audio: NextI18NextPage = () => { ...@@ -53,7 +54,7 @@ const Audio: NextI18NextPage = () => {
[onChangeRuns, running, runs, selectedRuns] [onChangeRuns, running, runs, selectedRuns]
); );
const withChart = useCallback<WithChart<typeof ungroupedSelectedTags[number]>>( const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>(
({run, label}) => <AudioChart audioContext={audioContext.current} run={run} tag={label} running={running} />, ({run, label}) => <AudioChart audioContext={audioContext.current} run={run} tag={label} running={running} />,
[running] [running]
); );
...@@ -70,7 +71,7 @@ const Audio: NextI18NextPage = () => { ...@@ -70,7 +71,7 @@ const Audio: NextI18NextPage = () => {
<Error /> <Error />
) : ( ) : (
<ChartPage <ChartPage
items={ungroupedSelectedTags} items={tagsWithSingleRun}
chartSize={chartSize} chartSize={chartSize}
withChart={withChart} withChart={withChart}
loading={loadingRuns || loadingTags} loading={loadingRuns || loadingTags}
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
import ChartPage, {WithChart} from '~/components/ChartPage'; import ChartPage, {WithChart} from '~/components/ChartPage';
import {NextI18NextPage, useTranslation} from '~/utils/i18n'; import {NextI18NextPage, useTranslation} from '~/utils/i18n';
import React, {useCallback, useMemo, useState} from 'react'; import React, {useCallback, useMemo, useState} from 'react';
import useTagFilter, {ungroup} from '~/hooks/useTagFilter';
import {AsideSection} from '~/components/Aside'; import {AsideSection} from '~/components/Aside';
import Checkbox from '~/components/Checkbox'; import Checkbox from '~/components/Checkbox';
...@@ -16,6 +15,7 @@ import RunAside from '~/components/RunAside'; ...@@ -16,6 +15,7 @@ import RunAside from '~/components/RunAside';
import Slider from '~/components/Slider'; import Slider from '~/components/Slider';
import Title from '~/components/Title'; import Title from '~/components/Title';
import {rem} from '~/utils/style'; import {rem} from '~/utils/style';
import useTagFilter from '~/hooks/useTagFilter';
const chartSize = { const chartSize = {
height: rem(406) height: rem(406)
...@@ -26,9 +26,10 @@ const Image: NextI18NextPage = () => { ...@@ -26,9 +26,10 @@ const Image: NextI18NextPage = () => {
const [running, setRunning] = useState(true); const [running, setRunning] = useState(true);
const {runs, tags, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter('image', running); const {runs, tagsWithSingleRun, selectedRuns, onChangeRuns, loadingRuns, loadingTags} = useTagFilter(
'image',
const ungroupedSelectedTags = useMemo(() => ungroup(tags), [tags]); running
);
const [showActualSize, setShowActualSize] = useState(false); const [showActualSize, setShowActualSize] = useState(false);
const [brightness, setBrightness] = useState(1); const [brightness, setBrightness] = useState(1);
...@@ -64,7 +65,7 @@ const Image: NextI18NextPage = () => { ...@@ -64,7 +65,7 @@ const Image: NextI18NextPage = () => {
[t, brightness, contrast, onChangeRuns, running, runs, selectedRuns, showActualSize] [t, brightness, contrast, onChangeRuns, running, runs, selectedRuns, showActualSize]
); );
const withChart = useCallback<WithChart<typeof ungroupedSelectedTags[number]>>( const withChart = useCallback<WithChart<typeof tagsWithSingleRun[number]>>(
({run, label}) => ( ({run, label}) => (
<ImageChart <ImageChart
run={run} run={run}
...@@ -90,7 +91,7 @@ const Image: NextI18NextPage = () => { ...@@ -90,7 +91,7 @@ const Image: NextI18NextPage = () => {
<Error /> <Error />
) : ( ) : (
<ChartPage <ChartPage
items={ungroupedSelectedTags} items={tagsWithSingleRun}
chartSize={chartSize} chartSize={chartSize}
withChart={withChart} withChart={withChart}
loading={loadingRuns || loadingTags} loading={loadingRuns || loadingTags}
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"minimize": "Minimize", "minimize": "Minimize",
"precision": "Precision", "precision": "Precision",
"recall": "Recall", "recall": "Recall",
"restore": "Restore", "restore": "Selection restore",
"threshold": "Threshold", "threshold": "Threshold",
"time-display-type": "Time Display Type", "time-display-type": "Time Display Type",
"true-negatives": "TN", "true-negatives": "TN",
......
{ {
"axis": "Axis",
"download-image": "Download image", "download-image": "Download image",
"ignore-outliers": "Ignore outliers in chart scaling", "ignore-outliers": "Ignore outliers in chart scaling",
"maximize": "Maximize", "maximize": "Maximize",
"minimize": "Minimize", "minimize": "Minimize",
"restore": "Restore", "restore": "Selection restore",
"smoothed": "Smoothed", "smoothed": "Smoothed",
"smoothing": "Smoothing", "smoothing": "Smoothing",
"toggle-log-axis": "Logarithmic axis",
"tooltip-sorting": "Tooltip Sorting", "tooltip-sorting": "Tooltip Sorting",
"tooltip-sorting-value": { "tooltip-sorting-value": {
"ascending": "Ascending", "ascending": "Ascending",
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
"minimize": "最小化", "minimize": "最小化",
"precision": "Precision", "precision": "Precision",
"recall": "Recall", "recall": "Recall",
"restore": "还原", "restore": "还原图表框选",
"threshold": "Threshold", "threshold": "Threshold",
"time-display-type": "时间显示类型", "time-display-type": "时间显示类型",
"true-negatives": "TN", "true-negatives": "TN",
......
{ {
"axis": "坐标轴",
"download-image": "下载图片", "download-image": "下载图片",
"ignore-outliers": "图表缩放时忽略极端值", "ignore-outliers": "图表缩放时忽略极端值",
"maximize": "最大化", "maximize": "最大化",
"minimize": "最小化", "minimize": "最小化",
"restore": "还原", "restore": "还原图表框选",
"smoothed": "Smoothed", "smoothed": "Smoothed",
"smoothing": "平滑度", "smoothing": "平滑度",
"toggle-log-axis": "切换对数坐标轴",
"tooltip-sorting": "标签排序方法", "tooltip-sorting": "标签排序方法",
"tooltip-sorting-value": { "tooltip-sorting-value": {
"ascending": "升序", "ascending": "升序",
......
export const nearestPoint = (data: number[][][], recall: number): number[][] => { export const nearestPoint = (data: number[][][], recall: number): number[][][] => {
return data.map(series => { return data.map(series => {
let delta = Number.POSITIVE_INFINITY; let delta = Number.POSITIVE_INFINITY;
let index = 0; let nearestRecall = 0;
for (let i = 0; i < series.length; i++) { for (let i = 0; i < series.length; i++) {
if (series[i][1] - recall < Number.EPSILON) { const d = Math.abs(series[i][1] - recall);
return series[i]; if (d < Number.EPSILON) {
nearestRecall = series[i][1];
break;
} }
if (Math.abs(series[i][1] - recall) < delta) { if (d < delta) {
delta = Math.abs(series[i][1] - recall); delta = d;
index = i; nearestRecall = series[i][1];
} }
} }
return series[index]; return series.filter(s => s[1] === nearestRecall);
}); });
}; };
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册