@@ -82,6 +82,14 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
82
82
// Custom projection.
83
83
@property ( { type : String } )
84
84
customSelectedSearchByMetadataOption : string ;
85
+ @property ( { type : String } )
86
+ superviseInput : string ;
87
+ @property ( { type : String } )
88
+ superviseInputLabel : string = 'Ignored label' ;
89
+ @property ( { type : String } )
90
+ superviseColumn : string ;
91
+ @property ( { type : Boolean } )
92
+ showSuperviseSettings : boolean = false ;
85
93
86
94
private projector : any ; // Projector; type omitted b/c LegacyElement
87
95
@@ -109,6 +117,8 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
109
117
private perplexitySlider : HTMLInputElement ;
110
118
private learningRateInput : HTMLInputElement ;
111
119
private superviseFactorInput : HTMLInputElement ;
120
+ private superviseInputSelected : string ;
121
+ private metadataFields : string [ ] ;
112
122
private zDropdown : HTMLElement ;
113
123
private iterationLabelTsne : HTMLElement ;
114
124
private runUmapButton : HTMLButtonElement ;
@@ -144,6 +154,7 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
144
154
) as HTMLInputElement ;
145
155
this . iterationLabelTsne = this . $$ ( '.run-tsne-iter' ) as HTMLElement ;
146
156
this . runUmapButton = this . $$ ( '#run-umap' ) as HTMLButtonElement ;
157
+ this . superviseInputSelected = '' ;
147
158
}
148
159
disablePolymerChangesTriggerReprojection ( ) {
149
160
this . polymerChangesTriggerReprojection = false ;
@@ -258,6 +269,9 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
258
269
if ( this . learningRateInput ) {
259
270
this . learningRateInput . value = bookmark . tSNELearningRate . toString ( ) ;
260
271
}
272
+ this . superviseFactor = bookmark . tSNESuperviseFactor ;
273
+ this . superviseColumn = bookmark . tSNESuperviseColumn ;
274
+ this . superviseInput = bookmark . tSNESuperviseInput || '' ;
261
275
this . tSNEis3d = bookmark . tSNEis3d ;
262
276
// UMAP
263
277
this . umapIs3d = bookmark . umapIs3d ;
@@ -293,6 +307,9 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
293
307
this . setZDropdownEnabled ( this . pcaIs3d ) ;
294
308
this . updateTSNEPerplexityFromSliderChange ( ) ;
295
309
this . updateTSNELearningRateFromUIChange ( ) ;
310
+ this . updateTSNESuperviseFactorFromUIChange ( ) ;
311
+ this . setSupervision ( this . superviseInput , this . superviseColumn ) ;
312
+ this . superviseInputChange ( ) ;
296
313
if ( this . iterationLabelTsne ) {
297
314
this . iterationLabelTsne . innerText = bookmark . tSNEIteration . toString ( ) ;
298
315
}
@@ -315,6 +332,9 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
315
332
if ( this . learningRateInput != null ) {
316
333
bookmark . tSNELearningRate = + this . learningRateInput . value ;
317
334
}
335
+ bookmark . tSNESuperviseFactor = this . superviseFactor ;
336
+ bookmark . tSNESuperviseInput = this . superviseInput ;
337
+ bookmark . tSNESuperviseColumn = this . superviseColumn ;
318
338
bookmark . tSNEis3d = this . tSNEis3d ;
319
339
// UMAP
320
340
bookmark . umapIs3d = this . umapIs3d ;
@@ -401,6 +421,25 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
401
421
} ) ! ;
402
422
this . customSelectedSearchByMetadataOption =
403
423
this . searchByMetadataOptions [ Math . max ( 0 , searchByMetadataIndex ) ] ;
424
+ let labelIndex = - 1 ;
425
+ if ( spriteAndMetadata . stats ) {
426
+ this . metadataFields = spriteAndMetadata . stats . map ( ( stats , i ) => {
427
+ if ( ! stats . isNumeric && labelIndex === - 1 ) {
428
+ labelIndex = i ;
429
+ }
430
+ return stats . name ;
431
+ } ) ;
432
+ }
433
+ if (
434
+ this . superviseColumn == null ||
435
+ this . metadataFields . filter ( ( name ) => name === this . superviseColumn )
436
+ . length === 0
437
+ ) {
438
+ // Make the default supervise class the first non-numeric column.
439
+ this . superviseColumn = this . metadataFields [ Math . max ( 0 , labelIndex ) ] ;
440
+ this . superviseInput = '' ;
441
+ }
442
+ this . superviseInputChange ( ) ;
404
443
}
405
444
public showTab ( id : ProjectionType ) {
406
445
this . currentProjection = id ;
@@ -455,6 +494,59 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
455
494
this . reprojectCustom ( ) ;
456
495
}
457
496
}
497
+ private superviseInputTyping ( ) {
498
+ let value = this . superviseInput . trim ( ) ;
499
+ if ( value == null || value . trim ( ) === '' ) {
500
+ if ( this . superviseInputSelected === '' ) {
501
+ this . superviseInputLabel = 'No ignored label' ;
502
+ } else {
503
+ this . superviseInputLabel = `Supervising without '${ this . superviseInputSelected } '` ;
504
+ }
505
+ return ;
506
+ }
507
+ if ( this . projector && this . projector . dataSet ) {
508
+ let numMatches = this . projector . dataSet . points . filter (
509
+ ( p ) => p . metadata [ this . superviseColumn ] . toString ( ) . trim ( ) === value
510
+ ) . length ;
511
+ if ( numMatches === 0 ) {
512
+ this . superviseInputLabel = 'Label not found' ;
513
+ } else {
514
+ if ( this . projector . dataSet . superviseInput != value ) {
515
+ this . superviseInputLabel = `Supervise without '${ value } ' [${ numMatches } points]` ;
516
+ }
517
+ }
518
+ }
519
+ }
520
+ private superviseInputChange ( ) {
521
+ let value = this . superviseInput . trim ( ) ;
522
+ if ( value == null || value . trim ( ) === '' ) {
523
+ this . superviseInputSelected = '' ;
524
+ this . superviseInputLabel = 'No ignored label' ;
525
+ this . setSupervision ( this . superviseColumn , '' ) ;
526
+ return ;
527
+ }
528
+ if ( this . projector && this . projector . dataSet ) {
529
+ let numMatches = this . projector . dataSet . points . filter (
530
+ ( p ) => p . metadata [ this . superviseColumn ] . toString ( ) . trim ( ) === value
531
+ ) . length ;
532
+ if ( numMatches === 0 ) {
533
+ this . superviseInputLabel = `Supervising without '${ this . superviseInputSelected } '` ;
534
+ } else {
535
+ this . superviseInputSelected = value ;
536
+ this . superviseInputLabel = `Supervising without '${ value } ' [${ numMatches } points]` ;
537
+ this . setSupervision ( this . superviseColumn , value ) ;
538
+ }
539
+ }
540
+ }
541
+ private superviseColumnChanged ( ) {
542
+ this . superviseInput = '' ;
543
+ this . superviseInputChange ( ) ;
544
+ }
545
+ private setSupervision ( superviseColumn : string , superviseInput : string ) {
546
+ if ( this . projector && this . projector . dataSet ) {
547
+ this . projector . dataSet . setSupervision ( superviseColumn , superviseInput ) ;
548
+ }
549
+ }
458
550
private showTSNE ( ) {
459
551
const dataSet = this . dataSet ;
460
552
if ( dataSet == null ) {
@@ -480,7 +572,6 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
480
572
}
481
573
}
482
574
private runTSNE ( ) {
483
- let projectionChangeNotified = false ;
484
575
this . runTsneButton . innerText = 'Stop' ;
485
576
this . runTsneButton . disabled = true ;
486
577
this . pauseTsneButton . innerText = 'Pause' ;
@@ -496,17 +587,12 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
496
587
this . pauseTsneButton . disabled = false ;
497
588
this . iterationLabelTsne . innerText = '' + iteration ;
498
589
this . projector . notifyProjectionPositionsUpdated ( ) ;
499
- if ( ! projectionChangeNotified && this . dataSet . projections [ 'tsne' ] ) {
500
- this . projector . onProjectionChanged ( ) ;
501
- projectionChangeNotified = true ;
502
- }
503
590
} else {
504
591
this . runTsneButton . innerText = 'Re-run' ;
505
592
this . runTsneButton . disabled = false ;
506
593
this . pauseTsneButton . innerText = 'Pause' ;
507
594
this . pauseTsneButton . disabled = true ;
508
595
this . perturbTsneButton . disabled = true ;
509
- this . projector . onProjectionChanged ( ) ;
510
596
}
511
597
}
512
598
) ;
@@ -536,22 +622,16 @@ class ProjectionsPanel extends LegacyElementMixin(PolymerElement) {
536
622
}
537
623
}
538
624
private runUmap ( ) {
539
- let projectionChangeNotified = false ;
540
625
this . runUmapButton . disabled = true ;
541
626
const nComponents = this . umapIs3d ? 3 : 2 ;
542
627
const nNeighbors = this . umapNeighbors ;
543
628
this . dataSet . projectUmap ( nComponents , nNeighbors , ( iteration : number ) => {
544
629
if ( iteration != null ) {
545
630
this . runUmapButton . disabled = false ;
546
631
this . projector . notifyProjectionPositionsUpdated ( ) ;
547
- if ( ! projectionChangeNotified && this . dataSet . projections [ 'umap' ] ) {
548
- this . projector . onProjectionChanged ( ) ;
549
- projectionChangeNotified = true ;
550
- }
551
632
} else {
552
633
this . runUmapButton . innerText = 'Re-run' ;
553
634
this . runUmapButton . disabled = false ;
554
- this . projector . onProjectionChanged ( ) ;
555
635
}
556
636
} ) ;
557
637
}
0 commit comments