Skip to content

Commit 7855aab

Browse files
authored
Apply selected experiment colors to custom plots (#4647)
1 parent f054f40 commit 7855aab

File tree

6 files changed

+156
-18
lines changed

6 files changed

+156
-18
lines changed

extension/src/plots/model/collect.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,31 @@ describe('collectCustomPlots', () => {
4141
it('should return the expected data from the test fixture', () => {
4242
const expectedOutput: CustomPlotData[] = customPlotsFixture.plots
4343
const data = collectCustomPlots({
44+
colorScale: {
45+
domain: ['main', 'exp-e7a67', 'test-branch', 'exp-83425'],
46+
range: ['#13adc7', '#f46837', '#48bb78', '#4299e1']
47+
},
4448
experiments: experimentsWithCommits,
4549
height: DEFAULT_PLOT_HEIGHT,
4650
nbItemsPerRow: DEFAULT_NB_ITEMS_PER_ROW,
4751
plotsOrderValues: customPlotsOrderFixture
4852
})
4953
expect(data).toStrictEqual(expectedOutput)
5054
})
55+
56+
it('should return selected revisions last', () => {
57+
const data = collectCustomPlots({
58+
colorScale: {
59+
domain: ['main'],
60+
range: ['#13adc7']
61+
},
62+
experiments: experimentsWithCommits,
63+
height: DEFAULT_PLOT_HEIGHT,
64+
nbItemsPerRow: DEFAULT_NB_ITEMS_PER_ROW,
65+
plotsOrderValues: customPlotsOrderFixture
66+
})
67+
expect(data[0].values.slice(-1)[0].id).toStrictEqual('main')
68+
})
5169
})
5270

5371
describe('collectData', () => {

extension/src/plots/model/collect.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
getParent,
4343
getPathArray
4444
} from '../../fileSystem/util'
45+
import { Color } from '../../experiments/model/status/colors'
4546

4647
export const getCustomPlotId = (metric: string, param: string) =>
4748
`custom-${metric}-${param}`
@@ -52,21 +53,28 @@ const getValueFromColumn = (path: string, experiment: Experiment) =>
5253
const getValues = (
5354
experiments: Experiment[],
5455
metricPath: string,
55-
paramPath: string
56+
paramPath: string,
57+
renderLastIds: Set<string> = new Set<string>()
5658
): CustomPlotValues => {
5759
const values: CustomPlotValues = []
5860

5961
for (const experiment of experiments) {
6062
const metricValue = getValueFromColumn(metricPath, experiment)
6163
const paramValue = getValueFromColumn(paramPath, experiment)
6264

63-
if (metricValue !== undefined && paramValue !== undefined) {
64-
values.push({
65-
id: experiment.id,
66-
metric: metricValue,
67-
param: paramValue
68-
})
65+
if (metricValue === undefined || paramValue === undefined) {
66+
continue
6967
}
68+
69+
const value = {
70+
id: experiment.id,
71+
metric: metricValue,
72+
param: paramValue
73+
}
74+
75+
renderLastIds.has(experiment.id)
76+
? values.push(value)
77+
: values.unshift(value)
7078
}
7179

7280
return values
@@ -76,13 +84,15 @@ const getCustomPlotData = (
7684
orderValue: CustomPlotsOrderValue,
7785
experiments: Experiment[],
7886
height: number,
79-
nbItemsPerRow: number
87+
nbItemsPerRow: number,
88+
completeColorScale: ColorScale,
89+
renderLastIds: Set<string>
8090
): CustomPlotData => {
8191
const { metric, param } = orderValue
8292
const metricPath = getFullValuePath(ColumnType.METRICS, metric)
8393
const paramPath = getFullValuePath(ColumnType.PARAMS, param)
8494

85-
const values = getValues(experiments, metricPath, paramPath)
95+
const values = getValues(experiments, metricPath, paramPath, renderLastIds)
8696

8797
const [{ param: paramVal, metric: metricVal }] = values
8898
const yTitle = truncateVerticalTitle(metric, nbItemsPerRow, height) as string
@@ -92,7 +102,8 @@ const getCustomPlotData = (
92102
metric,
93103
param,
94104
typeof metricVal,
95-
typeof paramVal
105+
typeof paramVal,
106+
completeColorScale
96107
)
97108

98109
return {
@@ -104,21 +115,54 @@ const getCustomPlotData = (
104115
} as CustomPlotData
105116
}
106117

118+
const fillColorScale = (
119+
colorScale: ColorScale | undefined,
120+
experiments: Experiment[]
121+
) => {
122+
const completeColorScale = {
123+
domain: [...(colorScale?.domain || [])],
124+
range: [...(colorScale?.range || [])]
125+
}
126+
127+
for (const experiment of experiments) {
128+
const { id } = experiment
129+
if (completeColorScale.domain.includes(id)) {
130+
continue
131+
}
132+
completeColorScale.domain.push(id)
133+
completeColorScale.range.push('#4c78a8' as Color)
134+
}
135+
return completeColorScale
136+
}
137+
107138
export const collectCustomPlots = ({
139+
colorScale,
108140
plotsOrderValues,
109141
experiments,
110142
height,
111143
nbItemsPerRow
112144
}: {
145+
colorScale: ColorScale | undefined
113146
plotsOrderValues: CustomPlotsOrderValue[]
114147
experiments: Experiment[]
115148
height: number
116149
nbItemsPerRow: number
117150
}): CustomPlotData[] => {
118151
const plots = []
119152

153+
const completeColorScale = fillColorScale(colorScale, experiments)
154+
120155
for (const value of plotsOrderValues) {
121-
plots.push(getCustomPlotData(value, experiments, height, nbItemsPerRow))
156+
plots.push(
157+
getCustomPlotData(
158+
value,
159+
experiments,
160+
height,
161+
nbItemsPerRow,
162+
completeColorScale,
163+
new Set(colorScale?.domain)
164+
)
165+
)
122166
}
123167

124168
return plots

extension/src/plots/model/custom.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { VisualizationSpec } from 'react-vega'
22
import { getCustomPlotId } from './collect'
33
import { Column, ColumnType } from '../../experiments/webview/contract'
44
import { FILE_SEPARATOR } from '../../experiments/columns/paths'
5+
import { ColorScale } from '../webview/contract'
56

67
export type CustomPlotsOrderValue = {
78
metric: string
@@ -77,12 +78,14 @@ export const createSpec = (
7778
metric: string,
7879
param: string,
7980
metricType: string,
80-
paramType: string
81+
paramType: string,
82+
colorScale: ColorScale
8183
) =>
8284
({
8385
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
8486
data: { name: 'values' },
8587
encoding: {
88+
color: { field: 'id', legend: null, scale: colorScale },
8689
x: {
8790
axis: {
8891
labelLimit: 75,

extension/src/plots/model/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,14 @@ export class PlotsModel extends ModelWithPersistence {
166166
const nbItemsPerRow = this.getNbItemsPerRowOrWidth(
167167
PlotsSection.CUSTOM_PLOTS
168168
)
169+
const colorScale = getColorScale(
170+
this.getSelectedRevisionDetails().filter(
171+
({ id }) => id !== EXPERIMENT_WORKSPACE_ID
172+
)
173+
)
169174
const plotsOrderValues = this.getCustomPlotsOrder()
170175
const plots: CustomPlotData[] = collectCustomPlots({
176+
colorScale,
171177
experiments,
172178
height,
173179
nbItemsPerRow,

extension/src/test/fixtures/expShow/base/customPlots.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,28 @@ const data: CustomPlotsData = {
103103
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
104104
data: { name: 'values' },
105105
encoding: {
106+
color: {
107+
field: 'id',
108+
legend: null,
109+
scale: {
110+
domain: [
111+
'main',
112+
'exp-e7a67',
113+
'test-branch',
114+
'exp-83425',
115+
'fe2919b',
116+
'7df876c'
117+
],
118+
range: [
119+
'#13adc7',
120+
'#f46837',
121+
'#48bb78',
122+
'#4299e1',
123+
'#4c78a8',
124+
'#4c78a8'
125+
]
126+
}
127+
},
106128
x: {
107129
axis: {
108130
labelLimit: 75,
@@ -157,13 +179,13 @@ const data: CustomPlotsData = {
157179
width: 'container'
158180
},
159181
values: [
160-
{ id: 'main', metric: 2.048856019973755, param: 'logs.csv' },
182+
{ id: '7df876c', metric: 2.048856019973755, param: 'logs.csv' },
161183
{
162184
id: 'fe2919b',
163185
metric: 2.048856019973755,
164186
param: 'logs.csv'
165187
},
166-
{ id: '7df876c', metric: 2.048856019973755, param: 'logs.csv' },
188+
{ id: 'main', metric: 2.048856019973755, param: 'logs.csv' },
167189
{ id: 'exp-e7a67', metric: 2.0205044746398926, param: 'logs.csv' },
168190
{ id: 'test-branch', metric: 1.9293040037155151, param: 'logs.csv' },
169191
{
@@ -179,7 +201,7 @@ const data: CustomPlotsData = {
179201
param: 'params.yaml:epochs',
180202
values: [
181203
{
182-
id: 'main',
204+
id: '7df876c',
183205
metric: 0.3484833240509033,
184206
param: 5
185207
},
@@ -189,7 +211,7 @@ const data: CustomPlotsData = {
189211
param: 5
190212
},
191213
{
192-
id: '7df876c',
214+
id: 'main',
193215
metric: 0.3484833240509033,
194216
param: 5
195217
},
@@ -213,6 +235,28 @@ const data: CustomPlotsData = {
213235
$schema: 'https://vega.github.io/schema/vega-lite/v5.json',
214236
data: { name: 'values' },
215237
encoding: {
238+
color: {
239+
field: 'id',
240+
legend: null,
241+
scale: {
242+
domain: [
243+
'main',
244+
'exp-e7a67',
245+
'test-branch',
246+
'exp-83425',
247+
'fe2919b',
248+
'7df876c'
249+
],
250+
range: [
251+
'#13adc7',
252+
'#f46837',
253+
'#48bb78',
254+
'#4299e1',
255+
'#4c78a8',
256+
'#4c78a8'
257+
]
258+
}
259+
},
216260
x: {
217261
axis: {
218262
labelLimit: 75,

extension/src/test/suite/plots/index.test.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,12 +490,23 @@ suite('Plots Test Suite', () => {
490490
type: MessageFromWebviewType.EXPORT_PLOT_DATA_AS_JSON
491491
})
492492

493+
const expectedOrder = [
494+
'exp-83425',
495+
'test-branch',
496+
'exp-e7a67',
497+
'7df876c',
498+
'fe2919b',
499+
'main'
500+
]
501+
493502
await openFileEvent
494503

495504
expect(mockWriteJson).to.be.calledOnce
496505
expect(mockWriteJson).to.be.calledWithExactly(
497506
exportFile.path,
498-
customPlot.values,
507+
[...customPlot.values].sort(
508+
(a, b) => expectedOrder.indexOf(a.id) - expectedOrder.indexOf(b.id)
509+
),
499510
true
500511
)
501512
expect(mockOpenFile).to.calledWithExactly(exportFile.path)
@@ -579,11 +590,23 @@ suite('Plots Test Suite', () => {
579590

580591
await openFileEvent
581592

593+
const expectedOrder = [
594+
'exp-83425',
595+
'test-branch',
596+
'exp-e7a67',
597+
'7df876c',
598+
'fe2919b',
599+
'main'
600+
]
601+
582602
expect(mockWriteTsv).to.be.calledOnce
583603
expect(mockWriteTsv).to.be.calledWithExactly(
584604
exportFile.path,
585-
customPlot.values
605+
[...customPlot.values].sort(
606+
(a, b) => expectedOrder.indexOf(a.id) - expectedOrder.indexOf(b.id)
607+
)
586608
)
609+
587610
expect(mockOpenFile).to.calledWithExactly(exportFile.path)
588611
expect(mockSendTelemetryEvent).to.be.calledOnce
589612
expect(mockSendTelemetryEvent).to.be.calledWithExactly(

0 commit comments

Comments
 (0)