import React, { Component, Fragment } from "react";
import PropTypes from "prop-types";
import BigNumber from "bignumber.js";
import { nonNullProperty } from "../../../Utils/utilFunctions";
import { fetchCrossValidation, fetchFold, downloadMatrix } from "../../../Utils/utilFunctions/fetchFunctions";
import { parseFormData } from "../../../Utils/utilFunctions/fetchFunctions/parseFormData";
import { getItemName, parseClassifiedItems } from "../../../Utils/utilFunctions/parseItems";
import { parseClassifiedListItems } from "../../../Utils/utilFunctions/parseListItems";
import { parseCrossValidationParams } from "../../../Utils/utilFunctions/parseParams";
import TabBody from "../../../Utils/Containers/TabBody";
import filterFunction from "../Filtering/FilterFunction";
import FilterTextField from "../Filtering/FilterTextField";
import { CalculateButton, MatrixButton, MatrixDownloadButton, MatrixSwapButton,
SettingsButton, StyledIconButton } from "../../../Utils/Buttons";
import DefaultClassificationResultSelector from "../Calculations/DefaultClassificationResultSelector";
import NumberOfFoldsSelector from "../Calculations/NumberOfFoldsSelector";
import SeedSelector from "../Calculations/SeedSelector";
import ThresholdSelector from "../Calculations/ThresholdSelector";
import TypeOfClassifierSelector from "../Calculations/TypeOfClassifierSelector";
import TypeOfRulesSelector from "../Calculations/TypeOfRulesSelector";
import TypeOfUnionsSelector from "../Calculations/TypeOfUnionsSelector";
import CustomBox from "../../../Utils/Containers/CustomBox";
import CustomDrawer from "../../../Utils/Containers/CustomDrawer"
import MatrixDialog from "../../../Utils/Dialogs/MatrixDialog";
import StyledDivider from "../../../Utils/DataDisplay/StyledDivider";
import CustomTooltip from "../../../Utils/DataDisplay/CustomTooltip";
import { ClassifiedObjectDialog } from "../../../Utils/Dialogs/DetailsDialog";
import StyledAlert from "../../../Utils/Feedback/StyledAlert";
import CustomTextField from "../../../Utils/Inputs/CustomTextField";
import CustomHeader from "../../../Utils/Surfaces/CustomHeader";
import InputAdornment from "@material-ui/core/InputAdornment";
import MenuItem from "@material-ui/core/MenuItem";
import Sigma from "mdi-material-ui/Sigma";
import {AttributesMenu} from "../../../Utils/Menus/AttributesMenu";
/**
* <h3>Overview</h3>
* The cross-validation tab in RuLeStudio.
* Presents the outcome of cross-validation for information table from current project.
*
* @constructor
* @category Project
* @subcategory Tabs
* @param {Object} props
* @param {string} props.objectGlobalName - The global visible object name used by all tabs as reference.
* @param {function} props.onTabChange - Callback fired when a tab is changed and there are unsaved changes in this tab.
* @param {Object} props.project - Current project.
* @param {string} props.serverBase - The host and port in the URL of an API call.
* @param {function} props.showAlert - Callback fired when results in this tab are based on outdated information table.
* @param {number} props.value - The index of a selected tab.
* @returns {React.Component}
*/
class CrossValidation extends Component {
constructor(props) {
super(props);
this.state = {
loading: {
crossValidation: false,
selectedFold: false,
},
folds: null,
foldData: null,
items: null,
displayedItems: [],
calculationsTime: "-",
parameters: {
consistencyThreshold: 0,
defaultClassificationResultType: "majorityDecisionClass",
numberOfFolds: 10,
seed: 0,
classifierType: "SimpleRuleClassifier",
typeOfRules: "certain",
typeOfUnions: "monotonic",
},
parametersSaved: true,
refreshNeeded: {
attributesMenu: false,
matrixMean: false,
matrixSum: false,
matrixFold: false
},
selected: {
foldIndex: 0,
item: null,
},
open: {
details: false,
matrixMean: false,
matrixSum: false,
matrixFold: false,
settings: false,
},
attributesMenuEl: null,
alertProps: undefined,
};
this.upperBar = React.createRef();
}
/**
* <h3>Overview</h3>
* Makes an API call on selected fold from cross-validation to receive it's current copy from server.
* Then, updates state and makes necessary changes in display.
*
* @function
* @memberOf CrossValidation
*/
getFold = (foldIndex, finallyCallback) => {
this.setState(({loading}) => ({
loading: { ...loading, selectedFold: true }
}), () => {
const { project: { id: projectId }, serverBase } = this.props;
const pathParams = { projectId, foldIndex };
fetchFold(
pathParams, serverBase
).then(result => {
if (this._isMounted && result != null && result.hasOwnProperty("Objects")
&& result.hasOwnProperty("objectNames")) {
const items = parseClassifiedItems(result.Objects, result.objectNames)
this.setState({
foldData: result,
items: items,
displayedItems: items
});
}
}).catch(exception => {
this.onSnackbarOpen(exception, () => {
if (this._isMounted) {
this.setState({
foldData: null,
items: null,
displayedItems: []
});
}
});
}).finally(() => {
if (this._isMounted) {
this.setState(({loading}) => ({
loading: { ...loading, selectedFold: false }
}), () => {
if (typeof finallyCallback === "function") finallyCallback();
});
}
});
});
}
generateFoldNames = (numberOfFolds) => {
return Array.from(Array(numberOfFolds).keys()).map(number => number + 1);
}
/**
* <h3>Overview</h3>
* Makes an API call on cross-validation to receive current copy of cross-validation from server.
* Then, updates state and makes necessary changes in display.
*
* @function
* @memberOf CrossValidation
*/
getCrossValidation = () => {
const { project, serverBase } = this.props;
const pathParams = { projectId: project.id }
const method = "GET";
fetchCrossValidation(
pathParams, method, null, serverBase
).then(result => {
if (this._isMounted && result != null) {
const { project: { foldIndex } } = this.props;
const resultParams = result.hasOwnProperty("parameters") ?
parseCrossValidationParams(result.parameters) : { };
const folds = resultParams.hasOwnProperty("numberOfFolds") ?
this.generateFoldNames(resultParams.numberOfFolds) : [];
this.setState(({calculationsTime, parameters, selected}) => ({
folds: folds,
calculationsTime: nonNullProperty(result, "calculationsTime") ?
result.calculationsTime : calculationsTime,
parameters: { ...parameters, ...resultParams },
selected: { ...selected, foldIndex: foldIndex }
}), () => {
const { selected: { foldIndex }} = this.state;
this.getFold(foldIndex);
});
if (result.hasOwnProperty("isCurrentData")) {
const messages = result.hasOwnProperty("errorMessages") ?
result.errorMessages : null;
this.props.showAlert(this.props.value, !result.isCurrentData, messages);
}
}
}).catch(exception => {
this.onSnackbarOpen(exception, () => {
if ( this._isMounted ) {
this.setState({
items: null,
displayedItems: []
});
}
});
}).finally(() => {
if ( this._isMounted ) {
const { project: { parameters: savedParameters, parametersSaved }} = this.props;
this.setState(({loading, parameters, selected}) => ({
loading: { ...loading, crossValidation: false },
parameters: parametersSaved ? parameters : { ...savedParameters },
parametersSaved: parametersSaved,
selected: { ...selected, item: null }
}));
}
});
}
/**
* <h3>Overview</h3>
* A component's lifecycle method. Fired once when component was mounted.
*
* <h3>Goal</h3>
* Method calls {@link getCrossValidation}.
*
* @function
* @memberOf CrossValidation
*/
componentDidMount() {
this._isMounted = true;
this.setState(({loading}) => ({
loading: { ...loading, crossValidation: true }
}), this.getCrossValidation);
}
/**
* <h3>Overview</h3>
* A component's lifecycle method. Fired after a component was updated.
*
* <h3>Goal</h3>
* If type of unions was changed to <code>monotonic</code> and consistency threshold is equal to 1,
* method changes value of threshold to 0.
* <br>
* <br>
* If type of rules was changed to <code>possible</code>, method changes consistency threshold to 0.
* <br>
* <br>
* If project was changed, method saves changes from previous project
* and calls {@link getCrossValidation} to receive the latest copy of cross-validation.
*
* @function
* @memberOf CrossValidation
* @param {Object} prevProps - Old props that were already replaced.
* @param {Object} prevState - Old state that was already replaced.
* @param {Object} snapshot - Returned from another lifecycle method <code>getSnapshotBeforeUpdate</code>. Usually undefined.
*/
componentDidUpdate(prevProps, prevState, snapshot) {
const { parameters: prevParameters } = prevState;
const { parameters } = this.state;
/* Check if consistency measure has changed and whether consistency threshold had boundary value */
if (parameters.typeOfUnions !== "monotonic") {
if (parameters.consistencyThreshold === 1) {
this.setState(({parameters}) => ({
parameters: { ...parameters, consistencyThreshold: 0, typeOfUnions: "monotonic" }
}))
} else {
this.setState(({parameters}) => ({
parameters: { ...parameters, typeOfUnions: "monotonic" }
}));
}
}
/* Check if type of rules changed to possible */
if (prevParameters.typeOfRules !== parameters.typeOfRules && parameters.typeOfRules === "possible") {
this.setState(({parameters}) => ({
parameters: { ...parameters, consistencyThreshold: 0}
}));
}
/* Check if project has been changed by user and save changes from previous project */
if (prevProps.project.id !== this.props.project.id) {
const { parametersSaved } = prevState;
if (!parametersSaved) {
let project = { ...prevProps.project };
const { parameters, selected: { foldIndex } } = this.state;
project.parameters = {
...project.parameters,
...parameters,
typeOfUnions: project.parameters.typeOfUnions
};
project.parametersSaved = parametersSaved;
project.foldIndex = foldIndex;
this.props.onTabChange(project);
}
this.setState(({loading}) => ({
loading: { ...loading, crossValidation: true }
}), this.getCrossValidation);
}
}
/**
* <h3>Overview</h3>
* A component's lifecycle method. Fired when component was requested to be unmounted.
*
* <h3>Goal</h3>
* Method saves changes from current project.
*
* @function
* @memberOf CrossValidation
*/
componentWillUnmount() {
this._isMounted = false;
const { parametersSaved } = this.state;
if (!parametersSaved) {
let project = JSON.parse(JSON.stringify(this.props.project));
const { parameters, selected: { foldIndex } } = this.state;
project.parameters = {
...project.parameters,
...parameters,
typeOfUnions: project.parameters.typeOfUnions
};
project.parametersSaved = parametersSaved;
project.foldIndex = foldIndex;
this.props.onTabChange(project);
}
}
/**
* <h3>Overview</h3>
* Makes an API call on cross-validation to cross-validate objects from current information table
* with selected parameters.
* Then, updates state and makes necessary changes in display.
*
* @function
* @memberOf CrossValidation
*/
onCalculateClick = () => {
const { project, serverBase } = this.props;
const { parameters } = this.state;
this.setState(({loading}) => ({
loading: { ...loading, crossValidation: true },
}), () => {
const pathParams = { projectId: project.id };
const method = "PUT";
const data = parseFormData(parameters, null);
fetchCrossValidation(
pathParams, method, data, serverBase
).then(result => {
if (result) {
const resultParams = result.hasOwnProperty("parameters") ?
parseCrossValidationParams(result.parameters) : { };
const folds = resultParams.hasOwnProperty("numberOfFolds") ?
this.generateFoldNames(resultParams.numberOfFolds) : [];
if (this._isMounted) {
this.setState(({calculationsTime, selected}) => ({
folds: folds,
calculationsTime: nonNullProperty(result, "calculationsTime") ?
result.calculationsTime : calculationsTime,
parametersSaved: true,
refreshNeeded: {
attributesMenu: true,
matrixMean: true,
matrixSum: true,
matrixFold: true
},
selected: { ...selected, foldIndex: 0 }
}), () => {
const { selected: { foldIndex }} = this.state;
this.getFold(foldIndex);
});
}
const projectCopy = JSON.parse(JSON.stringify(project));
projectCopy.parameters = {
...projectCopy.parameters,
...resultParams,
typeOfUnions: projectCopy.parameters.typeOfUnions
};
projectCopy.parametersSaved = true;
this.props.onTabChange(projectCopy);
if (result.hasOwnProperty("isCurrentData")) {
const messages = result.hasOwnProperty("errorMessages") ?
result.errorMessages : null;
this.props.showAlert(this.props.value, !result.isCurrentData, messages);
}
}
}).catch(exception => {
this.onSnackbarOpen(exception, () => {
if (this._isMounted) {
this.setState({
items: null,
displayedItems: []
});
}
});
}).finally(() => {
if (this._isMounted) {
this.setState(({loading, selected}) => ({
loading: { ...loading, crossValidation: false },
selected: { ...selected, item: null }
}));
}
});
});
};
/**
* <h3>Overview</h3>
* Makes an API call to download specified misclassification matrix.
*
* @function
* @memberOf CrossValidation
* @param {Object} queryParams - Specifies the type of a matrix to downloaded.
*/
onSaveToFile = (queryParams) => {
const { project, serverBase } = this.props;
const pathParams = { projectId: project.id };
downloadMatrix(pathParams, queryParams, serverBase)
.catch(this.onSnackbarOpen);
};
toggleOpen = (name) => {
this.setState(({open}) => ({
open: {...open, [name]: !open[name]}
}));
};
swapMatrix = (from, to) => {
this.setState(({open}) => ({
open: { ...open, [from]: false }
}), () => {
this.setState(({open}) => ({
open: { ...open, [to]: true }
}));
});
};
onItemSelected = (index) => {
this.setState(({items, open, selected}) => ({
open: {...open, details: true, settings: false},
selected: {...selected, item: items[index]}
}));
};
onDefaultClassificationResultChange = (event) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
this.setState(({parameters}) => ({
parameters: {...parameters, defaultClassificationResultType: event.target.value},
parametersSaved: false
}));
};
onSeedChange = (number) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
const javaLong = new BigNumber("9223372036854775807");
const bigNumber = new BigNumber(number);
if (!bigNumber.isNaN()) {
if (bigNumber.isGreaterThan(javaLong)) {
this.setState({
alertProps: {
message: "Seed shouldn't be greater than 9223372036854775807.",
open: true,
severity: "warning"
}
})
return;
}
this.setState(({parameters}) => ({
parameters: { ...parameters, seed: bigNumber },
parametersSaved: false
}));
}
};
onSeedRandomize = () => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
const newSeed = Math.round(Math.random() * Math.pow(10, 16));
this.onSeedChange(newSeed);
};
onTypeOfRulesChange = (event) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
this.setState(({parameters}) => ({
parameters: {...parameters, typeOfRules: event.target.value},
parametersSaved: false
}));
};
onConsistencyThresholdChange = (threshold) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
this.setState(({parameters}) => ({
parameters: {...parameters, consistencyThreshold: threshold},
parametersSaved: false
}));
};
onTypeOfClassifierChange = (event) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
this.setState(({parameters}) => ({
parameters: {...parameters, classifierType: event.target.value},
parametersSaved: false
}));
};
onTypeOfUnionsChange = (event) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
this.setState(({parameters}) => ({
parameters: {...parameters, typeOfUnions: event.target.value},
parametersSaved: false
}));
};
onNumberOfFoldsChange = (event) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
const input = event.target.value;
if (!isNaN(input)) {
this.setState(({parameters}) => ({
parameters: {...parameters, numberOfFolds: Number(input)},
parametersSaved: false
}));
}
};
onFoldIndexChange = (event) => {
const { loading } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
const foldIndex = Number(event.target.value);
const finallyCallback = () => this.setState(({ selected }) => ({
selected: {...selected, foldIndex: foldIndex},
parametersSaved: false
}));
this.getFold(foldIndex, finallyCallback);
};
/**
* <h3>Overview</h3>
* Filters items from {@link CrossValidation}'s state.
* Method uses {@link filterFunction} to filter items.
*
* @function
* @memberOf CrossValidation
* @param {Object} event - Represents an event that takes place in DOM.
*/
onFilterChange = (event) => {
const { loading, items } = this.state;
if (loading.crossValidation || loading.selectedFold) {
return;
}
if (Array.isArray(items) && items.length) {
const filteredItems = filterFunction(event.target.value.toString(), items.slice());
this.setState(({selected}) => ({
displayedItems: filteredItems,
selected: { ...selected, item: null }
}));
}
};
onObjectNamesChange = (names) => {
this.setState(({items, displayedItems}) => ({
items: items.map((item, index) => {
item.name = getItemName(index, names);
return item;
}),
displayedItems: displayedItems.map((item, index) => {
item.name = getItemName(index, names);
return item;
})
}));
}
onAttributesMenuOpen = (event) => {
const currentTarget = event.currentTarget;
this.setState({
attributesMenuEl: currentTarget
});
};
onAttributesMenuClose = () => {
this.setState({
attributesMenuEl: null
});
};
onComponentRefresh = (target) => {
this.setState(({refreshNeeded}) => ({
refreshNeeded: { ...refreshNeeded, [target]: false }
}));
};
onSnackbarOpen = (exception, setStateCallback) => {
if (!(exception.hasOwnProperty("type") && exception.type === "AlertError")) {
console.error(exception);
return;
}
if (this._isMounted) {
this.setState({ alertProps: exception }, setStateCallback);
}
};
onSnackbarClose = (event, reason) => {
if (reason !== 'clickaway') {
this.setState(({alertProps}) => ({
alertProps: {...alertProps, open: false}
}));
}
};
render() {
const {
loading,
folds,
foldData,
displayedItems,
calculationsTime,
open,
parameters,
refreshNeeded,
selected,
alertProps,
attributesMenuEl
} = this.state;
const { objectGlobalName, project: { id: projectId }, serverBase } = this.props;
return (
<CustomBox id={"cross-validation"} variant={"Tab"}>
<CustomDrawer
dividers={false}
id={"cross-validation-settings"}
open={open.settings}
onClose={() => this.toggleOpen("settings")}
placeholder={this.upperBar.current ? this.upperBar.current.offsetHeight : undefined}
>
<TypeOfRulesSelector
style={{marginBottom: 16}}
TextFieldProps={{
onChange: this.onTypeOfRulesChange,
value: parameters.typeOfRules
}}
/>
<TypeOfUnionsSelector
style={{marginBottom: 16}}
TextFieldProps={{
disabledChildren: ["standard"],
onChange: this.onTypeOfUnionsChange,
value: parameters.typeOfUnions
}}
variant={"extended"}
/>
<ThresholdSelector
keepChanges={parameters.typeOfRules !== "possible"}
onChange={this.onConsistencyThresholdChange}
value={parameters.consistencyThreshold}
variant={"extended"}
/>
<StyledDivider
color={"secondary"}
flexItem={true}
margin={16}
orientation={"horizontal"}
style={{height: 1}}
/>
<TypeOfClassifierSelector
style={{marginBottom: 16}}
TextFieldProps={{
onChange: this.onTypeOfClassifierChange,
value: parameters.classifierType
}}
/>
<DefaultClassificationResultSelector
TextFieldProps={{
onChange: this.onDefaultClassificationResultChange,
value: parameters.defaultClassificationResultType
}}
/>
<StyledDivider
color={"secondary"}
flexItem={true}
margin={16}
orientation={"horizontal"}
style={{height: 1}}
/>
<SeedSelector
randomizeSeed={this.onSeedRandomize}
style={{marginBottom: 16}}
TextFieldProps={{
onChange: event => this.onSeedChange(event.target.value),
value: parameters.seed
}}
/>
<NumberOfFoldsSelector
TextFieldProps={{
onChange: this.onNumberOfFoldsChange,
value: parameters.numberOfFolds
}}
/>
</CustomDrawer>
<CustomBox customScrollbar={true} id={"cross-validation-content"} variant={"TabBody"}>
<CustomHeader id={"cross-validation-header"} paperRef={this.upperBar}>
<SettingsButton onClick={() => this.toggleOpen("settings")} />
<StyledDivider margin={16} />
<CustomTooltip
disableMaxWidth={true}
title={"Click on settings button on the left to customize parameters"}
>
<CalculateButton
aria-label={"cross-validation-calculate-button"}
disabled={loading.crossValidation || loading.selectedFold}
onClick={this.onCalculateClick}
/>
</CustomTooltip>
<StyledDivider margin={16} />
{Array.isArray(folds) && Boolean(folds.length) &&
<Fragment>
{/* Part regarding all folds */}
<p id={"all-folds"} style={{margin: "0 16px 0 0", fontSize: "1.15rem"}}>All folds:</p>
<MatrixButton
onClick={() => this.toggleOpen("matrixMean")}
tooltip={"Show mean ordinal misclassification matrix"}
/>
<CustomTooltip title={"Show accumulated ordinal misclassification matrix"}>
<StyledIconButton
aria-label={"sum-matrix-button"}
color={"secondary"}
onClick={() => this.toggleOpen("matrixSum")}
>
<Sigma />
</StyledIconButton>
</CustomTooltip>
<StyledDivider margin={16} />
{/* Part regarding specific fold */}
<CustomTextField
onChange={this.onFoldIndexChange}
InputProps={{
startAdornment: (
<InputAdornment position={"start"}>
Fold:
</InputAdornment>
)
}}
select={true}
value={selected.foldIndex}
>
{folds.map((fold, index) => (
<MenuItem key={index} value={index}>
{fold}
</MenuItem>
))}
</CustomTextField>
<p id={"fold-colon"} style={{margin: "0 16px 0 4px", fontSize: "1.15rem"}}>:</p>
<MatrixButton
onClick={() => this.toggleOpen("matrixFold")}
tooltip={`Open ordinal misclassification matrix for fold ${selected.foldIndex + 1}`}
/>
</Fragment>
}
<span style={{flexGrow: 1}} />
<FilterTextField onChange={this.onFilterChange} />
</CustomHeader>
<TabBody
content={parseClassifiedListItems(displayedItems)}
id={"cross-validation-list"}
isArray={Array.isArray(displayedItems) && Boolean(displayedItems.length)}
isLoading={loading.crossValidation || loading.selectedFold}
ListProps={{
onItemSelected: this.onItemSelected
}}
ListSubheaderProps={{
onSettingsClick: this.onAttributesMenuOpen,
style: this.upperBar.current ? { top: this.upperBar.current.offsetHeight } : undefined
}}
noFilterResults={!displayedItems}
subheaderContent={[
{
label: "Fold:",
value: selected.foldIndex + 1
},
{
label: "Training objects:",
value: nonNullProperty(foldData, "numberOfTrainingObjects") ?
foldData.numberOfTrainingObjects : "-"
},
{
label: "Rules:",
value: nonNullProperty(foldData, "numberOfRules") ?
foldData.numberOfRules : "-"
},
{
label: "Test objects:",
value: nonNullProperty(foldData, "numberOfTestObjects") ?
foldData.numberOfTestObjects : "-",
},
{
label: "Calculated in:",
value: calculationsTime
}
]}
/>
{selected.item != null && folds != null &&
<ClassifiedObjectDialog
disableAttributesMenu={true}
coveredObjectResource={"crossValidation"}
item={selected.item}
objectGlobalName={undefined}
onClose={() => this.toggleOpen("details")}
onSnackbarOpen={this.onSnackbarOpen}
open={open.details}
projectId={projectId}
resource={`crossValidation/${selected.foldIndex}`}
serverBase={serverBase}
/>
}
{folds != null &&
<MatrixDialog
onClose={() => this.toggleOpen("matrixMean")}
onMatrixRefresh={() => this.onComponentRefresh("matrixMean")}
onSnackbarOpen={this.onSnackbarOpen}
open={open.matrixMean}
projectId={projectId}
refreshNeeded={refreshNeeded.matrixMean}
resource={"crossValidation"}
saveMatrix={() => this.onSaveToFile({ typeOfMatrix: "crossValidationMean" })}
serverBase={serverBase}
title={
<React.Fragment>
<MatrixSwapButton
onClick={() => this.swapMatrix("matrixMean", "matrixSum")}
tooltip={"Go to accumulated ordinal misclassification matrix"}
/>
<MatrixDownloadButton
onClick={() => this.onSaveToFile({ typeOfMatrix: "crossValidationMean" })}
tooltip={"Download mean matrix (txt)"}
/>
<span aria-label={"mean matrix title"} style={{paddingLeft: 8}}>
Mean ordinal misclassification matrix, deviations and details
</span>
</React.Fragment>
}
queryParams={{ typeOfMatrix: "crossValidationMean" }}
/>
}
{folds != null &&
<MatrixDialog
onClose={() => this.toggleOpen("matrixSum")}
onMatrixRefresh={() => this.onComponentRefresh("matrixSum")}
onSnackbarOpen={this.onSnackbarOpen}
open={open.matrixSum}
projectId={projectId}
refreshNeeded={refreshNeeded.matrixSum}
resource={"crossValidation"}
saveMatrix={() => this.onSaveToFile({ typeOfMatrix: "crossValidationSum" })}
serverBase={serverBase}
title={
<React.Fragment>
<MatrixSwapButton
onClick={() => this.swapMatrix("matrixSum", "matrixMean")}
tooltip={"Go to mean ordinal misclassification matrix"}
/>
<MatrixDownloadButton
onClick={() => this.onSaveToFile({ typeOfMatrix: "crossValidationSum" })}
tooltip={"Download accumulated matrix (txt)"}
/>
<span aria-label={"sum matrix title"} style={{paddingLeft: 8}}>
Accumulated ordinal misclassification matrix and details
</span>
</React.Fragment>
}
queryParams={{ typeOfMatrix: "crossValidationSum" }}
/>
}
{foldData != null &&
<MatrixDialog
onClose={() => this.toggleOpen("matrixFold")}
onMatrixRefresh={() => this.onComponentRefresh("matrixFold")}
onSnackbarOpen={this.onSnackbarOpen}
open={open.matrixFold}
projectId={projectId}
saveMatrix={() => {
this.onSaveToFile({
typeOfMatrix: "crossValidationFold",
numberOfFold: selected.foldIndex
});
}}
refreshNeeded={refreshNeeded.matrixFold}
resource={"crossValidation"}
serverBase={serverBase}
title={
<React.Fragment>
<MatrixDownloadButton
onClick={() => {
this.onSaveToFile({
typeOfMatrix: "crossValidationFold",
numberOfFold: selected.foldIndex
});
}}
tooltip={`Download fold ${selected.foldIndex + 1} matrix (txt)`}
/>
<span aria-label={"fold matrix title"} style={{paddingLeft: 8}}>
{`Fold ${selected.foldIndex + 1}: Ordinal misclassification matrix and details`}
</span>
</React.Fragment>
}
queryParams={{
typeOfMatrix: "crossValidationFold",
numberOfFold: selected.foldIndex
}}
/>
}
<AttributesMenu
ListProps={{
id: "cross-validation-main-desc-attr-menu"
}}
MuiMenuProps={{
anchorEl: attributesMenuEl,
onClose: this.onAttributesMenuClose
}}
objectGlobalName={objectGlobalName}
onAttributesRefreshed={() => this.onComponentRefresh("attributesMenu")}
onObjectNamesChange={this.onObjectNamesChange}
onSnackbarOpen={this.onSnackbarOpen}
projectId={projectId}
refreshNeeded={refreshNeeded.attributesMenu}
resource={"crossValidation"}
serverBase={serverBase}
queryParams={{ subject: selected.foldIndex }}
/>
</CustomBox>
<StyledAlert {...alertProps} onClose={this.onSnackbarClose} />
</CustomBox>
)
}
}
CrossValidation.propTypes = {
objectGlobalName: PropTypes.string,
onTabChange: PropTypes.func,
project: PropTypes.object,
serverBase: PropTypes.string,
showAlert: PropTypes.func,
value: PropTypes.number
};
export default CrossValidation;
Source