in tensorboard/webapp/metrics/views/card_renderer/scalar_card_container.ts [220:472]
ngOnInit() {
const selectCardMetadata$ = this.store.select(getCardMetadata, this.cardId);
const cardMetadata$ = selectCardMetadata$.pipe(
filter((cardMetadata) => {
return !!cardMetadata && this.isScalarCardMetadata(cardMetadata);
}),
map((cardMetadata) => {
return cardMetadata as ScalarCardMetadata;
})
);
const nonNullRunsToScalarSeries$ = this.store
.select(getCardTimeSeries, this.cardId)
.pipe(
takeUntil(this.ngUnsubscribe),
filter((runToSeries) => Boolean(runToSeries)),
map((runToSeries) => runToSeries as RunToSeries<PluginType.SCALARS>),
shareReplay(1)
);
const partialSeries$ = nonNullRunsToScalarSeries$.pipe(
combineLatestWith(this.store.select(getMetricsXAxisType)),
map(([runToSeries, xAxisType]) => {
const runIds = Object.keys(runToSeries);
const results = runIds.map((runId) => {
return {
runId,
points: this.stepSeriesToLineSeries(runToSeries[runId], xAxisType),
};
});
return results;
}),
distinctUntilChanged(areSeriesEqual)
);
function getSmoothedSeriesId(seriesId: string): string {
return JSON.stringify(['smoothed', seriesId]);
}
const partitionedSeries$ = partialSeries$.pipe(
combineLatestWith(
this.store.select(getMetricsScalarPartitionNonMonotonicX)
),
takeUntil(this.ngUnsubscribe),
map<[PartialSeries[], boolean], PartitionedSeries[]>(
([normalizedSeries, enablePartition]) => {
if (enablePartition) return partitionSeries(normalizedSeries);
return normalizedSeries.map((series) => {
return {
...series,
seriesId: series.runId,
partitionIndex: 0,
partitionSize: 1,
};
});
}
),
map((partitionedSeriesList) => {
return partitionedSeriesList.map((partitionedSeries) => {
const firstWallTime = partitionedSeries.points[0]?.wallTime;
return {
...partitionedSeries,
points: partitionedSeries.points.map((point) => {
return {
...point,
relativeTimeInMs: point.wallTime - firstWallTime,
};
}),
};
});
}),
combineLatestWith(this.store.select(getMetricsXAxisType)),
map(([partitionedSeriesList, xAxisType]) => {
return partitionedSeriesList.map((series) => {
return {
...series,
points: series.points.map((point) => {
let x: number;
switch (xAxisType) {
case XAxisType.RELATIVE:
x = point.relativeTimeInMs;
break;
case XAxisType.WALL_TIME:
x = point.wallTime;
break;
case XAxisType.STEP:
default:
x = point.step;
}
return {...point, x};
}),
};
});
}),
shareReplay(1)
);
this.dataSeries$ = partitionedSeries$.pipe(
// Smooth
combineLatestWith(this.store.select(getMetricsScalarSmoothing)),
switchMap<
[PartitionedSeries[], number],
Observable<ScalarCardDataSeries[]>
>(([runsData, smoothing]) => {
const cleanedRunsData = runsData.map(({seriesId, points}) => ({
id: seriesId,
points,
}));
if (smoothing <= 0) {
return of(cleanedRunsData);
}
return from(classicSmoothing(cleanedRunsData, smoothing)).pipe(
map((smoothedDataSeriesList) => {
const smoothedList = cleanedRunsData.map((dataSeries, index) => {
return {
id: getSmoothedSeriesId(dataSeries.id),
points: smoothedDataSeriesList[index].points.map(
({y}, pointIndex) => {
return {...dataSeries.points[pointIndex], y};
}
),
};
});
return [...cleanedRunsData, ...smoothedList];
})
);
}),
startWith([] as ScalarCardDataSeries[])
);
this.selectedTime$ = combineLatest([
partitionedSeries$,
this.store.select(getMetricsSelectedTime),
this.store.select(getMetricsXAxisType),
]).pipe(
map(([series, selectedTime, xAxisType]) => {
if (xAxisType !== XAxisType.STEP || !selectedTime) return null;
let minStep = Infinity;
let maxStep = -Infinity;
for (const {points} of series) {
for (const point of points) {
minStep = minStep > point.x ? point.x : minStep;
maxStep = maxStep < point.x ? point.x : maxStep;
}
}
return maybeClipSelectedTime(selectedTime, minStep, maxStep);
})
);
this.chartMetadataMap$ = partitionedSeries$.pipe(
switchMap<
PartitionedSeries[],
Observable<
Array<
PartitionedSeries & {
displayName: string;
alias: ExperimentAlias | null;
}
>
>
>((partitioned) => {
return combineLatest(
partitioned.map((series) => {
return this.getRunDisplayNameAndAlias(series.runId).pipe(
map((displayNameAndAlias) => {
return {...series, ...displayNameAndAlias};
})
);
})
);
}),
combineLatestWith(
this.store.select(getCurrentRouteRunSelection),
this.store.select(getRunColorMap),
this.store.select(getMetricsScalarSmoothing)
),
// When the `fetchRunsSucceeded` action fires, the run selection
// map and the metadata change. To prevent quick fire of changes,
// debounce by a microtask to emit only single change for the runs
// store change.
debounceTime(0),
map(([namedPartitionedSeries, runSelectionMap, colorMap, smoothing]) => {
const metadataMap: ScalarCardSeriesMetadataMap = {};
const shouldSmooth = smoothing > 0;
for (const partitioned of namedPartitionedSeries) {
const {
seriesId,
runId,
displayName,
alias,
partitionIndex,
partitionSize,
} = partitioned;
metadataMap[seriesId] = {
type: SeriesType.ORIGINAL,
id: seriesId,
alias,
displayName:
partitionSize > 1
? `${displayName}: ${partitionIndex}`
: displayName,
visible: Boolean(runSelectionMap && runSelectionMap.get(runId)),
color: colorMap[runId] ?? '#fff',
aux: false,
opacity: 1,
};
}
if (!shouldSmooth) {
return metadataMap;
}
for (const [id, metadata] of Object.entries(metadataMap)) {
const smoothedSeriesId = getSmoothedSeriesId(id);
metadataMap[smoothedSeriesId] = {
...metadata,
id: smoothedSeriesId,
type: SeriesType.DERIVED,
aux: false,
originalSeriesId: id,
};
metadata.aux = true;
metadata.opacity = 0.25;
}
return metadataMap;
}),
startWith({} as ScalarCardSeriesMetadataMap)
);
this.loadState$ = this.store.select(getCardLoadState, this.cardId);
this.tag$ = cardMetadata$.pipe(
map((cardMetadata) => {
return cardMetadata.tag;
})
);
this.title$ = this.tag$.pipe(
map((tag) => {
return getTagDisplayName(tag, this.groupName);
})
);
this.isPinned$ = this.store.select(getCardPinnedState, this.cardId);
}