Skip to content

Commit a69cf8b

Browse files
authored
Displays hparams in scalar card tables (#6737)
## Motivation for features / changes To complete hparams in time series phase 2, namely to show selected hparams in the scalar card tables similarly to how they're currently shown in runs tables. ## Technical description of changes - Enables hparam column adding and filtering in scalar card tables. Triggers the `dashboardHparamColumnAdded` action on table add button click, or on "insert left/right" context menu option click - Shows appropriate hparam data in scalar card table rows - Enables removing columns using scalar card table context menus ## Screenshots of UI changes (or N/A) Synced hparams: ![synced_hparams](https://github.com/tensorflow/tensorboard/assets/736199/28dcb468-8e28-4c51-95c7-cecb60075304) Insert, remove, filter context menus in scalar tables: ![scalar_Context](https://github.com/tensorflow/tensorboard/assets/736199/1945beda-fe33-4f0a-9273-f814f454f19e) ## Detailed steps to verify changes work correctly (as executed by you) - Manually tested scalar table hparam add, sort, filter, remove
1 parent 9937ee0 commit a69cf8b

19 files changed

+840
-80
lines changed

tensorboard/webapp/metrics/actions/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import {
2929
HeaderEditInfo,
3030
HeaderToggleInfo,
3131
HistogramMode,
32-
MinMaxStep,
3332
PluginType,
3433
TooltipSort,
3534
XAxisType,

tensorboard/webapp/metrics/store/metrics_selectors_test.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,30 +1830,30 @@ describe('metrics selectors', () => {
18301830
});
18311831

18321832
expect(selectors.getGroupedHeadersForCard('card1')(state)).toEqual([
1833-
{
1833+
jasmine.objectContaining({
18341834
type: ColumnHeaderType.RUN,
18351835
name: 'run',
18361836
displayName: 'My Run name',
18371837
enabled: false,
1838-
},
1839-
{
1838+
}),
1839+
jasmine.objectContaining({
18401840
type: ColumnHeaderType.HPARAM,
18411841
name: 'conv_layers',
18421842
displayName: 'Conv Layers',
18431843
enabled: true,
1844-
},
1845-
{
1844+
}),
1845+
jasmine.objectContaining({
18461846
type: ColumnHeaderType.HPARAM,
18471847
name: 'conv_kernel_size',
18481848
displayName: 'Conv Kernel Size',
18491849
enabled: true,
1850-
},
1851-
{
1850+
}),
1851+
jasmine.objectContaining({
18521852
type: ColumnHeaderType.COLOR,
18531853
name: 'color',
18541854
displayName: 'Color',
18551855
enabled: true,
1856-
},
1856+
}),
18571857
]);
18581858
});
18591859

@@ -1874,30 +1874,30 @@ describe('metrics selectors', () => {
18741874
});
18751875

18761876
expect(selectors.getGroupedHeadersForCard('card1')(state)).toEqual([
1877-
{
1877+
jasmine.objectContaining({
18781878
type: ColumnHeaderType.RUN,
18791879
name: 'run',
18801880
displayName: 'My Run name',
18811881
enabled: false,
1882-
},
1883-
{
1882+
}),
1883+
jasmine.objectContaining({
18841884
type: ColumnHeaderType.HPARAM,
18851885
name: 'conv_layers',
18861886
displayName: 'Conv Layers',
18871887
enabled: true,
1888-
},
1889-
{
1888+
}),
1889+
jasmine.objectContaining({
18901890
type: ColumnHeaderType.HPARAM,
18911891
name: 'conv_kernel_size',
18921892
displayName: 'Conv Kernel Size',
18931893
enabled: true,
1894-
},
1895-
{
1894+
}),
1895+
jasmine.objectContaining({
18961896
type: ColumnHeaderType.MEAN,
18971897
name: 'mean',
18981898
displayName: 'Mean',
18991899
enabled: true,
1900-
},
1900+
}),
19011901
]);
19021902
});
19031903

@@ -1914,30 +1914,30 @@ describe('metrics selectors', () => {
19141914
});
19151915

