From a8e220f6f8059eedddef46d6d0a7fe6f8f254c8a Mon Sep 17 00:00:00 2001 From: Gaurav Gupta <47334368+gaugup@users.noreply.github.com> Date: Fri, 4 Mar 2022 07:22:29 -0800 Subject: [PATCH 1/5] Add supported models and data types to README.md responsibleai (#1259) Signed-off-by: Gaurav Gupta --- responsibleai/README.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/responsibleai/README.md b/responsibleai/README.md index 7b944fa0cd..0d964244b5 100644 --- a/responsibleai/README.md +++ b/responsibleai/README.md @@ -11,5 +11,19 @@ Highlights of the package include: - `error_analysis.add()` runs error analysis - `causal.add()` runs causal analysis +### Supported scenarios, models and datasets + +`responsibleai` supports computation of Responsible AI insights for `scikit-learn` models that are trained on `pandas.DataFrame`. The `responsibleai` accept both models and pipelines as input as long as the model or pipeline implements a `predict` or `predict_proba` function that conforms to the `scikit-learn` convention. If not compatible, you can wrap your model's prediction function into a wrapper class that transforms the output into the format that is supported (`predict` or `predict_proba` of `scikit-learn`), and pass that wrapper class to modules in `responsibleai`. + +Currently, we support datasets having numerical and categorical features. The following table provides the scenarios supported for each of the four responsible AI insights:- + +| RAI insight | Binary classification | Multi-class classification | Multilabel classification | Regression | Timeseries forecasting | Categorical features | Text features | Image Features | Recommender Systems | Reinforcement Learning | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | -- | +| Explainability | Yes | Yes | No | Yes | No | Yes | No | No | No | No | +| Error Analysis | Yes | Yes | No | Yes | No | Yes | No | No | No | No | +| Causal Analysis | Yes | No | No | Yes | No | Yes (max 5 features due to expensiveness) | No | No | No | No | +| Counterfactual | Yes | Yes | No | Yes | No | Yes | No | No | No | No | + + The source code can be found here: -https://github.com/microsoft/responsible-ai-widgets +https://github.com/microsoft/responsible-ai-toolbox/tree/main/responsibleai From 1159e9d0cfb994c1995fbf527a93b99bd943c64d Mon Sep 17 00:00:00 2001 From: Ilya Matiach Date: Fri, 4 Mar 2022 10:29:19 -0500 Subject: [PATCH 2/5] make getting-started notebook a markdown file showing APIs (#1223) --- .../getting-started.ipynb | 152 ++++++++++++------ notebooks/test_notebooks.py | 9 ++ 2 files changed, 114 insertions(+), 47 deletions(-) diff --git a/notebooks/responsibleaidashboard/getting-started.ipynb b/notebooks/responsibleaidashboard/getting-started.ipynb index f3813bc378..c48cedb4fe 100644 --- a/notebooks/responsibleaidashboard/getting-started.ipynb +++ b/notebooks/responsibleaidashboard/getting-started.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "8bf3a3e1", "metadata": {}, "source": [ "# Getting Started" @@ -9,6 +10,15 @@ }, { "cell_type": "markdown", + "id": "e1491dd2", + "metadata": {}, + "source": [ + "This getting started notebook is an overview of the functionality in this repository. Note that this notebook is not runnable, it has a high-level overview of the APIs available and contains links to other notebooks in the repository." + ] + }, + { + "cell_type": "markdown", + "id": "2bf7fe3a", "metadata": {}, "source": [ "## Installation" @@ -16,38 +26,49 @@ }, { "cell_type": "markdown", + "id": "07303d17", "metadata": {}, "source": [ - "Use the following `pip` commands to install the Responsible AI Toolbox." + "Use the following `pip` commands to install the Responsible AI Toolbox.\n", + "\n", + "If running in jupyter, please make sure to restart the jupyter kernel after installing." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "756f5f51", "metadata": {}, - "outputs": [], "source": [ "!pip install raiwidgets" ] }, { "cell_type": "markdown", + "id": "60dca68e", "metadata": {}, "source": [ "## Dependencies" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "1bf0e821", + "metadata": {}, + "source": [ + "Please make sure to have the latest version of pandas installed if you are planning to use the error analysis component." + ] + }, + { + "cell_type": "markdown", + "id": "6f2ae1cd", "metadata": {}, - "outputs": [], "source": [ "!pip install --upgrade pandas" ] }, { "cell_type": "markdown", + "id": "f46ea6a5", "metadata": {}, "source": [ "## Overview & Setup" @@ -55,75 +76,98 @@ }, { "cell_type": "markdown", + "id": "da3c519c", "metadata": {}, "source": [ "Responsible AI Toolbox is an interoperable, customizable tool that empowers machine learning practitioners to evaluate their models and data based on their place in the model lifecycle.\n", "\n", - "Users may select components whose functionality supports their current objectives. First, import the relevant objects." + "Users may select components whose functionality supports their current objectives. First, the RAIInsights and ResponsibleAIDashboard must be imported." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "428f2b2d", "metadata": {}, - "outputs": [], "source": [ + "```Python\n", "from raiwidgets import ResponsibleAIDashboard\n", - "from responsibleai import RAIInsights" + "from responsibleai import RAIInsights\n", + "```" ] }, { "cell_type": "markdown", + "id": "9f152ac8", "metadata": {}, "source": [ - "It is necessary to initialize a RAIInsights object upon which the different components can be loaded. `task_type` holds the string `'regression'` or `'classification'` depending on the developer's purpose." + "Users will need to load a dataset, spit it into train and test datasets, and train a model on the training dataset." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "88a8e0fd", "metadata": {}, - "outputs": [], "source": [ - "rai_insights = RAIInsights(model, train_data, test_data, target_feature, task_type, \n", - " categorical_features=['f1', 'f2', 'f3'])" + "It is necessary to initialize a RAIInsights object upon which the different components can be loaded. `task_type` holds the string `'regression'` or `'classification'` depending on the developer's purpose.\n", + "\n", + "Users can also specify categorical features via the `categorical_features` parameter." + ] + }, + { + "cell_type": "markdown", + "id": "c9433f32", + "metadata": {}, + "source": [ + "```Python\n", + "task_type = 'regression'\n", + "rai_insights = RAIInsights(model, train_data, test_data, target_feature, task_type)\n", + "```" ] }, { "cell_type": "markdown", + "id": "c360364e", "metadata": {}, "source": [ - "The Interpretability and Error Analysis components can be added to the dashboard without any additional arguments:" + "The Interpretability and Error Analysis components can be added to the dashboard without any additional arguments.\n", + "\n", + "For an example, please see the [census classification model debugging notebook](https://github.com/microsoft/responsible-ai-toolbox/blob/main/notebooks/responsibleaidashboard/responsibleaidashboard-census-classification-model-debugging.ipynb)." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "407571b5", "metadata": {}, - "outputs": [], "source": [ + "```Python\n", "rai_insights.explainer.add()\n", - "rai_insights.error_analysis.add()" + "rai_insights.error_analysis.add()\n", + "```" ] }, { "cell_type": "markdown", + "id": "714655b3", "metadata": {}, "source": [ - "The Causal Inferencing component must be added with a specification of the feature that would be changed as a treatment." + "The Causal Inferencing component must be added with a specification of the feature that would be changed as a treatment.\n", + "\n", + "For an example, please see the [diabetes decision making notebook](https://github.com/microsoft/responsible-ai-toolbox/blob/main/notebooks/responsibleaidashboard/responsibleaidashboard-diabetes-decision-making.ipynb)." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "70f6f73e", "metadata": {}, - "outputs": [], "source": [ - "rai_insights.causal.add(treatment_features=['f1', 'f2', 'f3'])" + "```Python\n", + "rai_insights.causal.add(treatment_features=['bmi', 'bp', 's2'])\n", + "```" ] }, { "cell_type": "markdown", + "id": "7f5e8f45", "metadata": {}, "source": [ "The Counterfactuals component takes arguments specifying the number of counterfactuals to generate, the list of columns containing continuous values, and the desired label of the counterfactuals." @@ -131,38 +175,46 @@ }, { "cell_type": "markdown", + "id": "308d93ad", "metadata": {}, "source": [ - "In a classification situation, `desired_class` must specify the classification that the generated counterfactuals would fall into." + "In a classification situation, `desired_class` must specify the classification that the generated counterfactuals would fall into.\n", + "\n", + "For an example, please see the [housing classification model debugging notebook](https://github.com/microsoft/responsible-ai-toolbox/blob/main/notebooks/responsibleaidashboard/responsibleaidashboard-housing-classification-model-debugging.ipynb)." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "c7c244f2", "metadata": {}, - "outputs": [], "source": [ - "rai_insights.counterfactual.add(total_CFs=20, desired_class='opposite', continuous_features=['f1', 'f2', 'f3'])" + "```Python\n", + "rai_insights.counterfactual.add(total_CFs=20, desired_class='opposite')\n", + "```" ] }, { "cell_type": "markdown", + "id": "aa9ec639", "metadata": {}, "source": [ - "In a regression situation, `desired_range` must specify the minimum and maximum label that the generated counterfactuals can have." + "In a regression situation, `desired_range` must specify the minimum and maximum label that the generated counterfactuals can have.\n", + "For an example, please see the [diabetes regression model debugging notebook](https://github.com/microsoft/responsible-ai-toolbox/blob/main/notebooks/responsibleaidashboard/responsibleaidashboard-diabetes-regression-model-debugging.ipynb)." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "824449d9", "metadata": {}, - "outputs": [], "source": [ - "rai_insights.counterfactual.add(total_CFs=20, desired_range=[10, 20], continuous_features=['f1', 'f2', 'f3'])" + "```Python\n", + "rai_insights.counterfactual.add(total_CFs=20, desired_range=[50, 120])\n", + "```" ] }, { "cell_type": "markdown", + "id": "3031a740", "metadata": {}, "source": [ "## Computing and Visualizing Insights" @@ -170,38 +222,43 @@ }, { "cell_type": "markdown", + "id": "b3a4aec0", "metadata": {}, "source": [ "After loading the components into the RAIInsights object, it is necessary to calculate values relevant to them, such as model metrics and counterfactuals." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "c3e534a3", "metadata": {}, - "outputs": [], "source": [ - "rai_insights.compute()" + "```Python\n", + "rai_insights.compute()\n", + "```" ] }, { "cell_type": "markdown", + "id": "d2e05195", "metadata": {}, "source": [ "Once the values for each component have been computed, they can be displayed by loading the RAIInsights object into a ResponsibleAIDashboard." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", + "id": "3188e42d", "metadata": {}, - "outputs": [], "source": [ - "ResponsibleAIDashboard(rai_insights)" + "```Python\n", + "ResponsibleAIDashboard(rai_insights)\n", + "```" ] }, { "cell_type": "markdown", + "id": "17df1a35", "metadata": {}, "source": [ "## Learn More" @@ -209,6 +266,7 @@ }, { "cell_type": "markdown", + "id": "10b56cde", "metadata": {}, "source": [ "Visit the [GitHub](https://github.com/microsoft/responsible-ai-widgets) of Responsible AI Toolbox for more details, and take this [dashboard tour](./tour.ipynb) for an explanation of the different parts of each component." @@ -231,7 +289,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.7.11" } }, "nbformat": 4, diff --git a/notebooks/test_notebooks.py b/notebooks/test_notebooks.py index 581db08b46..bc6414c3b9 100644 --- a/notebooks/test_notebooks.py +++ b/notebooks/test_notebooks.py @@ -255,3 +255,12 @@ def test_responsibleaidashboard_multiclass_dnn_model_debugging(): test_values = {} assay_one_notebook(nb_path, nb_name, test_values) + + +@pytest.mark.notebooks +def test_responsibleaidashboard_getting_started(): + nb_path = RESPONSIBLEAIDASHBOARD + nb_name = "getting-started" + + test_values = {} + assay_one_notebook(nb_path, nb_name, test_values) From 36ab47d8931e558a901645a75c8803b0a831636a Mon Sep 17 00:00:00 2001 From: Ilya Matiach Date: Fri, 4 Mar 2022 12:43:50 -0500 Subject: [PATCH 3/5] refactor tabs out of RAI dashboard into a separate component (#1256) --- .../ErrorAnalysisDashboard.tsx | 3 +- .../IErrorAnalysisDashboardState.ts | 1 - .../Context/buildModelAssessmentContext.ts | 31 +- .../Controls/TabsView/TabsView.styles.ts | 31 ++ .../Controls/TabsView/TabsView.tsx | 270 ++++++++++++++++++ .../Controls/TabsView/TabsViewProps.ts | 70 +++++ .../ModelAssessmentDashboard.tsx | 246 +++------------- .../ModelAssessmentDashboardState.ts | 10 - 8 files changed, 409 insertions(+), 253 deletions(-) create mode 100644 libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.styles.ts create mode 100644 libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.tsx create mode 100644 libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsViewProps.ts diff --git a/libs/error-analysis/src/lib/ErrorAnalysisDashboard/ErrorAnalysisDashboard.tsx b/libs/error-analysis/src/lib/ErrorAnalysisDashboard/ErrorAnalysisDashboard.tsx index 64c66470d0..346bcea8db 100644 --- a/libs/error-analysis/src/lib/ErrorAnalysisDashboard/ErrorAnalysisDashboard.tsx +++ b/libs/error-analysis/src/lib/ErrorAnalysisDashboard/ErrorAnalysisDashboard.tsx @@ -328,8 +328,7 @@ export class ErrorAnalysisDashboard extends React.PureComponent< showMessageBar: false, viewType: ViewTypeKeys.ErrorAnalysisView, weightVectorLabels, - weightVectorOptions, - whatIfChartConfig: undefined + weightVectorOptions }; } diff --git a/libs/error-analysis/src/lib/ErrorAnalysisDashboard/Interfaces/IErrorAnalysisDashboardState.ts b/libs/error-analysis/src/lib/ErrorAnalysisDashboard/Interfaces/IErrorAnalysisDashboardState.ts index 22bb30e05d..2d2b79b448 100644 --- a/libs/error-analysis/src/lib/ErrorAnalysisDashboard/Interfaces/IErrorAnalysisDashboardState.ts +++ b/libs/error-analysis/src/lib/ErrorAnalysisDashboard/Interfaces/IErrorAnalysisDashboardState.ts @@ -25,7 +25,6 @@ export interface IErrorAnalysisDashboardState modelMetadata: IExplanationModelMetadata; modelChartConfig?: IGenericChartProps; dataChartConfig?: IGenericChartProps; - whatIfChartConfig?: IGenericChartProps; dependenceProps?: IGenericChartProps; globalImportanceIntercept: number[]; globalImportance: number[][]; diff --git a/libs/model-assessment/src/lib/ModelAssessmentDashboard/Context/buildModelAssessmentContext.ts b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Context/buildModelAssessmentContext.ts index efcd9ffbf0..ec268f0b27 100644 --- a/libs/model-assessment/src/lib/ModelAssessmentDashboard/Context/buildModelAssessmentContext.ts +++ b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Context/buildModelAssessmentContext.ts @@ -6,8 +6,6 @@ import { ISingleClassLocalFeatureImportance, JointDataset, Cohort, - WeightVectors, - ModelTypes, IExplanationModelMetadata, isThreeDimArray, ErrorCohort, @@ -71,21 +69,6 @@ export function buildInitialModelAssessmentContext( errorCohortList = errorCohortList.concat(preBuiltErrorCohortList); const cohorts = errorCohortList; - const weightVectorLabels = { - [WeightVectors.AbsAvg]: localization.Interpret.absoluteAverage - }; - const weightVectorOptions = []; - if (modelMetadata.modelType === ModelTypes.Multiclass) { - weightVectorOptions.push(WeightVectors.AbsAvg); - } - modelMetadata.classNames.forEach((name, index) => { - weightVectorLabels[index] = localization.formatString( - localization.Interpret.WhatIfTab.classLabel, - name - ); - weightVectorOptions.push(index); - }); - // only include tabs for which we have the required data const activeGlobalTabs: IModelAssessmentDashboardTab[] = getAvailableTabs( props, @@ -97,7 +80,6 @@ export function buildInitialModelAssessmentContext( name: item.text as string }; }); - const importances = props.errorAnalysisData?.[0]?.importances ?? []; return { activeGlobalTabs, baseCohort: cohorts[0], @@ -108,26 +90,15 @@ export function buildInitialModelAssessmentContext( errorAnalysisOption: ErrorAnalysisOptions.TreeMap, globalImportance: globalProps.globalImportance, globalImportanceIntercept: globalProps.globalImportanceIntercept, - importances, isGlobalImportanceDerivedFromLocal: globalProps.isGlobalImportanceDerivedFromLocal, jointDataset, - mapShiftErrorAnalysisOption: ErrorAnalysisOptions.TreeMap, - mapShiftVisible: false, modelChartConfig: undefined, modelMetadata, saveCohortVisible: false, selectedCohort: cohorts[0], - selectedFeatures: props.dataset.feature_names, - selectedWeightVector: - modelMetadata.modelType === ModelTypes.Multiclass - ? WeightVectors.AbsAvg - : 0, selectedWhatIfIndex: undefined, - sortVector: undefined, - weightVectorLabels, - weightVectorOptions, - whatIfChartConfig: undefined + sortVector: undefined }; } diff --git a/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.styles.ts b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.styles.ts new file mode 100644 index 0000000000..f2c7180aeb --- /dev/null +++ b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.styles.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + IStyle, + mergeStyleSets, + IProcessedStyleSet, + getTheme +} from "office-ui-fabric-react"; + +export interface ITabsViewStyles { + section: IStyle; + sectionHeader: IStyle; + buttonSection: IStyle; +} + +export const tabsViewStyles: () => IProcessedStyleSet = () => { + const theme = getTheme(); + return mergeStyleSets({ + buttonSection: { + textAlign: "center" + }, + section: { + textAlign: "left" + }, + sectionHeader: { + color: theme.semanticColors.bodyText, + padding: "16px 24px 16px 40px" + } + }); +}; diff --git a/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.tsx b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.tsx new file mode 100644 index 0000000000..7a87927e4c --- /dev/null +++ b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsView.tsx @@ -0,0 +1,270 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { CausalInsightsTab } from "@responsible-ai/causality"; +import { + WeightVectorOption, + ModelTypes, + WeightVectors +} from "@responsible-ai/core-ui"; +import { CounterfactualsTab } from "@responsible-ai/counterfactuals"; +import { DatasetExplorerTab } from "@responsible-ai/dataset-explorer"; +import { + ErrorAnalysisOptions, + ErrorAnalysisViewTab, + MapShift, + MatrixArea, + MatrixFilter, + TreeViewRenderer +} from "@responsible-ai/error-analysis"; +import { localization } from "@responsible-ai/localization"; +import _, { Dictionary } from "lodash"; +import { DefaultEffects, PivotItem, Stack, Text } from "office-ui-fabric-react"; +import * as React from "react"; + +import { AddTabButton } from "../../AddTabButton"; +import { GlobalTabKeys } from "../../ModelAssessmentEnums"; +import { FeatureImportancesTab } from "../FeatureImportances"; +import { ModelOverview } from "../ModelOverview"; + +import { tabsViewStyles } from "./TabsView.styles"; +import { ITabsViewProps } from "./TabsViewProps"; + +export interface ITabsViewState { + errorAnalysisOption: ErrorAnalysisOptions; + importances: number[]; + mapShiftErrorAnalysisOption: ErrorAnalysisOptions; + mapShiftVisible: boolean; + selectedFeatures: string[]; + selectedWeightVector: WeightVectorOption; + weightVectorLabels: Dictionary; + weightVectorOptions: WeightVectorOption[]; +} + +export class TabsView extends React.PureComponent< + ITabsViewProps, + ITabsViewState +> { + public constructor(props: ITabsViewProps) { + super(props); + const weightVectorLabels = { + [WeightVectors.AbsAvg]: localization.Interpret.absoluteAverage + }; + const weightVectorOptions = []; + if (props.modelMetadata.modelType === ModelTypes.Multiclass) { + weightVectorOptions.push(WeightVectors.AbsAvg); + } + props.modelMetadata.classNames.forEach((name, index) => { + weightVectorLabels[index] = localization.formatString( + localization.Interpret.WhatIfTab.classLabel, + name + ); + weightVectorOptions.push(index); + }); + const importances = props.errorAnalysisData?.[0]?.importances ?? []; + this.state = { + errorAnalysisOption: ErrorAnalysisOptions.TreeMap, + importances, + mapShiftErrorAnalysisOption: ErrorAnalysisOptions.TreeMap, + mapShiftVisible: false, + selectedFeatures: props.dataset.feature_names, + selectedWeightVector: + props.modelMetadata.modelType === ModelTypes.Multiclass + ? WeightVectors.AbsAvg + : 0, + weightVectorLabels, + weightVectorOptions + }; + if (this.props.requestImportances) { + this.props + .requestImportances([], new AbortController().signal) + .then((result) => { + this.setState({ importances: result }); + }); + } + } + + public render(): React.ReactNode { + const disabledView = + this.props.requestDebugML === undefined && + this.props.requestMatrix === undefined && + this.props.baseCohort.cohort.name !== + localization.ErrorAnalysis.Cohort.defaultLabel; + const classNames = tabsViewStyles(); + return ( + + {this.props.activeGlobalTabs[0]?.key !== + GlobalTabKeys.ErrorAnalysisTab && ( + + + + )} + {this.props.activeGlobalTabs.map((t, i) => ( + <> + + {t.key === GlobalTabKeys.ErrorAnalysisTab && + this.props.errorAnalysisData?.[0] && ( + + this.setState({ selectedFeatures: features }) + } + importances={this.state.importances} + onSaveCohortClick={(): void => { + this.props.setSaveCohortVisible(); + }} + showCohortName={false} + handleErrorDetectorChanged={this.handleErrorDetectorChanged} + selectedKey={this.state.errorAnalysisOption} + /> + )} + {t.key === GlobalTabKeys.ModelOverviewTab && ( + <> +
+ + { + localization.ModelAssessment.ComponentNames + .ModelOverview + } + +
+ + + )} + {t.key === GlobalTabKeys.DataExplorerTab && ( + <> +
+ + {localization.ModelAssessment.ComponentNames.DataExplorer} + +
+ + + )} + {t.key === GlobalTabKeys.FeatureImportancesTab && + this.props.modelExplanationData?.[0] && ( + + )} + {t.key === GlobalTabKeys.CausalAnalysisTab && + this.props.causalAnalysisData?.[0] && ( + + )} + + {t.key === GlobalTabKeys.CounterfactualsTab && + this.props.counterfactualData?.[0] && ( + + )} +
+ + + + + ))} + {this.state.mapShiftVisible && ( + + this.setState({ + errorAnalysisOption: this.state.errorAnalysisOption, + mapShiftVisible: false + }) + } + onSave={(): void => { + this.setState({ + mapShiftVisible: false + }); + this.props.setSaveCohortVisible(); + }} + onShift={(): void => { + // reset all states on shift + MatrixFilter.resetState(); + MatrixArea.resetState(); + TreeViewRenderer.resetState(); + this.setState({ + errorAnalysisOption: this.state.mapShiftErrorAnalysisOption, + mapShiftVisible: false + }); + this.props.setSelectedCohort(this.props.baseCohort); + }} + /> + )} +
+ ); + } + + private onWeightVectorChange = (weightOption: WeightVectorOption): void => { + this.props.jointDataset.buildLocalFlattenMatrix(weightOption); + this.props.cohorts.forEach((errorCohort) => + errorCohort.cohort.clearCachedImportances() + ); + this.setState({ selectedWeightVector: weightOption }); + }; + + private handleErrorDetectorChanged = (item?: PivotItem): void => { + if (item && item.props.itemKey) { + // Note comparison below is actually string comparison (key is string), we have to set the enum + if (item.props.itemKey === ErrorAnalysisOptions.HeatMap) { + const selectedOptionHeatMap = ErrorAnalysisOptions.HeatMap; + this.setErrorDetector(selectedOptionHeatMap); + } else { + const selectedOptionTreeMap = ErrorAnalysisOptions.TreeMap; + this.setErrorDetector(selectedOptionTreeMap); + } + } + }; + + private setErrorDetector = (key: ErrorAnalysisOptions): void => { + if (this.props.selectedCohort.isTemporary) { + this.setState({ + mapShiftErrorAnalysisOption: key, + mapShiftVisible: true + }); + } else { + this.setState({ + errorAnalysisOption: key + }); + } + }; +} diff --git a/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsViewProps.ts b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsViewProps.ts new file mode 100644 index 0000000000..099f800ee7 --- /dev/null +++ b/libs/model-assessment/src/lib/ModelAssessmentDashboard/Controls/TabsView/TabsViewProps.ts @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + ErrorCohort, + CohortSource, + IModelExplanationData, + IDataset, + IErrorAnalysisData, + ICausalAnalysisData, + ICounterfactualData, + IErrorAnalysisTreeNode, + IErrorAnalysisMatrix, + IPreBuiltCohort, + JointDataset, + IFilter, + ICompositeFilter, + MetricCohortStats, + IExplanationModelMetadata +} from "@responsible-ai/core-ui"; +import { IStringsParam } from "@responsible-ai/error-analysis"; +import { IDropdownOption } from "office-ui-fabric-react"; + +import { IModelAssessmentDashboardTab } from "../../ModelAssessmentDashboardState"; +import { GlobalTabKeys } from "../../ModelAssessmentEnums"; + +export interface ITabsViewProps { + modelExplanationData?: Array< + Omit + >; + causalAnalysisData?: ICausalAnalysisData[]; + counterfactualData?: ICounterfactualData[]; + errorAnalysisData?: IErrorAnalysisData[]; + cohortData?: IPreBuiltCohort[]; + cohorts: ErrorCohort[]; + jointDataset: JointDataset; + activeGlobalTabs: IModelAssessmentDashboardTab[]; + baseCohort: ErrorCohort; + selectedCohort: ErrorCohort; + dataset: IDataset; + requestPredictions?: ( + request: any[], + abortSignal: AbortSignal + ) => Promise; + requestDebugML?: ( + request: any[], + abortSignal: AbortSignal + ) => Promise; + requestImportances?: ( + request: any[], + abortSignal: AbortSignal + ) => Promise; + requestMatrix?: ( + request: any[], + abortSignal: AbortSignal + ) => Promise; + stringParams?: IStringsParam; + updateSelectedCohort: ( + filters: IFilter[], + compositeFilters: ICompositeFilter[], + source: CohortSource, + cells: number, + cohortStats: MetricCohortStats | undefined + ) => void; + setSaveCohortVisible: () => void; + setSelectedCohort: (cohort: ErrorCohort) => void; + modelMetadata: IExplanationModelMetadata; + addTabDropdownOptions: IDropdownOption[]; + addTab: (index: number, tab: GlobalTabKeys) => void; +} diff --git a/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboard.tsx b/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboard.tsx index 8e474a5857..484c0b84eb 100644 --- a/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboard.tsx +++ b/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboard.tsx @@ -1,46 +1,29 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -import { CausalInsightsTab } from "@responsible-ai/causality"; import { CohortBasedComponent, ModelAssessmentContext, ErrorCohort, - WeightVectorOption, CohortSource, Cohort, SaveCohort, defaultTheme } from "@responsible-ai/core-ui"; -import { CounterfactualsTab } from "@responsible-ai/counterfactuals"; -import { DatasetExplorerTab } from "@responsible-ai/dataset-explorer"; -import { - ErrorAnalysisOptions, - ErrorAnalysisViewTab, - MapShift, - MatrixArea, - MatrixFilter, - TreeViewRenderer -} from "@responsible-ai/error-analysis"; import { localization } from "@responsible-ai/localization"; import _ from "lodash"; import { - DefaultEffects, getTheme, IDropdownOption, loadTheme, - PivotItem, - Stack, - Text + Stack } from "office-ui-fabric-react"; import * as React from "react"; -import { AddTabButton } from "./AddTabButton"; import { getAvailableTabs } from "./AvailableTabs"; import { buildInitialModelAssessmentContext } from "./Context/buildModelAssessmentContext"; -import { FeatureImportancesTab } from "./Controls/FeatureImportances"; import { MainMenu } from "./Controls/MainMenu"; -import { ModelOverview } from "./Controls/ModelOverview"; +import { TabsView } from "./Controls/TabsView/TabsView"; import { modelAssessmentDashboardStyles } from "./ModelAssessmentDashboard.styles"; import { IModelAssessmentDashboardProps } from "./ModelAssessmentDashboardProps"; import { IModelAssessmentDashboardState } from "./ModelAssessmentDashboardState"; @@ -60,14 +43,6 @@ export class ModelAssessmentDashboard extends CohortBasedComponent< this.state = buildInitialModelAssessmentContext(_.cloneDeep(props)); loadTheme(props.theme || defaultTheme); this.addTabDropdownOptions = getAvailableTabs(this.props, true); - - if (this.props.requestImportances) { - this.props - .requestImportances([], new AbortController().signal) - .then((result) => { - this.setState({ importances: result }); - }); - } } public componentDidUpdate(prev: IModelAssessmentDashboardProps): void { if (prev.theme !== this.props.theme) { @@ -79,11 +54,6 @@ export class ModelAssessmentDashboard extends CohortBasedComponent< } public render(): React.ReactNode { - const disabledView = - this.props.requestDebugML === undefined && - this.props.requestMatrix === undefined && - this.state.baseCohort.cohort.name !== - localization.ErrorAnalysis.Cohort.defaultLabel; const classNames = modelAssessmentDashboardStyles(); return ( - - {this.state.activeGlobalTabs[0]?.key !== - GlobalTabKeys.ErrorAnalysisTab && ( - - - - )} - {this.state.activeGlobalTabs.map((t, i) => ( - <> - - {t.key === GlobalTabKeys.ErrorAnalysisTab && - this.props.errorAnalysisData?.[0] && ( - - this.setState({ selectedFeatures: features }) - } - importances={this.state.importances} - onSaveCohortClick={(): void => { - this.setState({ saveCohortVisible: true }); - }} - showCohortName={false} - handleErrorDetectorChanged={ - this.handleErrorDetectorChanged - } - selectedKey={this.state.errorAnalysisOption} - /> - )} - {t.key === GlobalTabKeys.ModelOverviewTab && ( - <> -
- - { - localization.ModelAssessment.ComponentNames - .ModelOverview - } - -
- - - )} - {t.key === GlobalTabKeys.DataExplorerTab && ( - <> -
- - { - localization.ModelAssessment.ComponentNames - .DataExplorer - } - -
- - - )} - {t.key === GlobalTabKeys.FeatureImportancesTab && - this.props.modelExplanationData?.[0] && ( - - )} - {t.key === GlobalTabKeys.CausalAnalysisTab && - this.props.causalAnalysisData?.[0] && ( - - )} - - {t.key === GlobalTabKeys.CounterfactualsTab && - this.props.counterfactualData?.[0] && ( - - )} -
- - - - - ))} -
+
{this.state.saveCohortVisible && ( )} - {this.state.mapShiftVisible && ( - - this.setState({ - errorAnalysisOption: this.state.errorAnalysisOption, - mapShiftVisible: false - }) - } - onSave={(): void => { - this.setState({ - mapShiftVisible: false, - saveCohortVisible: true - }); - }} - onShift={(): void => { - // reset all states on shift - MatrixFilter.resetState(); - MatrixArea.resetState(); - TreeViewRenderer.resetState(); - this.setState({ - errorAnalysisOption: this.state.mapShiftErrorAnalysisOption, - mapShiftVisible: false, - selectedCohort: this.state.baseCohort - }); - }} - /> - )}
); } + private setSaveCohortVisible = (): void => { + this.setState({ saveCohortVisible: true }); + }; + private addTab = (index: number, tab: GlobalTabKeys): void => { const tabs = [...this.state.activeGlobalTabs]; let dataCount: number; @@ -310,40 +164,6 @@ export class ModelAssessmentDashboard extends CohortBasedComponent< this.setState({ activeGlobalTabs: tabs }); }; - private onWeightVectorChange = (weightOption: WeightVectorOption): void => { - this.state.jointDataset.buildLocalFlattenMatrix(weightOption); - this.state.cohorts.forEach((errorCohort) => - errorCohort.cohort.clearCachedImportances() - ); - this.setState({ selectedWeightVector: weightOption }); - }; - - private handleErrorDetectorChanged = (item?: PivotItem): void => { - if (item && item.props.itemKey) { - // Note comparison below is actually string comparison (key is string), we have to set the enum - if (item.props.itemKey === ErrorAnalysisOptions.HeatMap) { - const selectedOptionHeatMap = ErrorAnalysisOptions.HeatMap; - this.setErrorDetector(selectedOptionHeatMap); - } else { - const selectedOptionTreeMap = ErrorAnalysisOptions.TreeMap; - this.setErrorDetector(selectedOptionTreeMap); - } - } - }; - - private setErrorDetector = (key: ErrorAnalysisOptions): void => { - if (this.state.selectedCohort.isTemporary) { - this.setState({ - mapShiftErrorAnalysisOption: key, - mapShiftVisible: true - }); - } else { - this.setState({ - errorAnalysisOption: key - }); - } - }; - private shiftErrorCohort = (cohort: ErrorCohort) => { this.setState({ baseCohort: cohort, @@ -351,6 +171,12 @@ export class ModelAssessmentDashboard extends CohortBasedComponent< }); }; + private setSelectedCohort = (cohort: ErrorCohort): void => { + this.setState({ + selectedCohort: cohort + }); + }; + private onSaveCohort = ( savedCohort: ErrorCohort, switchNew?: boolean diff --git a/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboardState.ts b/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboardState.ts index fe884a6e4a..e610390c4e 100644 --- a/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboardState.ts +++ b/libs/model-assessment/src/lib/ModelAssessmentDashboard/ModelAssessmentDashboardState.ts @@ -4,11 +4,9 @@ import { IExplanationModelMetadata, IGenericChartProps, - WeightVectorOption, ICohortBasedComponentState } from "@responsible-ai/core-ui"; import { ErrorAnalysisOptions } from "@responsible-ai/error-analysis"; -import { Dictionary } from "lodash"; import { GlobalTabKeys } from "./ModelAssessmentEnums"; @@ -19,22 +17,14 @@ export interface IModelAssessmentDashboardState modelMetadata: IExplanationModelMetadata; modelChartConfig?: IGenericChartProps; dataChartConfig?: IGenericChartProps; - whatIfChartConfig?: IGenericChartProps; dependenceProps?: IGenericChartProps; globalImportanceIntercept: number[]; globalImportance: number[][]; - importances: number[]; isGlobalImportanceDerivedFromLocal: boolean; sortVector?: number[]; editingCohortIndex?: number; - mapShiftErrorAnalysisOption: ErrorAnalysisOptions; - mapShiftVisible: boolean; selectedWhatIfIndex: number | undefined; - selectedFeatures: string[]; errorAnalysisOption: ErrorAnalysisOptions; - selectedWeightVector: WeightVectorOption; - weightVectorOptions: WeightVectorOption[]; - weightVectorLabels: Dictionary; saveCohortVisible: boolean; } From 8f79bcf69bc6577042a44f95ad092b1322bf139f Mon Sep 17 00:00:00 2001 From: Bo Zhang <71688188+zhb000@users.noreply.github.com> Date: Sat, 5 Mar 2022 13:38:48 +0800 Subject: [PATCH 4/5] Add individual causal scatter chart (#1258) * temp * refactor * test * style fix * comment --- .../CausalIndividualChart.tsx | 43 ++++++------ .../CausalIndividualChartStyles.ts | 4 ++ .../CausalIndividualStyles.ts | 3 +- .../CausalIndividualView.tsx | 4 +- .../getIndividualChartOptions.ts | 68 +++++++++++++++++++ libs/core-ui/src/index.ts | 1 + .../src/lib/Highchart/HighchartTypes.ts | 60 ---------------- .../src/lib/Highchart/HighchartWrapper.tsx | 3 +- .../src/lib/Highchart/ICommonChartProps.ts | 2 +- .../src/lib/Highchart/IHighchartsConfig.ts | 64 +++++++++++++++++ .../src/lib/Highchart/getHighchartsTheme.ts | 2 +- .../src/lib/util/getDependencyChartOptions.ts | 2 +- .../src/lib/util/getErrorBarChartOptions.ts | 2 +- .../util/getFeatureImportanceBarOptions.ts | 2 +- .../util/getFeatureImportanceBoxOptions.ts | 2 +- .../lib/util/getTreatmentBarChartOptions.ts | 2 +- 16 files changed, 172 insertions(+), 92 deletions(-) create mode 100644 libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/getIndividualChartOptions.ts create mode 100644 libs/core-ui/src/lib/Highchart/IHighchartsConfig.ts diff --git a/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChart.tsx b/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChart.tsx index b036596cb2..0c1db3628a 100644 --- a/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChart.tsx +++ b/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChart.tsx @@ -13,15 +13,11 @@ import { defaultModelAssessmentContext, ModelAssessmentContext, FabricStyles, - rowErrorSize + rowErrorSize, + BasicHighChart } from "@responsible-ai/core-ui"; import { localization } from "@responsible-ai/localization"; -import { - AccessibleChart, - IPlotlyProperty, - PlotlyMode, - IData -} from "@responsible-ai/mlchartlib"; +import { IPlotlyProperty, PlotlyMode, IData } from "@responsible-ai/mlchartlib"; import _, { Dictionary } from "lodash"; import { getTheme, @@ -36,6 +32,7 @@ import React from "react"; import { causalIndividualChartStyles } from "./CausalIndividualChartStyles"; import { CausalIndividualConstants } from "./CausalIndividualConstants"; import { CausalWhatIf } from "./CausalWhatIf"; +import { getIndividualChartOptions } from "./getIndividualChartOptions"; export interface ICausalIndividualChartProps { onDataClick: (data: number | undefined) => void; @@ -137,7 +134,7 @@ export class CausalIndividualChart extends React.PureComponent< onCancel={this.setXOpen.bind(this, false)} /> )} -
+
)} {canRenderChart && ( - +
+ +
)} -
-
+ +
-
+
{ - const trace = data.points[0]; - const index = trace.customdata[JointDataset.IndexLabel]; + const index = data.customdata[JointDataset.IndexLabel]; this.setTemporaryPointToCopyOfDatasetPoint(index); this.toggleSelectionOfPoint(index); }; @@ -378,7 +379,7 @@ export class CausalIndividualChart extends React.PureComponent< const metaX = this.context.jointDataset.metaDict[chartProps.xAxis.property]; const rawX = JointDataset.unwrap(dictionary, chartProps.xAxis.property); - hovertemplate += `${metaX.label}: %{customdata.X}
`; + hovertemplate += `${metaX.label}: {point.customdata.X}
`; rawX.forEach((val, index) => { if (metaX.treatAsCategorical) { @@ -405,7 +406,7 @@ export class CausalIndividualChart extends React.PureComponent< const metaY = this.context.jointDataset.metaDict[chartProps.yAxis.property]; const rawY = JointDataset.unwrap(dictionary, chartProps.yAxis.property); - hovertemplate += `${metaY.label}: %{customdata.Y}
`; + hovertemplate += `${metaY.label}: {point.customdata.Y}
`; rawY.forEach((val, index) => { if (metaY.treatAsCategorical) { customdata[index].Y = metaY.sortedCategoricalValues?.[val]; @@ -427,7 +428,7 @@ export class CausalIndividualChart extends React.PureComponent< trace.y = rawY; } } - hovertemplate += `${localization.Interpret.Charts.rowIndex}: %{customdata.Index}
`; + hovertemplate += `${localization.Interpret.Charts.rowIndex}: {point.customdata.Index}
`; hovertemplate += ""; trace.customdata = customdata as any; trace.hovertemplate = hovertemplate; diff --git a/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChartStyles.ts b/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChartStyles.ts index 37dcc97e60..acef475353 100644 --- a/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChartStyles.ts +++ b/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/CausalIndividualChartStyles.ts @@ -72,6 +72,7 @@ export interface ICausalIndividualChartStyles { infoButton: IStyle; rightJustifiedContainer: IStyle; notAvailable: IStyle; + highchartContainer: IStyle; } export const causalIndividualChartStyles: () => IProcessedStyleSet = @@ -199,6 +200,9 @@ export const causalIndividualChartStyles: () => IProcessedStyleSet IProcessedStyleSet - + - + {localization.CausalAnalysis.IndividualView.directIndividual} diff --git a/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/getIndividualChartOptions.ts b/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/getIndividualChartOptions.ts new file mode 100644 index 0000000000..a372e0391d --- /dev/null +++ b/libs/causality/src/lib/CausalAnalysisDashboard/Controls/CausalAnalysisView/CausalIndividualView/getIndividualChartOptions.ts @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { IPlotlyProperty } from "@responsible-ai/mlchartlib"; + +export function getIndividualChartOptions( + plotlyProperty: IPlotlyProperty, + onClickHandler?: (data: any) => void +): any { + let template = ""; + const data = plotlyProperty.data.map((series, seriesIndex) => { + const data: any = []; + series.x?.forEach((p, index) => { + const temp = { + customdata: series?.customdata?.[index], + marker: { + fillColor: + seriesIndex === 0 ? series?.marker?.color?.[index] : undefined, + lineColor: + seriesIndex === 0 ? undefined : series?.marker?.line?.color, + lineWidth: seriesIndex === 0 ? undefined : 3, + radius: seriesIndex === 0 ? 4 : 6, + symbol: + seriesIndex === 0 ? series?.marker?.symbol?.[index] : "diamond" + }, + x: p, + y: series?.y?.[index] + }; + template = series.hovertemplate as string; + data.push(temp); + }); + return data; + }); + + const series = data.map((d) => { + return { + data: d, + showInLegend: false + }; + }); + return { + chart: { + type: "scatter", + zoomType: "xy" + }, + plotOptions: { + scatter: { + tooltip: { + pointFormat: template + } + }, + series: { + cursor: "pointer", + point: { + events: { + click() { + if (onClickHandler === undefined) { + return; + } + onClickHandler(this); + } + } + } + } + }, + series + }; +} diff --git a/libs/core-ui/src/index.ts b/libs/core-ui/src/index.ts index 9e4024d3e5..8f3a4af7fa 100644 --- a/libs/core-ui/src/index.ts +++ b/libs/core-ui/src/index.ts @@ -75,3 +75,4 @@ export * from "./lib/Interfaces/IErrorAnalysisData"; export * from "./lib/Highchart/BasicHighChart"; export * from "./lib/Highchart/FeatureImportanceDependence"; export * from "./lib/Highchart/FeatureImportanceBar"; +export * from "./lib/Highchart/IHighchartsConfig"; diff --git a/libs/core-ui/src/lib/Highchart/HighchartTypes.ts b/libs/core-ui/src/lib/Highchart/HighchartTypes.ts index 5f10ef3aea..41a5b22a74 100644 --- a/libs/core-ui/src/lib/Highchart/HighchartTypes.ts +++ b/libs/core-ui/src/lib/Highchart/HighchartTypes.ts @@ -8,66 +8,6 @@ export type { ChartSelectionContextObject as HighchartSelectionContext } from "highcharts"; -export interface IHighchartsCustomConfig { - /** - * Max color name for color axis. Min is white. - */ - colorAxisMaxColor?: keyof IChartColorNames; - - /** - * Disables chart update and rerenders chart when parent component - * of the chart is rerendered - */ - disableUpdate?: boolean; - - /** - * Disables zooming for chart. Default zooming behavior is "xy". - * To keep zooming enabled but specify a different value then default, - * use "chartOptions.chart.zoomType" - */ - disableZoom?: boolean; - - /** - * If set true, makes chart background transparent. Default behavior is making - * chart background color same as theme background color - */ - transparentBackground?: boolean; - - /** - * Gets called when parent component is rerendered and chart is updated - * - * @param chart Chart reference - */ - onUpdate?(chart: Highcharts.Chart): void; - - /** - * Delegate which enables to change the order of the colors. - * This is the current order: - * primary - * blueMid - * teal - * purple - * purpleLight - * magentaDark - * magentaLight - * black - * orangeLighter - * redDark - * red - * neutral - * - * @param colors Currently sorted colors - * @returns New sorted colors - */ - onSortColors?( - colors: Array - ): Array; -} - -export interface IHighchartsConfig extends Highcharts.Options { - custom?: IHighchartsCustomConfig; -} - export type HighchartsModuleNames = "heatmap"; export type { IChartColorNames }; diff --git a/libs/core-ui/src/lib/Highchart/HighchartWrapper.tsx b/libs/core-ui/src/lib/Highchart/HighchartWrapper.tsx index a7b0f58757..b75c3d6e88 100644 --- a/libs/core-ui/src/lib/Highchart/HighchartWrapper.tsx +++ b/libs/core-ui/src/lib/Highchart/HighchartWrapper.tsx @@ -9,7 +9,8 @@ import * as React from "react"; import { getDefaultHighchartOptions } from "./getDefaultHighchartOptions"; import { getHighchartsTheme } from "./getHighchartsTheme"; import { HighchartReact } from "./HighchartReact"; -import { HighchartsModuleNames, IHighchartsConfig } from "./HighchartTypes"; +import { HighchartsModuleNames } from "./HighchartTypes"; +import { IHighchartsConfig } from "./IHighchartsConfig"; export interface IHighchartWrapperProps { chartOptions?: IHighchartsConfig; diff --git a/libs/core-ui/src/lib/Highchart/ICommonChartProps.ts b/libs/core-ui/src/lib/Highchart/ICommonChartProps.ts index 4bedd02c05..9f2aa2bf54 100644 --- a/libs/core-ui/src/lib/Highchart/ICommonChartProps.ts +++ b/libs/core-ui/src/lib/Highchart/ICommonChartProps.ts @@ -3,7 +3,7 @@ import { ITheme } from "@fluentui/react"; -import { IHighchartsConfig } from "./HighchartTypes"; +import { IHighchartsConfig } from "./IHighchartsConfig"; export interface ICommonChartProps { id?: string; diff --git a/libs/core-ui/src/lib/Highchart/IHighchartsConfig.ts b/libs/core-ui/src/lib/Highchart/IHighchartsConfig.ts new file mode 100644 index 0000000000..e55277f50e --- /dev/null +++ b/libs/core-ui/src/lib/Highchart/IHighchartsConfig.ts @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { IChartColorNames } from "./getHighchartsTheme"; + +export interface IHighchartsCustomConfig { + /** + * Max color name for color axis. Min is white. + */ + colorAxisMaxColor?: keyof IChartColorNames; + + /** + * Disables chart update and rerenders chart when parent component + * of the chart is rerendered + */ + disableUpdate?: boolean; + + /** + * Disables zooming for chart. Default zooming behavior is "xy". + * To keep zooming enabled but specify a different value then default, + * use "chartOptions.chart.zoomType" + */ + disableZoom?: boolean; + + /** + * If set true, makes chart background transparent. Default behavior is making + * chart background color same as theme background color + */ + transparentBackground?: boolean; + + /** + * Gets called when parent component is rerendered and chart is updated + * + * @param chart Chart reference + */ + onUpdate?(chart: Highcharts.Chart): void; + + /** + * Delegate which enables to change the order of the colors. + * This is the current order: + * primary + * blueMid + * teal + * purple + * purpleLight + * magentaDark + * magentaLight + * black + * orangeLighter + * redDark + * red + * neutral + * + * @param colors Currently sorted colors + * @returns New sorted colors + */ + onSortColors?( + colors: Array + ): Array; +} + +export interface IHighchartsConfig extends Highcharts.Options { + custom?: IHighchartsCustomConfig; +} diff --git a/libs/core-ui/src/lib/Highchart/getHighchartsTheme.ts b/libs/core-ui/src/lib/Highchart/getHighchartsTheme.ts index bfb856e73a..7af911a9d5 100644 --- a/libs/core-ui/src/lib/Highchart/getHighchartsTheme.ts +++ b/libs/core-ui/src/lib/Highchart/getHighchartsTheme.ts @@ -3,7 +3,7 @@ import { ITheme } from "@fluentui/react"; -import { IHighchartsConfig } from "./HighchartTypes"; +import { IHighchartsConfig } from "./IHighchartsConfig"; export interface IChartColorNames { black: string; diff --git a/libs/core-ui/src/lib/util/getDependencyChartOptions.ts b/libs/core-ui/src/lib/util/getDependencyChartOptions.ts index c775dd0270..c1a075bf07 100644 --- a/libs/core-ui/src/lib/util/getDependencyChartOptions.ts +++ b/libs/core-ui/src/lib/util/getDependencyChartOptions.ts @@ -3,7 +3,7 @@ import { ITheme } from "@fluentui/react"; -import { IHighchartsConfig } from "../Highchart/HighchartTypes"; +import { IHighchartsConfig } from "../Highchart/IHighchartsConfig"; export interface IDependenceData { x: number; diff --git a/libs/core-ui/src/lib/util/getErrorBarChartOptions.ts b/libs/core-ui/src/lib/util/getErrorBarChartOptions.ts index 0d516e1d24..d0b4eea1df 100644 --- a/libs/core-ui/src/lib/util/getErrorBarChartOptions.ts +++ b/libs/core-ui/src/lib/util/getErrorBarChartOptions.ts @@ -4,7 +4,7 @@ import { ITheme } from "@fluentui/react"; import { localization } from "@responsible-ai/localization"; -import { IHighchartsConfig } from "../Highchart/HighchartTypes"; +import { IHighchartsConfig } from "../Highchart/IHighchartsConfig"; import { ICausalAnalysisSingleData } from "../Interfaces/ICausalAnalysisData"; import { FabricStyles } from "./FabricStyles"; diff --git a/libs/core-ui/src/lib/util/getFeatureImportanceBarOptions.ts b/libs/core-ui/src/lib/util/getFeatureImportanceBarOptions.ts index 6d098c77e0..2d3d235c26 100644 --- a/libs/core-ui/src/lib/util/getFeatureImportanceBarOptions.ts +++ b/libs/core-ui/src/lib/util/getFeatureImportanceBarOptions.ts @@ -5,7 +5,7 @@ import { ITheme } from "@fluentui/react"; import { SeriesOptionsType } from "highcharts"; import { IGlobalSeries } from "../Highchart/FeatureImportanceBar"; -import { IHighchartsConfig } from "../Highchart/HighchartTypes"; +import { IHighchartsConfig } from "../Highchart/IHighchartsConfig"; import { FabricStyles } from "./FabricStyles"; diff --git a/libs/core-ui/src/lib/util/getFeatureImportanceBoxOptions.ts b/libs/core-ui/src/lib/util/getFeatureImportanceBoxOptions.ts index f2f3f2024b..5949686b87 100644 --- a/libs/core-ui/src/lib/util/getFeatureImportanceBoxOptions.ts +++ b/libs/core-ui/src/lib/util/getFeatureImportanceBoxOptions.ts @@ -4,7 +4,7 @@ import { ITheme } from "@fluentui/react"; import { IGlobalSeries } from "../Highchart/FeatureImportanceBar"; -import { IHighchartsConfig } from "../Highchart/HighchartTypes"; +import { IHighchartsConfig } from "../Highchart/IHighchartsConfig"; import { FabricStyles } from "./FabricStyles"; import { getBoxData } from "./getBoxData"; diff --git a/libs/core-ui/src/lib/util/getTreatmentBarChartOptions.ts b/libs/core-ui/src/lib/util/getTreatmentBarChartOptions.ts index 348e77c92a..c86c5781f8 100644 --- a/libs/core-ui/src/lib/util/getTreatmentBarChartOptions.ts +++ b/libs/core-ui/src/lib/util/getTreatmentBarChartOptions.ts @@ -4,7 +4,7 @@ import { ITheme } from "@fluentui/react"; import { localization } from "@responsible-ai/localization"; -import { IHighchartsConfig } from "../Highchart/HighchartTypes"; +import { IHighchartsConfig } from "../Highchart/IHighchartsConfig"; import { ICausalPolicyGains } from "../Interfaces/ICausalAnalysisData"; import { FabricStyles } from "./FabricStyles"; From cf6f2f58b7f396cfff33739e336ff8a220b8c1e9 Mon Sep 17 00:00:00 2001 From: Ilya Matiach Date: Sun, 6 Mar 2022 23:48:47 -0500 Subject: [PATCH 5/5] minor fix to url for responsibleai package in setup.py (#1260) --- responsibleai/setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/responsibleai/setup.py b/responsibleai/setup.py index cfee8c44c0..e5307990da 100644 --- a/responsibleai/setup.py +++ b/responsibleai/setup.py @@ -26,13 +26,13 @@ version=version, # noqa: F821 author="Roman Lutz, Ilya Matiach, Ke Xu", author_email="raiwidgets-maintain@microsoft.com", - description="SDK API to assess explain " + description="SDK API to explain " "models, generate counterfactual examples, analyze " "causal effects and analyze errors in Machine Learning " "models.", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/microsoft/responsible-ai-widgets", + url="https://github.com/microsoft/responsible-ai-toolbox", packages=setuptools.find_packages(), package_data={ '': [