ScalarChart.tsx 6.7 KB
Newer Older
P
Peter Pan 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 * Copyright 2020 Baidu Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

17
import type {Dataset, Range, ScalarDataset} from '~/resource/scalar';
P
Peter Pan 已提交
18 19
import React, {FunctionComponent, useCallback, useMemo} from 'react';
import SChart, {DownloadDataTypes, chartSize, chartSizeInRem} from '~/components/ScalarChart';
20
import {
P
Peter Pan 已提交
21 22
    SortingMethod,
    XAxis,
23
    chartData,
24
    nearestPoint,
25
    singlePointRange,
26 27 28
    sortingMethodMap,
    tooltip,
    xAxisMap
P
Peter Pan 已提交
29
} from '~/resource/scalar';
30

P
Peter Pan 已提交
31 32
import Chart from '~/components/Chart';
import {Chart as ChartLoader} from '~/components/Loader/ChartPage';
33
import type {Run} from '~/types';
P
Peter Pan 已提交
34
import {XAxisType} from '~/components/LineChart';
35 36
import {cycleFetcher} from '~/utils/fetch';
import queryString from 'query-string';
P
Peter Pan 已提交
37
import saveFile from '~/utils/saveFile';
38 39
import styled from 'styled-components';
import {useRunningRequest} from '~/hooks/useRequest';
40
import {useTranslation} from 'react-i18next';
P
Peter Pan 已提交
41
import useWebAssembly from '~/hooks/useWebAssembly';
42

43
const Error = styled.div`
P
Peter Pan 已提交
44 45
    width: 100%;
    height: 100%;
46 47 48 49 50
    display: flex;
    justify-content: center;
    align-items: center;
`;

51
type ScalarChartProps = {
52
    runs: Run[];
53 54
    tag: string;
    smoothing: number;
P
Peter Pan 已提交
55 56
    xAxis: XAxis;
    sortingMethod: SortingMethod;
57
    outlier?: boolean;
58
    smoothedOnly?: boolean;
59
    showMostValue?: boolean;
60 61 62 63 64 65 66 67 68 69
    running?: boolean;
};