19161916
expect(selectors.getGroupedHeadersForCard('card1')(state)).toEqual([
1917-
{
1917+
jasmine.objectContaining({
19181918
type: ColumnHeaderType.RUN,
19191919
name: 'run',
19201920
displayName: 'My Run name',
19211921
enabled: false,
1922-
},
1923-
{
1922+
}),
1923+
jasmine.objectContaining({
19241924
type: ColumnHeaderType.HPARAM,
19251925
name: 'conv_layers',
19261926
displayName: 'Conv Layers',
19271927
enabled: true,
1928-
},
1929-
{
1928+
}),
1929+
jasmine.objectContaining({
19301930
type: ColumnHeaderType.HPARAM,
19311931
name: 'conv_kernel_size',
19321932
displayName: 'Conv Kernel Size',
19331933
enabled: true,
1934-
},
1935-
{
1934+
}),
1935+
jasmine.objectContaining({
19361936
type: ColumnHeaderType.MEAN,
19371937
name: 'mean',
19381938
displayName: 'Mean',
19391939
enabled: true,
1940-
},
1940+
}),
19411941
]);
19421942
});
19431943
});

tensorboard/webapp/metrics/views/card_renderer/BUILD

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,8 @@ tf_ng_module(
335335
"//tensorboard/webapp/angular:expect_angular_material_progress_spinner",
336336
"//tensorboard/webapp/experiments:types",
337337
"//tensorboard/webapp/feature_flag/store",
338+
"//tensorboard/webapp/hparams",
339+
"//tensorboard/webapp/hparams:types",
338340
"//tensorboard/webapp/metrics:types",
339341
"//tensorboard/webapp/metrics/actions",
340342
"//tensorboard/webapp/metrics/data_source",
@@ -456,13 +458,17 @@ tf_ts_library(
456458
"//tensorboard/webapp/angular:expect_angular_platform_browser_animations",
457459
"//tensorboard/webapp/angular:expect_ngrx_store_testing",
458460
"//tensorboard/webapp/experiments:types",
461+
"//tensorboard/webapp/hparams/_redux:hparams_actions",
462+
"//tensorboard/webapp/hparams/_redux:hparams_selectors",
463+
"//tensorboard/webapp/hparams/_redux:types",
459464
"//tensorboard/webapp/metrics:test_lib",
460465
"//tensorboard/webapp/metrics:types",
461466
"//tensorboard/webapp/metrics/actions",
462467
"//tensorboard/webapp/metrics/data_source",
463468
"//tensorboard/webapp/metrics/store",
464469
"//tensorboard/webapp/metrics/store:types",
465470
"//tensorboard/webapp/metrics/views/main_view:common_selectors",
471+
"//tensorboard/webapp/runs/store:selectors",
466472
"//tensorboard/webapp/runs/store:testing",
467473
"//tensorboard/webapp/runs/store:types",
468474
"//tensorboard/webapp/testing:mat_icon",

tensorboard/webapp/metrics/views/card_renderer/scalar_card_component.ng.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,15 @@
201201
[columnContextMenusEnabled]="columnContextMenusEnabled"
202202
[smoothingEnabled]="smoothingEnabled"
203203
[hparamsEnabled]="hparamsEnabled"
204+
[columnFilters]="columnFilters"
205+
[runToHparamMap]="runToHparamMap"
206+
[selectableColumns]="selectableColumns"
204207
(sortDataBy)="sortDataBy($event)"
205208
(editColumnHeaders)="editColumnHeaders.emit($event)"
209+
(addColumn)="addColumn.emit($event)"
210+
(removeColumn)="removeColumn.emit($event)"
211+
(hideColumn)="hideColumn.emit($event)"
212+
(addFilter)="addFilter.emit($event)"
206213
>
207214
</scalar-card-data-table>
208215
</div>

tensorboard/webapp/metrics/views/card_renderer/scalar_card_component.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,12 @@ import {
4444
TooltipDatum,
4545
} from '../../../widgets/line_chart_v2/types';
4646
import {CardState} from '../../store';
47-
import {HeaderEditInfo, TooltipSort, XAxisType} from '../../types';
47+
import {
48+
HeaderEditInfo,
49+
HeaderToggleInfo,
50+
TooltipSort,
51+
XAxisType,
52+
} from '../../types';
4853
import {
4954
MinMaxStep,
5055
ScalarCardDataSeries,
@@ -56,8 +61,13 @@ import {
5661
DataTableMode,
5762
SortingInfo,
5863
SortingOrder,
64+
DiscreteFilter,
65+
IntervalFilter,
66+
FilterAddedEvent,
67+
AddColumnEvent,
5968
} from '../../../widgets/data_table/types';
6069
import {isDatumVisible, TimeSelectionView} from './utils';
70+
import {RunToHparamMap} from '../../../runs/types';
6171

6272
type ScalarTooltipDatum = TooltipDatum<
6373
ScalarCardSeriesMetadata & {
@@ -103,6 +113,9 @@ export class ScalarCardComponent<Downloader> {
103113
@Input() columnHeaders!: ColumnHeader[];
104114
@Input() rangeEnabled!: boolean;
105115
@Input() hparamsEnabled?: boolean;
116+
@Input() columnFilters!: Map<string, DiscreteFilter | IntervalFilter>;
117+
@Input() selectableColumns!: ColumnHeader[];
118+
@Input() runToHparamMap!: RunToHparamMap;
106119

107120
@Output() onFullSizeToggle = new EventEmitter<void>();
108121
@Output() onPinClicked = new EventEmitter<boolean>();
@@ -115,6 +128,9 @@ export class ScalarCardComponent<Downloader> {
115128
@Output() onDataTableSorting = new EventEmitter<SortingInfo>();
116129
@Output() editColumnHeaders = new EventEmitter<HeaderEditInfo>();
117130
@Output() openTableEditMenuToMode = new EventEmitter<DataTableMode>();
131+
@Output() addColumn = new EventEmitter<AddColumnEvent>();
132+
@Output() removeColumn = new EventEmitter<HeaderToggleInfo>();
133+
@Output() addFilter = new EventEmitter<FilterAddedEvent>();
118134

119135
@Output() onLineChartZoom = new EventEmitter<Extent | null>();
120136
@Output() onCardStateChanged = new EventEmitter<Partial<CardState>>();

tensorboard/webapp/metrics/views/card_renderer/scalar_card_container.ts

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import {
3737
} from 'rxjs/operators';
3838
import {State} from '../../../app_state';
3939
import {ExperimentAlias} from '../../../experiments/types';
40+
import {actions as hparamsActions} from '../../../hparams';
4041
import {
4142
getEnableHparamsInTimeSeries,
4243
getForceSvgFeatureFlag,
@@ -58,8 +59,8 @@ import {
5859
getRun,
5960
getRunColorMap,
6061
getCurrentRouteRunSelection,
61-
getColumnHeadersForCard,
62-
getDashboardRunsToHparams,
62+
getGroupedHeadersForCard,
63+
getRunToHparamMap,
6364
} from '../../../selectors';
6465
import {DataLoadState} from '../../../types/data';
6566
import {
@@ -79,6 +80,7 @@ import {
7980
timeSelectionChanged,
8081
metricsSlideoutMenuOpened,
8182
dataTableColumnOrderChanged,
83+
dataTableColumnToggled,
8284
} from '../../actions';
8385
import {PluginType, ScalarStepDatum} from '../../data_source';
8486
import {
@@ -94,8 +96,19 @@ import {
9496
getMetricsXAxisType,
9597
RunToSeries,
9698
} from '../../store';
97-
import {CardId, CardMetadata, HeaderEditInfo, XAxisType} from '../../types';
98-
import {getFilteredRenderableRunsIds} from '../main_view/common_selectors';
99+
import {
100+
CardId,
101+
CardMetadata,
102+
HeaderEditInfo,
103+
HeaderToggleInfo,
104+
XAxisType,
105+
} from '../../types';
106+
import {RunToHparamMap} from '../../../runs/types';
107+
import {
108+
getFilteredRenderableRunsIds,
109+
getCurrentColumnFilters,
110+
getSelectableColumns,
111+
} from '../main_view/common_selectors';
99112
import {CardRenderer} from '../metrics_view_types';
100113
import {getTagDisplayName} from '../utils';
101114
import {DataDownloadDialogContainer} from './data_download_dialog_container';
@@ -112,6 +125,8 @@ import {
112125
ColumnHeader,
113126
DataTableMode,
114127
SortingInfo,
128+
FilterAddedEvent,
129+
AddColumnEvent,
115130
} from '../../../widgets/data_table/types';
116131
import {
117132
maybeClipTimeSelectionView,
@@ -176,6 +191,9 @@ function areSeriesEqual(
176191
[columnHeaders]="columnHeaders$ | async"
177192
[rangeEnabled]="rangeEnabled$ | async"
178193
[hparamsEnabled]="hparamsEnabled$ | async"
194+
[columnFilters]="columnFilters$ | async"
195+
[runToHparamMap]="runToHparamMap$ | async"
196+
[selectableColumns]="selectableColumns$ | async"
179197
(onFullSizeToggle)="onFullSizeToggle()"
180198
(onPinClicked)="pinStateChanged.emit($event)"
181199
observeIntersection
@@ -187,6 +205,9 @@ function areSeriesEqual(
187205
(editColumnHeaders)="editColumnHeaders($event)"
188206
(onCardStateChanged)="onCardStateChanged($event)"
189207
(openTableEditMenuToMode)="openTableEditMenuToMode($event)"
208+
(addColumn)="onAddColumn($event)"
209+
(removeColumn)="onRemoveColumn($event)"
210+
(addFilter)="addHparamFilter($event)"
190211
></scalar-card-component>
191212
`,
192213
styles: [
@@ -226,6 +247,9 @@ export class ScalarCardContainer implements CardRenderer, OnInit, OnDestroy {
226247
cardState$?: Observable<Partial<CardState>>;
227248
rangeEnabled$?: Observable<boolean>;
228249
hparamsEnabled$?: Observable<boolean>;
250+
columnFilters$ = this.store.select(getCurrentColumnFilters);
251+
runToHparamMap$?: Observable<RunToHparamMap>;
252+
selectableColumns$?: Observable<ColumnHeader[]>;
229253

230254
onVisibilityChange({visible}: {visible: boolean}) {
231255
this.isVisible = visible;
@@ -464,7 +488,7 @@ export class ScalarCardContainer implements CardRenderer, OnInit, OnDestroy {
464488
);
465489

466490
this.columnHeaders$ = this.store.select(
467-
getColumnHeadersForCard(this.cardId)
491+
getGroupedHeadersForCard(this.cardId)
468492
);
469493

470494
this.chartMetadataMap$ = partitionedSeries$.pipe(
@@ -593,6 +617,10 @@ export class ScalarCardContainer implements CardRenderer, OnInit, OnDestroy {
593617
);
594618

595619
this.hparamsEnabled$ = this.store.select(getEnableHparamsInTimeSeries);
620+
621+
this.runToHparamMap$ = this.store.select(getRunToHparamMap);
622+
623+
this.selectableColumns$ = this.store.select(getSelectableColumns);
596624
}
597625

598626
ngOnDestroy() {
@@ -679,11 +707,55 @@ export class ScalarCardContainer implements CardRenderer, OnInit, OnDestroy {
679707
);
680708
}
681709

682-
editColumnHeaders(headerEditInfo: HeaderEditInfo) {
683-
this.store.dispatch(dataTableColumnOrderChanged(headerEditInfo));
710+
editColumnHeaders({
711+
source,
712+
destination,
713+
side,
714+
dataTableMode,
715+
}: HeaderEditInfo) {
716+
if (source.type === 'HPARAM') {
717+
this.store.dispatch(
718+
hparamsActions.dashboardHparamColumnOrderChanged({
719+
source,
720+
destination,
721+
side,
722+
})
723+
);
724+
} else {
725+
this.store.dispatch(
726+
dataTableColumnOrderChanged({source, destination, side, dataTableMode})
727+
);
728+
}
684729
}
685730

686731
openTableEditMenuToMode(tableMode: DataTableMode) {
687732
this.store.dispatch(metricsSlideoutMenuOpened({mode: tableMode}));
688733
}
734+
735+
onAddColumn(addColumnEvent: AddColumnEvent) {
736+
this.store.dispatch(
737+
hparamsActions.dashboardHparamColumnAdded(addColumnEvent)
738+
);
739+
}
740+
741+
onRemoveColumn({header, dataTableMode}: HeaderToggleInfo) {
742+
if (header.type === 'HPARAM') {
743+
this.store.dispatch(
744+
hparamsActions.dashboardHparamColumnRemoved({column: header})
745+
);
746+
} else {
747+
this.store.dispatch(
748+
dataTableColumnToggled({header, cardId: this.cardId, dataTableMode})
749+
);
750+
}
751+
}
752+
753+
addHparamFilter(event: FilterAddedEvent) {
754+
this.store.dispatch(
755+
hparamsActions.dashboardHparamFilterAdded({
756+
name: event.name,
757+
filter: event.value,
758+
})
759+
);
760+
}
689761
}

tensorboard/webapp/metrics/views/card_renderer/scalar_card_data_table.ng.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
<tb-data-table
1919
[headers]="columnHeaders"
2020
[sortingInfo]="sortingInfo"
21+
[columnFilters]="columnFilters"
22+
[selectableColumns]="selectableColumns"
2123
(sortDataBy)="sortDataBy.emit($event)"
2224
(orderColumns)="onOrderColumns($event)"
25+
(addColumn)="addColumn.emit($event)"
26+
(removeColumn)="onRemoveColumn($event)"
27+
(addFilter)="addFilter.emit($event)"
2328
>
2429
<ng-container header>
2530
<ng-container *ngFor="let header of getHeaders()">

0 commit comments

Comments
 (0)