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