const ScalarChart: FunctionComponent<ScalarChartProps> = ({
    runs,
    tag,
    smoothing,
    xAxis,
    sortingMethod,
    outlier,
70
    smoothedOnly,
71
    showMostValue,
72 73
    running
}) => {
P
Peter Pan 已提交
74
    const {t, i18n} = useTranslation(['scalar', 'common']);
75

76 77 78 79 80
    const {
        data: datasets,
        error,
        loading
    } = useRunningRequest<(ScalarDataset | null)[]>(
P
Peter Pan 已提交
81
        runs.map(run => `/scalar/list?${queryString.stringify({run: run.label, tag})}`),
82 83
        !!running,
        (...urls) => cycleFetcher(urls)
84 85
    );

P
Peter Pan 已提交
86
    const xAxisType = useMemo(() => (xAxis === XAxis.WallTime ? XAxisType.time : XAxisType.value), [xAxis]);
87

88 89 90 91 92 93 94 95
    const transformParams = useMemo(
        () => [
            datasets?.map(data => data?.map(row => [row[0], row[1], Number.isFinite(row[2]) ? row[2] : null]) ?? []) ??
                [],
            smoothing
        ],
        [datasets, smoothing]
    );
P
Peter Pan 已提交
96
    const {data: smoothedDatasetsOrUndefined} = useWebAssembly<Dataset[]>('scalar_transform', transformParams);
97 98 99 100
    const smoothedDatasets = useMemo<NonNullable<typeof smoothedDatasetsOrUndefined>>(
        () => smoothedDatasetsOrUndefined ?? [],
        [smoothedDatasetsOrUndefined]
    );
101

P
Peter Pan 已提交
102 103
    const axisRangeParams = useMemo(() => [smoothedDatasets, !!outlier], [smoothedDatasets, outlier]);
    const {data: yRange} = useWebAssembly<Range>('scalar_axis_range', axisRangeParams);
104

P
Peter Pan 已提交
105 106
    const datasetRangesParams = useMemo(() => [smoothedDatasets], [smoothedDatasets]);
    const {data: datasetRanges} = useWebAssembly<Range[]>('scalar_range', datasetRangesParams);
107

108 109 110 111 112 113
    const ranges: Record<'x' | 'y', Range | undefined> = useMemo(() => {
        let x: Range | undefined = undefined;
        let y: Range | undefined = yRange;

        // if there is only one point, place it in the middle
        if (smoothedDatasets.length === 1 && smoothedDatasets[0].length === 1) {
114
            if ([XAxisType.value, XAxisType.log].includes(xAxisType)) {
115 116 117 118 119
                x = singlePointRange(smoothedDatasets[0][0][xAxisMap[xAxis]]);
            }
            y = singlePointRange(smoothedDatasets[0][0][2]);
        }
        return {x, y};
120
    }, [smoothedDatasets, yRange, xAxisType, xAxis]);
121

122 123
    const data = useMemo(
        () =>
124
            chartData({
125
                data: smoothedDatasets.slice(0, runs.length),
126
                ranges: showMostValue ? datasetRanges ?? [] : [],
127
                runs,
128 129
                xAxis,
                smoothedOnly
130
            }),
131
        [smoothedDatasets, datasetRanges, runs, xAxis, smoothedOnly, showMostValue]
132 133
    );

P
Peter Pan 已提交
134 135 136 137
    const maxStepLength = useMemo(
        () => String(Math.max(...smoothedDatasets.map(i => Math.max(...i.map(j => j[1]))))).length,
        [smoothedDatasets]
    );
P
Peter Pan 已提交
138 139
    const getTooltipTableData = useCallback(
        (series: number[]) => {
P
Peter Pan 已提交
140
            const idx = xAxisMap[xAxis];
141
            const points = nearestPoint(smoothedDatasets ?? [], runs, idx, series[idx]).map(point => ({
142
                ...point,
143
                ...datasetRanges?.[runs.findIndex(run => run.label === point.run.label)]
144
            }));
145
            const sort = sortingMethodMap[sortingMethod];
P
Peter Pan 已提交
146
            const sorted = sort(points, series);
147
            const {columns, data} = tooltip(sorted, maxStepLength, i18n);
P
Peter Pan 已提交
148 149 150 151 152
            return {
                runs: sorted.map(i => i.run),
                columns,
                data
            };
153
        },
P
Peter Pan 已提交
154
        [smoothedDatasets, datasetRanges, runs, sortingMethod, xAxis, maxStepLength, i18n]
P
Peter Pan 已提交
155 156
    );

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    const downloadData = useCallback(
        (type: keyof typeof DownloadDataTypes) => {
            saveFile(
                runs.map(
                    run =>
                        `/scalar/data?${queryString.stringify({
                            run: run.label,
                            tag,
                            type
                        })}`
                ),
                runs.map(run => `visualdl-scalar-${run.label}-${tag}.${DownloadDataTypes[type]}`),
                `visualdl-scalar-${tag}.zip`
            );
        },
        [runs, tag]
    );

175 176 177
    // display error only on first fetch
    if (!data && error) {
        return <Error>{t('common:error')}</Error>;
178 179
    }

180
    return (
P
Peter Pan 已提交
181 182 183 184 185 186 187 188 189 190
        <SChart
            title={tag}
            data={data}
            loading={loading}
            xAxisType={xAxisType}
            xRange={ranges.x}
            yRange={ranges.y}
            getTooltipTableData={getTooltipTableData}
            downloadData={downloadData}
        />
191 192 193 194
    );
};

export default ScalarChart;
P
Peter Pan 已提交
195 196 197 198 199 200 201 202 203 204 205

export const Loader: FunctionComponent = () => (
    <>
        <Chart {...chartSizeInRem}>
            <ChartLoader width={chartSize.width - 2} height={chartSize.height - 2} />
        </Chart>
        <Chart {...chartSizeInRem}>
            <ChartLoader width={chartSize.width - 2} height={chartSize.height - 2} />
        </Chart>
    </>
);