Skip to content

Commit

Permalink
[ML] Clone analytics job (#59791)
Browse files Browse the repository at this point in the history
* [ML] clone analytics job

* [ML] flyout clone header

* [ML] improve clone action context menu item

* [ML] support advanced job cloning

* [ML] extractCloningConfig

* [ML] fix isAdvancedSetting condition, add test

* [ML] clone job header

* [ML] job description placeholder

* [ML] setEstimatedModelMemoryLimit on source index change

* [ML] Fix types.

* [ML] useUpdateEffect in create_analytics_form.tsx

* [ML] setJobClone action

* [ML] remove CreateAnalyticsFlyoutWrapper instance from the create_analytics_button.tsx

* [ML] fix types

* [ML] hack to align Clone button with the other actions

* [ML] unknown props lead to advanced editor

* [ML] rename maximum_number_trees ot max_trees

* [ML] fix forceInput

* [ML] populate excludesOptions on the first update, skip setting mml on the fist update

* [ML] init functional test for cloning analytics jobs

* [ML] functional tests

* [ML] fix functional tests imports

* [ML] fix indices names for functional tests

* [ML] functional tests for outlier detection and regression jobs cloning

* [ML] delete james tag

* [ML] fix tests arrangement

Co-authored-by: Walter Rafelsberger <walter@elastic.co>
  • Loading branch information
darnautov and walterra committed Mar 15, 2020
1 parent 7e36950 commit 96ac1aa
Show file tree
Hide file tree
Showing 26 changed files with 1,132 additions and 105 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/ml/__mocks__/shared_imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/

export function XJsonMode() {}
export const XJsonMode = jest.fn();
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,36 @@ export type IndexName = string;
export type IndexPattern = string;
export type DataFrameAnalyticsId = string;

export enum ANALYSIS_CONFIG_TYPE {
OUTLIER_DETECTION = 'outlier_detection',
REGRESSION = 'regression',
CLASSIFICATION = 'classification',
}

interface OutlierAnalysis {
[key: string]: {};
outlier_detection: {};
}

interface RegressionAnalysis {
regression: {
dependent_variable: string;
training_percent?: number;
prediction_field_name?: string;
};
interface Regression {
dependent_variable: string;
training_percent?: number;
prediction_field_name?: string;
}
export interface RegressionAnalysis {
[key: string]: Regression;
regression: Regression;
}

interface ClassificationAnalysis {
classification: {
dependent_variable: string;
training_percent?: number;
num_top_classes?: string;
prediction_field_name?: string;
};
interface Classification {
dependent_variable: string;
training_percent?: number;
num_top_classes?: string;
prediction_field_name?: string;
}
export interface ClassificationAnalysis {
[key: string]: Classification;
classification: Classification;
}

export interface LoadExploreDataArg {
Expand Down Expand Up @@ -136,21 +147,14 @@ type AnalysisConfig =
| ClassificationAnalysis
| GenericAnalysis;

export enum ANALYSIS_CONFIG_TYPE {
OUTLIER_DETECTION = 'outlier_detection',
REGRESSION = 'regression',
CLASSIFICATION = 'classification',
UNKNOWN = 'unknown',
}

export const getAnalysisType = (analysis: AnalysisConfig) => {
const keys = Object.keys(analysis);

if (keys.length === 1) {
return keys[0];
}

return ANALYSIS_CONFIG_TYPE.UNKNOWN;
return 'unknown';
};

export const getDependentVar = (analysis: AnalysisConfig) => {
Expand Down Expand Up @@ -245,6 +249,7 @@ export interface DataFrameAnalyticsConfig {
};
source: {
index: IndexName | IndexName[];
query?: any;
};
analysis: AnalysisConfig;
analyzed_fields: {
Expand All @@ -254,6 +259,7 @@ export interface DataFrameAnalyticsConfig {
model_memory_limit: string;
create_time: number;
version: string;
allow_lazy_start?: boolean;
}

export enum REFRESH_ANALYTICS_LIST_STATE {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { isAdvancedConfig } from './action_clone';

describe('Analytics job clone action', () => {
describe('isAdvancedConfig', () => {
test('should detect a classification job created with the form', () => {
const formCreatedClassificationJob = {
description: "Classification job with 'bank-marketing' dataset",
source: {
index: ['bank-marketing'],
query: {
match_all: {},
},
},
dest: {
index: 'dest_bank_1',
results_field: 'ml',
},
analysis: {
classification: {
dependent_variable: 'y',
num_top_classes: 2,
prediction_field_name: 'y_prediction',
training_percent: 2,
randomize_seed: 6233212276062807000,
},
},
analyzed_fields: {
includes: [],
excludes: [],
},
model_memory_limit: '350mb',
allow_lazy_start: false,
};

expect(isAdvancedConfig(formCreatedClassificationJob)).toBe(false);
});

test('should detect a outlier_detection job created with the form', () => {
const formCreatedOutlierDetectionJob = {
description: "Outlier detection job with 'glass' dataset",
source: {
index: ['glass_withoutdupl_norm'],
query: {
match_all: {},
},
},
dest: {
index: 'dest_glass_1',
results_field: 'ml',
},
analysis: {
outlier_detection: {
compute_feature_influence: true,
outlier_fraction: 0.05,
standardization_enabled: true,
},
},
analyzed_fields: {
includes: [],
excludes: ['id', 'outlier'],
},
model_memory_limit: '1mb',
allow_lazy_start: false,
};
expect(isAdvancedConfig(formCreatedOutlierDetectionJob)).toBe(false);
});

test('should detect a regression job created with the form', () => {
const formCreatedRegressionJob = {
description: "Regression job with 'electrical-grid-stability' dataset",
source: {
index: ['electrical-grid-stability'],
query: {
match_all: {},
},
},
dest: {
index: 'dest_grid_1',
results_field: 'ml',
},
analysis: {
regression: {
dependent_variable: 'stab',
prediction_field_name: 'stab_prediction',
training_percent: 20,
randomize_seed: -2228827740028660200,
},
},
analyzed_fields: {
includes: [],
excludes: [],
},
model_memory_limit: '150mb',
allow_lazy_start: false,
};

expect(isAdvancedConfig(formCreatedRegressionJob)).toBe(false);
});

test('should detect advanced classification job', () => {
const advancedClassificationJob = {
description: "Classification job with 'bank-marketing' dataset",
source: {
index: ['bank-marketing'],
query: {
match_all: {},
},
},
dest: {
index: 'dest_bank_1',
results_field: 'CUSTOM_RESULT_FIELD',
},
analysis: {
classification: {
dependent_variable: 'y',
num_top_classes: 2,
prediction_field_name: 'y_prediction',
training_percent: 2,
randomize_seed: 6233212276062807000,
},
},
analyzed_fields: {
includes: [],
excludes: [],
},
model_memory_limit: '350mb',
allow_lazy_start: false,
};

expect(isAdvancedConfig(advancedClassificationJob)).toBe(true);
});

test('should detect advanced outlier_detection job', () => {
const advancedOutlierDetectionJob = {
description: "Outlier detection job with 'glass' dataset",
source: {
index: ['glass_withoutdupl_norm'],
query: {
// TODO check default for `match`
match_all: {},
},
},
dest: {
index: 'dest_glass_1',
results_field: 'ml',
},
analysis: {
outlier_detection: {
compute_feature_influence: false,
outlier_fraction: 0.05,
standardization_enabled: true,
},
},
analyzed_fields: {
includes: [],
excludes: ['id', 'outlier'],
},
model_memory_limit: '1mb',
allow_lazy_start: false,
};
expect(isAdvancedConfig(advancedOutlierDetectionJob)).toBe(true);
});

test('should detect a custom query', () => {
const advancedRegressionJob = {
description: "Regression job with 'electrical-grid-stability' dataset",
source: {
index: ['electrical-grid-stability'],
query: {
match: {
custom_field: 'custom_match',
},
},
},
dest: {
index: 'dest_grid_1',
results_field: 'ml',
},
analysis: {
regression: {
dependent_variable: 'stab',
prediction_field_name: 'stab_prediction',
training_percent: 20,
randomize_seed: -2228827740028660200,
},
},
analyzed_fields: {
includes: [],
excludes: [],
},
model_memory_limit: '150mb',
allow_lazy_start: false,
};

expect(isAdvancedConfig(advancedRegressionJob)).toBe(true);
});

test('should detect custom analysis settings', () => {
const config = {
description: "Classification clone with 'bank-marketing' dataset",
source: {
index: 'bank-marketing',
},
dest: {
index: 'bank_classification4',
},
analyzed_fields: {
excludes: [],
},
analysis: {
classification: {
dependent_variable: 'y',
training_percent: 71,
max_trees: 1500,
},
},
model_memory_limit: '400mb',
};

expect(isAdvancedConfig(config)).toBe(true);
});

test('should detect as advanced if the prop is unknown', () => {
const config = {
description: "Classification clone with 'bank-marketing' dataset",
source: {
index: 'bank-marketing',
},
dest: {
index: 'bank_classification4',
},
analyzed_fields: {
excludes: [],
},
analysis: {
classification: {
dependent_variable: 'y',
training_percent: 71,
maximum_number_trees: 1500,
},
},
model_memory_limit: '400mb',
};

expect(isAdvancedConfig(config)).toBe(true);
});
});
});
Loading

0 comments on commit 96ac1aa

Please sign in to comment.