Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ML] DF Analytics: add results field to wizard and show regression stats #70893

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,24 @@ export interface DfAnalyticsExplainResponse {
}

export interface Eval {
meanSquaredError: number | string;
mse: number | string;
msle: number | string;
huber: number | string;
rSquared: number | string;
error: null | string;
}

export interface RegressionEvaluateResponse {
regression: {
huber: {
value: number;
};
mse: {
value: number;
};
msle: {
value: number;
};
r_squared: {
value: number;
};
Expand Down Expand Up @@ -414,19 +422,37 @@ export const useRefreshAnalyticsList = (

const DEFAULT_SIG_FIGS = 3;

export function getValuesFromResponse(response: RegressionEvaluateResponse) {
let meanSquaredError = response?.regression?.mse?.value;
interface RegressionEvaluateExtractedResponse {
mse: number | string;
msle: number | string;
huber: number | string;
r_squared: number | string;
}

if (meanSquaredError) {
meanSquaredError = Number(meanSquaredError.toPrecision(DEFAULT_SIG_FIGS));
}
export const EMPTY_STAT = '--';

let rSquared = response?.regression?.r_squared?.value;
if (rSquared) {
rSquared = Number(rSquared.toPrecision(DEFAULT_SIG_FIGS));
export function getValuesFromResponse(response: RegressionEvaluateResponse) {
const results: RegressionEvaluateExtractedResponse = {
mse: EMPTY_STAT,
msle: EMPTY_STAT,
huber: EMPTY_STAT,
r_squared: EMPTY_STAT,
};

if (response?.regression) {
for (const statType in response.regression) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than using a for..of, this loop could be written as:

Object.entries(response.regression).forEach(([statType, { value }]) => {
  results[statType as keyof RegressionEvaluteExtractedResponse] = value.toPrecision(
    DEFAULT_SIG_FIGS
  );
});

but i don't know whether it's more readable :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the more readable for loop option since in this case there are only 4 properties we're checking for.

if (response.regression.hasOwnProperty(statType)) {
let currentStatValue =
response.regression[statType as keyof RegressionEvaluateResponse['regression']]?.value;
if (currentStatValue) {
currentStatValue = Number(currentStatValue.toPrecision(DEFAULT_SIG_FIGS));
}
results[statType as keyof RegressionEvaluateExtractedResponse] = currentStatValue;
}
}
}

return { meanSquaredError, rSquared };
return results;
}
interface ResultsSearchBoolQuery {
bool: Dictionary<any>;
Expand Down Expand Up @@ -490,13 +516,22 @@ export function getEvalQueryBody({
return query;
}

export enum REGRESSION_STATS {
MSE = 'mse',
MSLE = 'msle',
R_SQUARED = 'rSquared',
HUBER = 'huber',
}

interface EvaluateMetrics {
classification: {
multiclass_confusion_matrix: object;
};
regression: {
r_squared: object;
mean_squared_error: object;
mse: object;
msle: object;
huber: object;
};
}

Expand Down Expand Up @@ -541,7 +576,9 @@ export const loadEvalData = async ({
},
regression: {
r_squared: {},
mean_squared_error: {},
mse: {},
msle: {},
huber: {},
},
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,8 @@ export const ConfigurationStepForm: FC<CreateAnalyticsStepProps> = ({
EuiComboBoxOptionOption[]
>([]);
const [includesTableItems, setIncludesTableItems] = useState<FieldSelectionItem[]>([]);
const [maxDistinctValuesError, setMaxDistinctValuesError] = useState<string | undefined>(
undefined
);
const [unsupportedFieldsError, setUnsupportedFieldsError] = useState<string | undefined>(
undefined
);
const [maxDistinctValuesError, setMaxDistinctValuesError] = useState<string | undefined>();
const [unsupportedFieldsError, setUnsupportedFieldsError] = useState<string | undefined>();

const { setEstimatedModelMemoryLimit, setFormState } = actions;
const { estimatedModelMemoryLimit, form, isJobCreated, requestMessages } = state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const DetailsStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
state,
}) => {
const { form, isJobCreated } = state;
const { description, jobId, destinationIndex } = form;
const { description, jobId, destinationIndex, resultsField } = form;

const detailsFirstCol: ListItems[] = [
{
Expand All @@ -37,6 +37,19 @@ export const DetailsStepDetails: FC<{ setCurrentStep: any; state: State }> = ({
},
];

if (
resultsField !== undefined &&
typeof resultsField === 'string' &&
resultsField.trim() !== ''
) {
detailsFirstCol.push({
title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.resultsField', {
defaultMessage: 'Results field',
}),
description: resultsField,
});
}

const detailsSecondCol: ListItems[] = [
{
title: i18n.translate('xpack.ml.dataframe.analytics.create.configDetails.jobDescription', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
jobIdExists,
jobIdInvalidMaxLength,
jobIdValid,
resultsField,
} = form;
const forceInput = useRef<HTMLInputElement | null>(null);

Expand Down Expand Up @@ -195,6 +196,22 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
data-test-subj="mlAnalyticsCreateJobFlyoutDestinationIndexInput"
/>
</EuiFormRow>
<EuiFormRow
label={i18n.translate('xpack.ml.dataframe.analytics.create.resultsFieldLabel', {
defaultMessage: 'Results field',
peteharverson marked this conversation as resolved.
Show resolved Hide resolved
})}
helpText={i18n.translate('xpack.ml.dataframe.analytics.create.resultsFieldHelpText', {
defaultMessage:
'Defines the name of the field in which to store the results of the analysis. Defaults to ml.',
})}
>
<EuiFieldText
disabled={isJobCreated}
value={resultsField}
onChange={(e) => setFormState({ resultsField: e.target.value })}
data-test-subj="mlAnalyticsCreateJobWizardResultsFieldInput"
/>
</EuiFormRow>
<EuiFormRow
fullWidth
isInvalid={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import {
isResultsSearchBoolQuery,
isRegressionEvaluateResponse,
ANALYSIS_CONFIG_TYPE,
REGRESSION_STATS,
EMPTY_STAT,
} from '../../../../common/analytics';

interface Props {
Expand All @@ -42,7 +44,17 @@ interface Props {
searchQuery: SavedSearchQuery;
}

const defaultEval: Eval = { meanSquaredError: '', rSquared: '', error: null };
const EMPTY_STATS = {
mse: EMPTY_STAT,
msle: EMPTY_STAT,
huber: EMPTY_STAT,
rSquared: EMPTY_STAT,
};

const defaultEval: Eval = {
...EMPTY_STATS,
error: null,
};

export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery }) => {
const {
Expand Down Expand Up @@ -82,18 +94,19 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
genErrorEval.eval &&
isRegressionEvaluateResponse(genErrorEval.eval)
) {
const { meanSquaredError, rSquared } = getValuesFromResponse(genErrorEval.eval);
const { mse, msle, huber, r_squared } = getValuesFromResponse(genErrorEval.eval);
setGeneralizationEval({
meanSquaredError,
rSquared,
mse,
msle,
huber,
rSquared: r_squared,
error: null,
});
setIsLoadingGeneralization(false);
} else {
setIsLoadingGeneralization(false);
setGeneralizationEval({
meanSquaredError: '--',
rSquared: '--',
...EMPTY_STATS,
error: genErrorEval.error,
});
}
Expand All @@ -118,18 +131,19 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
trainingErrorEval.eval &&
isRegressionEvaluateResponse(trainingErrorEval.eval)
) {
const { meanSquaredError, rSquared } = getValuesFromResponse(trainingErrorEval.eval);
const { mse, msle, huber, r_squared } = getValuesFromResponse(trainingErrorEval.eval);
setTrainingEval({
meanSquaredError,
rSquared,
mse,
msle,
huber,
rSquared: r_squared,
error: null,
});
setIsLoadingTraining(false);
} else {
setIsLoadingTraining(false);
setTrainingEval({
meanSquaredError: '--',
rSquared: '--',
...EMPTY_STATS,
error: trainingErrorEval.error,
});
}
Expand Down Expand Up @@ -274,22 +288,48 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
<EuiSpacer />
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiFlexGroup>
<EuiFlexGroup direction="column" gutterSize="s">
{/* First row stats */}
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionGenMSEstat'}
isLoading={isLoadingGeneralization}
title={generalizationEval.meanSquaredError}
isMSE
/>
<EuiFlexGroup>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionGenMSEstat'}
isLoading={isLoadingGeneralization}
title={generalizationEval.mse}
statType={REGRESSION_STATS.MSE}
/>
</EuiFlexItem>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionGenRSquaredStat'}
isLoading={isLoadingGeneralization}
title={generalizationEval.rSquared}
statType={REGRESSION_STATS.R_SQUARED}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
{/* Second row stats */}
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionGenRSquaredStat'}
isLoading={isLoadingGeneralization}
title={generalizationEval.rSquared}
isMSE={false}
/>
<EuiFlexGroup>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionGenMsleStat'}
isLoading={isLoadingGeneralization}
title={generalizationEval.msle}
statType={REGRESSION_STATS.MSLE}
/>
</EuiFlexItem>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionGenHuberStat'}
isLoading={isLoadingGeneralization}
title={generalizationEval.huber}
statType={REGRESSION_STATS.HUBER}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down Expand Up @@ -331,22 +371,48 @@ export const EvaluatePanel: FC<Props> = ({ jobConfig, jobStatus, searchQuery })
<EuiSpacer />
<EuiFlexGroup direction="column" gutterSize="none">
<EuiFlexItem>
<EuiFlexGroup>
<EuiFlexGroup direction="column" gutterSize="s">
{/* First row stats */}
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionTrainingMSEstat'}
isLoading={isLoadingTraining}
title={trainingEval.meanSquaredError}
isMSE
/>
<EuiFlexGroup>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionTrainingMSEstat'}
isLoading={isLoadingTraining}
title={trainingEval.mse}
statType={REGRESSION_STATS.MSE}
/>
</EuiFlexItem>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionTrainingRSquaredStat'}
isLoading={isLoadingTraining}
title={trainingEval.rSquared}
statType={REGRESSION_STATS.R_SQUARED}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
{/* Second row stats */}
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionTrainingRSquaredStat'}
isLoading={isLoadingTraining}
title={trainingEval.rSquared}
isMSE={false}
/>
<EuiFlexGroup>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionTrainingMsleStat'}
isLoading={isLoadingTraining}
title={trainingEval.msle}
statType={REGRESSION_STATS.MSLE}
/>
</EuiFlexItem>
<EuiFlexItem>
<EvaluateStat
dataTestSubj={'mlDFAnalyticsRegressionTrainingHuberStat'}
isLoading={isLoadingTraining}
title={trainingEval.huber}
statType={REGRESSION_STATS.HUBER}
/>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
Expand Down
Loading