Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libraries/ml/src/MCL/clustering-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {KnownMetrics} from '../typed-metrics';
import {DistanceAggregationMethod} from '../distance-matrix/types';
import {PreprocessFunctionReturnType} from '../functionEditors/dimensionality-reduction-editor';
import {Options} from '@datagrok-libraries/utils/src/type-declarations';
import {findTableView} from '../multi-column-dimensionality-reduction/reduce-dimensionality';
import * as rxjs from 'rxjs';


Expand Down Expand Up @@ -38,7 +39,7 @@ export async function markovCluster(
let tv: DG.TableView | null = null;
let sc: DG.ScatterPlotViewer | undefined = scp;
if (!sc) {
tv = grok.shell.tableView(df.name) ?? grok.shell.addTableView(df);
tv = findTableView(df) ?? grok.shell.addTableView(df);
sc = tv.scatterPlot({...scatterPlotProps, title: 'MCL'});
}

Expand Down
2 changes: 1 addition & 1 deletion libraries/ml/src/MCL/mcl-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export class MCLEditor extends MultiColumnDimReductionEditor {

public get params() {
return {
table: this.tableInput.value!,
table: this.tableInput.value ?? this.currentTable!,
columns: this.columnsInput.value!,
methodName: this.methodInput.value!,
preprocessingFunctions: this.columnOptEditors.map((it) => it.preProcessingFunction),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ export type MultiColDimReductionParams = {
export class MultiColumnDimReductionEditor {
editorSettings: DimReductionEditorOptions = {};
tableInput!: DG.InputBase<DG.DataFrame | null>;
// last resolved table: tableInput resolves value by name and loses the selection if the table is renamed
currentTable: DG.DataFrame | null = null;
columnsInput!: DG.InputBase<DG.Column[]>;
columnsInputRoot!: HTMLElement;
columnOptEditors: DimReductionColumnEditor[] = [];
Expand Down Expand Up @@ -174,6 +176,7 @@ export class MultiColumnDimReductionEditor {
const table = this.tableInput.value;
if (!table)
return;
this.currentTable = table;
ui.empty(this.columnOptEditorsRoot);

this.columnFunctionsMap = {};
Expand Down Expand Up @@ -332,7 +335,7 @@ export class MultiColumnDimReductionEditor {
const usePlaceholders = this.columnOptEditors.length === 0 && columns && columns.length > 0;
const n = columns?.length ?? 0;
return {
table: this.tableInput.value!,
table: this.tableInput.value ?? this.currentTable!,
columns,
methodName: this.methodInput.value!,
preprocessingFunctions: usePlaceholders ?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export function getEmbeddingViewerName(columns: DG.Column[], method: DimReductio
return `${method} (${colNames})`;
}

export function findTableView(table: DG.DataFrame): DG.TableView | undefined {
// match by stable .dart identity: tableViews returns a fresh toJs wrapper per access, and table names are nullable
return Array.from(grok.shell.tableViews).find((v) => v.dataFrame?.dart === table.dart);
}

export async function multiColReduceDimensionality(table: DG.DataFrame, columns: DG.Column[],
method: DimReductionMethods, metrics: KnownMetrics[], weights: number[],
preprocessingFunctions: (DG.Func | null | undefined)[],
Expand All @@ -53,7 +58,7 @@ export async function multiColReduceDimensionality(table: DG.DataFrame, columns:
'must have the same length');
}

const tv = plotEmbeddings ? (uiOptions.tableView ?? grok.shell.tableView(table.name) ?? grok.shell.addTableView(table)) : null;
const tv = plotEmbeddings ? (uiOptions.tableView ?? findTableView(table) ?? grok.shell.addTableView(table)) : null;

const doReduce = async () => {
const pg = DG.TaskBarProgressIndicator.create(
Expand Down
4 changes: 4 additions & 0 deletions packages/EDA/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# EDA changelog

## v.next

* 1687: Resolve target table views by DataFrame identity instead of nullable name in dim reduction and MCL flows

## 1.9.0 (2026-08-26)

* Added SVM (classification and regression)
Expand Down
6 changes: 3 additions & 3 deletions packages/EDA/src/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {getDbscanWorker} from '@datagrok-libraries/math';
import {DistanceAggregationMethod, DistanceAggregationMethods} from '@datagrok-libraries/ml/src/distance-matrix/types';
import {MultiColumnDimReductionEditor} from
'@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/multi-column-dim-reduction-editor';
import {multiColReduceDimensionality} from
import {multiColReduceDimensionality, findTableView} from
'@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/reduce-dimensionality';
import {KnownMetrics} from '@datagrok-libraries/ml/src/typed-metrics';
import {DimReductionMethods} from '@datagrok-libraries/ml/src/multi-column-dimensionality-reduction/types';
Expand Down Expand Up @@ -140,7 +140,7 @@ export class PackageFunctions {
const resCol = await PackageFunctions.dbScan(df, col1, col2, epsilon, minimumPoints);
df.changeColumnType(resCol, 'string');
const colNames = [col1.name, col2.name];
const tv = grok.shell.tableView(df.name);
const tv = findTableView(df);
if (!tv)
return;
for (const v of tv.viewers) {
Expand Down Expand Up @@ -278,7 +278,7 @@ export class PackageFunctions {
@grok.decorators.param({'type': 'bool', 'options': {'initialValue': 'false', 'description': 'Run the computation on the GPU via WebGPU when available.'}}) useWebGPU: boolean = false,
@grok.decorators.param({'type': 'double', 'options': {'initialValue': '2', 'description': 'Inflation factor controlling cluster granularity: higher values yield more, smaller clusters.'}}) inflate: number = 0,
@grok.decorators.param({'type': 'int', 'options': {'initialValue': '5', 'description': 'Clusters smaller than this are merged into noise.'}}) minClusterSize: number = 5): Promise<MCLViewer> {
const tv = grok.shell.tableView(df.name) ?? grok.shell.addTableView(df);
const tv = findTableView(df) ?? grok.shell.addTableView(df);
const serializedOptions: string = JSON.stringify({
cols: cols.map((col) => col.name),
metrics: metrics,
Expand Down
5 changes: 5 additions & 0 deletions packages/Widgets/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Widgets changelog

## v.next

* 1862: Implement the isFiltering contract in RadioButtonFilter, make the no-selection state non-throwing, and give each filter card a unique radio-group name
23 changes: 14 additions & 9 deletions packages/Widgets/src/filters/radio-button-filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,25 @@ import '../styles/widgets.css';
* 2. Call dataFrame.rows.requestFilter when filtering criteria changes.
* */
export class RadioButtonFilter extends DG.Filter {
groupId = 0;
buttonId = 0;
static groupId = 0;
static buttonId = 0;

constructor() {
super();
this.root = ui.divV([], 'd4-radio-button-filter');
this.subs = [];
}

get isFiltering() {return true;}
get isFiltering() {return super.isFiltering && this.checkedCategoryId != null;}

get filterSummary() {return this.column!.getCategory(this.checkedCategoryId);}
get filterSummary() {
const categoryIdx = this.checkedCategoryId;
return categoryIdx == null ? '' : this.column!.getCategory(categoryIdx);
}

get checkedCategoryId() {
get checkedCategoryId(): number | null {
const checkedInput = this.root.querySelector('input[type=\'radio\']:checked');
return parseInt(checkedInput!.getAttribute('data-category-id')!);
return checkedInput == null ? null : parseInt(checkedInput.getAttribute('data-category-id')!);
}

attach(dataFrame: DG.DataFrame) {
Expand All @@ -51,8 +54,10 @@ export class RadioButtonFilter extends DG.Filter {
}

applyFilter() {
const indexes = this.column!.getRawData();
const categoryIdx = this.checkedCategoryId;
if (categoryIdx == null)
return;
const indexes = this.column!.getRawData();
const filter = this.dataFrame!.filter;
const rowCount = this.dataFrame!.rowCount;

Expand All @@ -63,12 +68,12 @@ export class RadioButtonFilter extends DG.Filter {
}

render() {
const name = `radio_${this.groupId++}`;
const name = `radio_${RadioButtonFilter.groupId++}`;
$(this.root).empty();

for (let i = 0; i < Math.min(20, this.column!.categories.length); i++) {
const category = this.column!.categories[i];
const id = `rb_${this.buttonId++}`;
const id = `rb_${RadioButtonFilter.buttonId++}`;
const radioButon = $(`<input type="radio" id="${id}" name="${name}" data-category-id="${i}">`)
.on('change', () => this.dataFrame!.rows.requestFilter());
const label = $(`<label for="${id}">${category}</label>`);
Expand Down
Loading