Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Limit individual feature importance selection to up to 5 (#1305)
Browse files Browse the repository at this point in the history
* update feature importance string

* limit selection to up to 5

* add group count

* remove message bar, show info icon instead

* update e2e locator

* fix E2E failure on feature importance

* add ariaLabel for expand collapse button

* add renderOnNewLayer props
tongyu-microsoft authored and gaugup committed May 26, 2022
1 parent 832f26a commit 6915216
Showing 4 changed files with 135 additions and 7 deletions.
3 changes: 2 additions & 1 deletion libs/core-ui/src/lib/components/LabelWithCallout.tsx
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@ import { labelWithCalloutStyles } from "./LabelWithCallout.styles";
export interface ILabelWithCalloutProps {
label: string;
calloutTitle: string | undefined;
renderOnNewLayer?: boolean;
type?: "label" | "button";
}
interface ILabelWithCalloutState {
@@ -62,7 +63,7 @@ export class LabelWithCallout extends React.Component<
)}
{this.state.showCallout && (
<FabricCallout
doNotLayer
doNotLayer={!this.props.renderOnNewLayer}
target={`#${id}`}
setInitialFocus
onDismiss={this.toggleCallout}
6 changes: 4 additions & 2 deletions libs/localization/src/lib/en.json
Original file line number Diff line number Diff line change
@@ -1300,8 +1300,10 @@
"CorrectPredictions": "Correct predictions",
"GlobalExplanation": "Aggregate feature importance",
"IncorrectPredictions": "Incorrect predictions",
"IndividualFeature": "Select a datapoint by clicking on a datapoint (or multiple datapoints) in the table to view their local feature importance values (local explanation) and individual conditional expectation (ICE) plot below. For datasets with more than 5000 datapoints, the view is a random subsample to enable easy exploration.",
"LocalExplanation": "Individual feature importance"
"IndividualFeature": "Select a datapoint by clicking on a datapoint (up to 5 datapoints) in the table to view their local feature importance values (local explanation) and individual conditional expectation (ICE) plot below. For datasets with more than 5000 datapoints, the view is a random subsample to enable easy exploration.",
"LocalExplanation": "Individual feature importance",
"SelectionCounter": "{0}/{1} datapoints selected",
"SelectionLimit": "Up to 5 datapoints can be selected at this time."
},
"MainMenu": {
"DashboardSettings": "Dashboard configuration",
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import {
IStyle,
mergeStyleSets,
IProcessedStyleSet,
getTheme
} from "office-ui-fabric-react";

export interface IFeatureImportanceStyles {
chevronButton: IStyle;
header: IStyle;
headerCount: IStyle;
headerTitle: IStyle;
selectionCounter: IStyle;
}

export const individualFeatureImportanceViewStyles: () => IProcessedStyleSet<IFeatureImportanceStyles> =
() => {
const theme = getTheme();
return mergeStyleSets({
chevronButton: {
marginLeft: 48,
paddingTop: 6,
width: 36
},
header: {
margin: `8px 0`,
padding: 8,
// Overlay the sizer bars
position: "relative",
zIndex: 100
},
headerCount: [
"headerCount",
theme.fonts.medium,
{
paddingTop: 4
}
],
headerTitle: [
theme.fonts.medium,
{
paddingTop: 4
}
],
selectionCounter: {
paddingTop: 12
}
});
};
Original file line number Diff line number Diff line change
@@ -11,7 +11,8 @@ import {
FabricStyles,
constructRows,
constructCols,
ModelTypes
ModelTypes,
LabelWithCallout
} from "@responsible-ai/core-ui";
import { IGlobalSeries, LocalImportancePlots } from "@responsible-ai/interpret";
import { localization } from "@responsible-ai/localization";
@@ -34,10 +35,14 @@ import {
TooltipHost,
IColumn,
IGroup,
Text
Text,
IDetailsGroupDividerProps,
Icon
} from "office-ui-fabric-react";
import React from "react";

import { individualFeatureImportanceViewStyles } from "./IndividualFeatureImportanceView.styles";

export interface IIndividualFeatureImportanceProps {
features: string[];
jointDataset: JointDataset;
@@ -59,6 +64,8 @@ export interface IIndividualFeatureImportanceTableState {
export interface IIndividualFeatureImportanceState
extends IIndividualFeatureImportanceTableState {
featureImportances: IGlobalSeries[];
indexToUnselect?: number;
selectedIndices: number[];
sortArray: number[];
sortingSeriesIndex?: number;
}
@@ -70,9 +77,22 @@ export class IndividualFeatureImportanceView extends React.Component<
public static contextType = ModelAssessmentContext;
public context: React.ContextType<typeof ModelAssessmentContext> =
defaultModelAssessmentContext;
private readonly maxSelectable = 5;

private selection: Selection = new Selection({
onSelectionChanged: (): void => {
const c = this.selection.getSelectedCount();
const indices = this.selection.getSelectedIndices();
if (c === this.maxSelectable) {
this.setState({ selectedIndices: indices });
}
if (c > this.maxSelectable) {
for (const index of indices) {
if (!this.state.selectedIndices.includes(index)) {
this.setState({ indexToUnselect: index });
}
}
}
this.updateViewedFeatureImportances();
}
});
@@ -84,6 +104,8 @@ export class IndividualFeatureImportanceView extends React.Component<

this.state = {
featureImportances: [],
indexToUnselect: undefined,
selectedIndices: [],
sortArray: [],
...tableState
};
@@ -95,6 +117,10 @@ export class IndividualFeatureImportanceView extends React.Component<
if (this.props.selectedCohort !== prevProps.selectedCohort) {
this.setState(this.updateItems());
}
if (this.state.indexToUnselect) {
this.selection.toggleIndexSelected(this.state.indexToUnselect);
this.setState({ indexToUnselect: undefined });
}
}

public render(): React.ReactNode {
@@ -132,6 +158,7 @@ export class IndividualFeatureImportanceView extends React.Component<
text: meta.abbridgedLabel
};
});
const classNames = individualFeatureImportanceViewStyles();

return (
<Stack tokens={{ padding: "l1" }}>
@@ -140,6 +167,22 @@ export class IndividualFeatureImportanceView extends React.Component<
{localization.ModelAssessment.FeatureImportances.IndividualFeature}
</Text>
</Stack.Item>
<Stack.Item className={classNames.selectionCounter}>
<LabelWithCallout
label={localization.formatString(
localization.ModelAssessment.FeatureImportances.SelectionCounter,
this.selection.count,
this.maxSelectable
)}
calloutTitle={undefined}
renderOnNewLayer
type="label"
>
<Text block>
{localization.ModelAssessment.FeatureImportances.SelectionLimit}
</Text>
</LabelWithCallout>
</Stack.Item>
<Stack.Item className="tabularDataView">
<div style={{ height: "500px", position: "relative" }}>
<Fabric>
@@ -155,10 +198,12 @@ export class IndividualFeatureImportanceView extends React.Component<
onRenderDetailsHeader={this.onRenderDetailsHeader}
selectionPreservedOnEmptyClick
ariaLabelForSelectionColumn="Toggle selection"
ariaLabelForSelectAllCheckbox="Toggle selection for all items"
checkButtonAriaLabel="Row checkbox"
// checkButtonGroupAriaLabel="Group checkbox"
groupProps={{ showEmptyGroups: true }}
groupProps={{
onRenderHeader: this._onRenderGroupHeader,
showEmptyGroups: true
}}
selectionMode={SelectionMode.multiple}
selection={this.selection}
/>
@@ -329,4 +374,32 @@ export class IndividualFeatureImportanceView extends React.Component<
</div>
);
};

private _onRenderGroupHeader = (props?: IDetailsGroupDividerProps) => {
const classNames = individualFeatureImportanceViewStyles();
const iconName = props?.group?.isCollapsed
? "ChevronRightMed"
: "ChevronDownMed";
return (
<Stack className={classNames.header} horizontal>
<Icon
ariaLabel="expand collapse group"
className={classNames.chevronButton}
iconName={iconName}
onClick={this._onToggleCollapse(props)}
/>
<span className={classNames.headerTitle}>{props?.group!.name}</span>
&nbsp;
<span className={classNames.headerCount}>
{`(${props?.group!.count})`}
</span>
</Stack>
);
};

private _onToggleCollapse = (props?: IDetailsGroupDividerProps) => {
return () => {
props!.onToggleCollapse!(props!.group!);
};
};
}

0 comments on commit 6915216

Please sign in to comment.