@modelx/modelx
Version:
Construct AI & ML models with JSON using Typescript & Tensorflow
742 lines (732 loc) • 30.3 kB
text/typescript
import { ModelX, ModelTypes, getGeneratedStatefulFunction, sumPreviousRows, } from './model';
import { Dimensions, getIsOutlier, mockDates } from './constants';
import { DataSet, } from '@modelx/data/src/index';
import { Faker, getData, getDatum, timeseriesSort, getMockClassification, getMockRegression, getMockTimeseries, } from './util';
describe('Generated Functions', () => {
describe('getGeneratedStatefulFunction', () => {
it('should be a function', () => {
expect(getGeneratedStatefulFunction).toHaveProperty('constructor');
});
it('should return a function', () => {
const func = {
variable_name: 'testFunction',
function_body: 'return 3',
};
const generatedFunc = getGeneratedStatefulFunction({ ...func, props: { Luxon: {}, ModelXData: {} }, function_name_prefix: 'custom_prefix_' });
expect(generatedFunc).toHaveProperty('constructor');
expect(generatedFunc(true)).toBe(3);
});
it('should inject stateful information into the function execution context on this.props', () => {
const func = {
variable_name: 'testFunction',
function_body: 'return 3+this.props.Luxon.someVal',
};
const props = { Luxon: { someVal: 3 }, ModelXData: {} };
const generatedFunc = getGeneratedStatefulFunction({ ...func, props, function_name_prefix: 'custom_prefix_' });
expect(generatedFunc(true)).toBe(6);
});
});
describe('sumPreviousRows', () => {
it('should summarize data from previous rows', () => {
const data = [
{label:'current', value:10},
{label:'prev 1', value:20},
{label:'prev 2', value:30},
{label:'prev 3', value:40},
{label:'prev 4', value:50},
{label:'prev 5', value:60},
{label:'prev 6', value:70},
{label:'prev 7', value:80},
{label:'prev 8', value:90},
{label:'prev 9', value:100},
];
const property = 'value';
const prev2 = sumPreviousRows.call({ data, }, { property, offset: 1, rows: 2 });
expect(prev2).toBe(50);
expect(sumPreviousRows.call({ data, }, { property, offset: 1, rows: 1 })).toBe(20);
expect(sumPreviousRows.call({ data, }, { property, offset: 2, rows: 1 })).toBe(30);
expect(sumPreviousRows.call({ data, }, { property, offset: 1, rows: 3 })).toBe(90);
expect(() => { sumPreviousRows.call({ data, }, { property, offset: 0, rows: 3 }) }).toThrow('Offset must be larger');
});
});
});
describe('ModelX', () => {
// beforeAll(async function () {
// return true;
// },5000);
describe('constructor', () => {
it('should export a named module class', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_timeseries_date_format:'ff',
});
expect(m1).toBeInstanceOf(ModelX);
expect(m1).toHaveProperty('constructor');
});
});
describe('getTimeseriesDimension', () => {
it('should return dimension and format if set correctly', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_timeseries_date_format:'ff',
});
const m2 = new ModelX({
model_type: ModelTypes.REGRESSION,
dimension: Dimensions.MONTHLY,
prediction_timeseries_date_format:'ff',
});
// console.log({m1})
expect(m1.getTimeseriesDimension({dimension:Dimensions.YEARLY})).toMatchObject({
dimension: Dimensions.YEARLY,
dateFormat: 'ff',
});
expect(m2.getTimeseriesDimension({})).toMatchObject({
dimension: Dimensions.MONTHLY,
dateFormat: 'ff',
});
});
it('should return error without a date format ', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
});
const m2 = new ModelX({
model_type: ModelTypes.REGRESSION,
dimension: Dimensions.MONTHLY,
});
// console.log({m1})
expect(() => {
m1.getTimeseriesDimension({ })
}).toThrow(/Invalid timeseries dimension/);
expect(() => {
m2.getTimeseriesDimension({});
}).toThrow(/Invalid timeseries date format/);
});
it('should return dimension and format from dataset', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_timeseries_date_format:'ff',
});
// console.log({ m1 })
const DataSetData = [{ dimension: 'monthly', }];
expect(m1.getTimeseriesDimension({
DataSetData,
})).toMatchObject({
dimension: Dimensions.MONTHLY,
dateFormat: 'ff',
});
});
it('should calculate and return dimension and format from dataset', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_timeseries_date_format:'iso',
});
const DataSetData = [
{
date:'2020-02-01',
},
{
date:'2020-03-01',
},
{
date:'2020-04-01',
},
];
expect(m1.getTimeseriesDimension({
DataSetData,
})).toMatchObject({
dimension: Dimensions.MONTHLY,
dateFormat: 'iso',
});
const m2 = new ModelX({
model_type: ModelTypes.REGRESSION,
});
const DataSetData2 = [
{
date: new Date('2020-02-01'),
},
{
date: new Date('2020-02-02'),
},
{
date: new Date('2020-02-03'),
},
];
expect(m2.getTimeseriesDimension({
DataSetData:DataSetData2,
})).toMatchObject({
dimension: Dimensions.DAILY,
dateFormat: 'js',
});
});
});
describe('getForecastDates', () => {
it('should return range of dates', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_timeseries_start_date: new Date('2020-04-01'),
prediction_timeseries_end_date: new Date('2020-04-03'),
dimension: Dimensions.DAILY,
});
// console.log({m1})
const dates = m1.getForecastDates({ dimension: Dimensions.YEARLY });
expect(dates.length).toBe(3);
});
it('should throw error with missing dimension', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_timeseries_start_date: new Date('2020-04-01'),
prediction_timeseries_end_date: new Date('2020-04-03'),
});
expect(() => {
m1.getForecastDates({});
}).toThrow('Forecasts require a timeseries dimension');
});
it('should throw error with missing dimension', () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
dimension: Dimensions.DAILY,
});
expect(() => {
m1.getForecastDates({});
}).toThrow('Start and End Forecast Dates are required');
});
});
describe('addMockData', () => {
it('should add mock data', () => {
const data = getData(2);
const mockEncodedData = getData(1);
const DS = new DataSet(data);
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
mockEncodedData,
});
// console.log('BEFORE m1.DataSet.data', m1.DataSet.data);
m1.addMockData();
// console.log('AFTER m1.DataSet.data', m1.DataSet.data);
expect(m1.DataSet.data.length).toBe(data.length+mockEncodedData.length);
});
it('should add mock data and mock dates', () => {
const data = getData(2);
const mockEncodedData = getData(1);
const DS = new DataSet(data);
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
mockEncodedData,
});
m1.addMockData({use_mock_dates:true});
expect(m1.DataSet.data.length).toBe(data.length+mockEncodedData.length+mockDates.length);
});
it('should add mock dates', () => {
const data = getData(2);
const DS = new DataSet(data);
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
});
m1.addMockData({use_mock_dates:true});
expect(m1.DataSet.data.length).toBe(data.length+mockDates.length);
});
});
describe('removeMockData', () => {
it('should remove mock data', () => {
const data = getData(2);
const mockEncodedData = getData(1);
const DS = new DataSet(data.concat(mockEncodedData));
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
mockEncodedData,
});
// console.log('BEFORE m1.DataSet.data', m1.DataSet.data);
m1.removeMockData();
// console.log('AFTER m1.DataSet.data', m1.DataSet.data);
expect(m1.DataSet.data.length).toBe(data.length);
});
it('should remove mock data and mock dates', () => {
const data = getData(2);
const mockEncodedData = getData(1);
const DS = new DataSet(data.concat(mockEncodedData, mockDates));
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
mockEncodedData,
});
m1.removeMockData({use_mock_dates:true});
expect(m1.DataSet.data.length).toBe(data.length);
});
it('should remove mock dates', () => {
const data = getData(2);
const DS = new DataSet(data.concat(mockDates));
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
});
m1.removeMockData({use_mock_dates:true});
expect(m1.DataSet.data.length).toBe(data.length);
});
});
describe('getCrosstrainingData', () => {
it('should return test and train data', () => {
const data = getData(10);
const DS = new DataSet(data);
const train_size = 0.6;
const dataTrainSize = Math.round(data.length * train_size);
const dataTestSize = Math.round(data.length * (1-train_size));
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
DataSet: DS,
cross_validation_options: {
train_size,
}
});
// console.log({ m1 })
const { test, train } = m1.getCrosstrainingData();
// console.log({ test, train });
expect(dataTrainSize).toBe(train.length);
expect(dataTestSize).toBe(test.length);
// m1.removeMockData({use_mock_dates:true});
// expect(m1.DataSet.data.length).toBe(data.length);
});
it('should return test and train data sorted for timeseries. The split data should split original data in sorted order', () => {
const data = getData(10).sort((a, b) => a.date.valueOf() - b.date.valueOf());
// console.log({ data });
const DS = new DataSet(data);
const train_size = 0.8;
const dataTrainSize = Math.round(data.length * train_size);
const dataTestSize = Math.round(data.length * (1-train_size));
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: DS,
cross_validation_options: {
train_size,
}
});
// console.log({ m1 })
const { test, train } = m1.getCrosstrainingData();
// console.log({ test, });
expect(dataTrainSize).toBe(train.length);
expect(dataTestSize).toBe(test.length);
expect(test[0].date).toBe(data[dataTrainSize].date);
// m1.removeMockData({use_mock_dates:true});
// expect(m1.DataSet.data.length).toBe(data.length);
});
});
describe('validateTrainingData', () => {
it('should not throw error if input matrix is all numerical', () => {
const inputMatrix = [
[1, 0, 1, 0],
[0, 0, 0, 1],
[1, 0, 0, 1],
];
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
x_indep_matrix_train: inputMatrix,
});
expect(m1.validateTrainingData()).toBeTruthy;
expect(m1.validateTrainingData({ inputMatrix, })).toBeTruthy;
});
it('should throw errors if input matrix is not all numerical', () => {
const inputMatrixUndef = [
[1, 0, 1, undefined],
[0, 0, 0, 1],
[1, 0, 0, 1],
];
const inputMatrixNaN = [
[1, 0, 1, NaN],
[0, 0, 0, 1],
[1, 0, 0, 1],
];
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
});
expect(() => {
m1.validateTrainingData({ inputMatrix: inputMatrixUndef, })
}).toThrow();
expect(() => {
m1.validateTrainingData({ inputMatrix: inputMatrixNaN, })
}).toThrow();
});
});
describe('async getPredictionData', () => {
const predictionMatrix = [
[1, 0, 1, 0],
[0, 0, 0, 1],
[1, 0, 0, 1],
];
it('should get predictions from a promise', async () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
});
const getPredictionInputPromise = async function () {
return predictionMatrix;
}
const predictionInputs = await m1.getPredictionData({ getPredictionInputPromise });
expect(predictionInputs).toBe(predictionMatrix);
});
it('should return prediction inputs if not passed a custom function', async () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
prediction_inputs: predictionMatrix,
});
const predictionInputs = await m1.getPredictionData();
expect(predictionInputs).toBe(predictionMatrix);
});
});
describe('async validateTimeseriesData', () => {
const timeseriesData = [
getDatum(new Date('2020-04-04T00:00:00.000Z')),
getDatum(new Date('2020-04-05T00:00:00.000Z')),
getDatum(new Date('2020-04-06T00:00:00.000Z')),
getDatum(new Date('2020-04-07T00:00:00.000Z')),
getDatum(new Date('2020-04-08T00:00:00.000Z')),
getDatum(new Date('2020-04-09T00:00:00.000Z')),
getDatum(new Date('2020-04-10T00:00:00.000Z')),
getDatum(new Date('2020-04-11T00:00:00.000Z')),
].sort(timeseriesSort);
// console.log({ timeseriesData });
it('should validate timeseries data that input predictions start within the timeseries range', async () => {
const prediction_inputs = [
getDatum(new Date('2020-04-11T00:00:00.000Z')),
getDatum(new Date('2020-04-12T00:00:00.000Z')),
// getDatum(new Date('2020-04-13T00:00:00.000Z')),
].sort(timeseriesSort);
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs,
prediction_timeseries_start_date: prediction_inputs[0],
});
m1.forecastDates = timeseriesData.map(t => t.date);
const validatedTimeseriesData = await m1.validateTimeseriesData();
expect(validatedTimeseriesData).toBeTruthy;
});
it('should throw an error if prediction inputs are not inclusive of forecastDates', async () => {
const prediction_inputs = [
getDatum(new Date('2020-04-12T00:00:00.000Z')),
getDatum(new Date('2020-04-13T00:00:00.000Z')),
].sort(timeseriesSort);
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs,
prediction_timeseries_start_date: prediction_inputs[0],
});
m1.forecastDates = timeseriesData.map(t => t.date);
expect(m1.validateTimeseriesData()).rejects.toThrow(/must be inclusive of forecastDates/);
});
it('should fix prediction inputs to only predict inputs inclusive of forecastDates', async () => {
const prediction_inputs = [
getDatum(new Date('2020-04-02T00:00:00.000Z')),
getDatum(new Date('2020-04-03T00:00:00.000Z')),
getDatum(new Date('2020-04-04T00:00:00.000Z')),
getDatum(new Date('2020-04-05T00:00:00.000Z')),
].sort(timeseriesSort);
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs,
prediction_timeseries_start_date: prediction_inputs[0],
});
m1.forecastDates = timeseriesData.map(t => t.date);
const validatedTimeseriesData = await m1.validateTimeseriesData();
expect(validatedTimeseriesData.raw_prediction_inputs.length).toBe(2);
const prediction_inputs2 = [
getDatum(new Date('2020-04-04T00:00:00.000Z')),
getDatum(new Date('2020-04-05T00:00:00.000Z')),
].sort(timeseriesSort);
const m2 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs: prediction_inputs2,
prediction_timeseries_start_date: prediction_inputs2[0],
});
m2.forecastDates = timeseriesData.map(t => t.date);
const validatedTimeseriesData2 = await m2.validateTimeseriesData({ fixPredictionDates: false, });
expect(validatedTimeseriesData2.raw_prediction_inputs.length).toBe(2);
const prediction_inputs3 = [
getDatum(new Date('2020-04-04T00:00:00.000Z')),
getDatum(new Date('2020-04-05T00:00:00.000Z')),
].sort(timeseriesSort);
const m3 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs: prediction_inputs3,
});
m3.forecastDates = timeseriesData.map(t => t.date);
const validatedTimeseriesData3 = await m2.validateTimeseriesData();
expect(validatedTimeseriesData3.raw_prediction_inputs.length).toBe(2);
// console.log({ validatedTimeseriesData });
});
});
describe('async getDataSetProperties', () => {
const timeseriesData = [
getDatum(new Date('2020-04-04T00:00:00.000Z')),
getDatum(new Date('2020-04-05T00:00:00.000Z')),
getDatum(new Date('2020-04-06T00:00:00.000Z')),
getDatum(new Date('2020-04-07T00:00:00.000Z')),
getDatum(new Date('2020-04-08T00:00:00.000Z')),
getDatum(new Date('2020-04-09T00:00:00.000Z')),
getDatum(new Date('2020-04-10T00:00:00.000Z')),
getDatum(new Date('2020-04-11T00:00:00.000Z')),
].sort(timeseriesSort);
it('should convert prediction next value functions ', async () => {
const prediction_inputs_next_value_functions = [
{
variable_name: 'type',
function_body: 'return "deposit"',
},
{
variable_name: 'gen_type',
function_body: 'return "deposit generated"',
},
];
const training_data_filter_function_body = 'return true;';
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs_next_value_functions,
training_data_filter_function_body,
// prediction_inputs,
// prediction_timeseries_start_date: prediction_inputs[0],
});
await m1.getDataSetProperties();
expect(m1.dimension).toBe(Dimensions.DAILY);
expect(m1.training_data_filter_function).toHaveProperty('constructor');
expect(m1.prediction_inputs_next_value_function).toHaveProperty('constructor');
});
it('should create Forecast Dates ', async () => {
const prediction_inputs = [
getDatum(new Date('2020-04-12T00:00:00.000Z')),
getDatum(new Date('2020-04-13T00:00:00.000Z')),
].sort(timeseriesSort);
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs,
prediction_timeseries_start_date: prediction_inputs[0].date,
prediction_timeseries_end_date: prediction_inputs[1].date,
});
await m1.getDataSetProperties();
expect(m1.forecastDates.length).toBe(2);
});
it('should create start and end prediction forecast Dates ', async () => {
const prediction_inputs = [
getDatum(new Date('2020-04-12T00:00:00.000Z')),
getDatum(new Date('2020-04-13T00:00:00.000Z')),
].sort(timeseriesSort);
const m1 = new ModelX({
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
DataSet: new DataSet([].concat(timeseriesData)),
prediction_inputs,
});
await m1.getDataSetProperties();
expect(m1.prediction_timeseries_start_date).toBeInstanceOf(Date);
expect(m1.prediction_timeseries_end_date).toBeInstanceOf(Date);
// expect(m1.training_data_filter_function).toHaveProperty('constructor');
// expect(m1.prediction_inputs_next_value_function).toHaveProperty('constructor');
// m1.forecastDates = timeseriesData.map(t => t.date);
// console.log({ m1 });
});
});
describe('async trainModel', () => {
const timeseriesData = [
getDatum(new Date('2020-04-04T00:00:00.000Z'),{amount:407}),
getDatum(new Date('2020-04-05T00:00:00.000Z'),{amount:408}),
getDatum(new Date('2020-04-06T00:00:00.000Z'),{amount:409}),
getDatum(new Date('2020-04-07T00:00:00.000Z'),{amount:410}),
getDatum(new Date('2020-04-08T00:00:00.000Z'),{amount:411}),
getDatum(new Date('2020-04-09T00:00:00.000Z'),{amount:412}),
getDatum(new Date('2020-04-10T00:00:00.000Z'),{amount:413}),
getDatum(new Date('2020-04-11T00:00:00.000Z'),{amount:414}),
getDatum(new Date('2020-04-12T00:00:00.000Z'),{amount:415}),
getDatum(new Date('2020-04-13T00:00:00.000Z'),{amount:416}),
getDatum(new Date('2020-04-14T00:00:00.000Z'),{amount:417}),
getDatum(new Date('2020-04-15T00:00:00.000Z'),{amount:418}),
getDatum(new Date('2020-04-16T00:00:00.000Z'),{amount:419}),
getDatum(new Date('2020-04-17T00:00:00.000Z'),{amount:420}),
getDatum(new Date('2020-04-18T00:00:00.000Z'),{amount:421}),
getDatum(new Date('2020-04-19T00:00:00.000Z'),{amount:422}),
getDatum(new Date('2020-04-20T00:00:00.000Z'),{amount:423}),
getDatum(new Date('2020-04-21T00:00:00.000Z'),{amount:424}),
getDatum(new Date('2020-04-22T00:00:00.000Z'),{amount:425}),
getDatum(new Date('2020-04-23T00:00:00.000Z'),{amount:426}),
].sort(timeseriesSort);
const data = [
{ input_1: 1, input_2: 10, input_3: 100, ignored_1: 1, output_1: 1000, output_2: 10000, },
{ input_1: 2, input_2: 20, input_3: 200, ignored_1: 2, output_1: 2000, output_2: 20000, },
{ input_1: 3, input_2: 30, input_3: 300, ignored_1: 3, output_1: 3000, output_2: 30000, },
{ input_1: 4, input_2: 40, input_3: 400, ignored_1: 4, output_1: 4000, output_2: 40000, },
{ input_1: 5, input_2: 50, input_3: 500, ignored_1: 5, output_1: 5000, output_2: 50000, },
{ input_1: 6, input_2: 60, input_3: 600, ignored_1: 6, output_1: 6000, output_2: 60000, },
{ input_1: 7, input_2: 70, input_3: 700, ignored_1: 7, output_1: 7000, output_2: 70000, },
{ input_1: 8, input_2: 80, input_3: 800, ignored_1: 8, output_1: 8000, output_2: 80000, },
{ input_1: 9, input_2: 90, input_3: 900, ignored_1: 9, output_1: 9000, output_2: 90000, },
];
const independentVariables = [
'input_1',
'input_2',
'input_3',
];
const dependentVariables = [
'output_1',
'output_2',
];
const featureColumns = [].concat(independentVariables, dependentVariables);
const training_feature_column_options = featureColumns
.reduce((result, val) => {
result[ val ] = ['scale', 'standard', ];
return result;
}, {});
it('should throw an error if missing inputs and outputs', async () => {
const m1 = new ModelX({
model_type: ModelTypes.REGRESSION,
trainingData: data,
});
const m2 = new ModelX({
model_type: ModelTypes.REGRESSION,
trainingData: data,
preprocessing_feature_column_options: {
ignored_1:['scale', 'standard', ],
},
x_independent_features: ['input_1'],
});
expect(m1.trainModel()).rejects.toThrow(/Missing Inputs/);
expect(m2.trainModel()).rejects.toThrow(/Missing Outputs/);
});
it('should train a model', async () => {
const m1 = new ModelX({
debug:false,
model_type: ModelTypes.REGRESSION,
trainingData: data,
training_data_filter_function: function (datum, dataIndex) {
if (datum.input_1 === 5) return false;
return true;
},
training_feature_column_options,
x_independent_features: independentVariables,
y_dependent_labels: dependentVariables,
});
await m1.trainModel();
// console.log({ m1 });
expect(m1.Model.trained).toBe(true);
expect(m1.Model.compiled).toBe(true);
}, 120000);
it('should train a forecast model', async () => {
const independent_variables = [
'year',
'month',
'day',
'late_payments',
];
const dependent_variables = [
'amount',
];
const training_feature_column_options = {
// amount: ['scale', 'standard',],
year: ['onehot',],
month: ['onehot',],
day: ['onehot',],
};
const m1 = new ModelX({
debug: false,
use_mock_dates_to_fit_trainning_data: true,
model_type: ModelTypes.TIMESERIES_REGRESSION_FORECAST,
trainingData: timeseriesData,
training_feature_column_options,
independent_variables,
dependent_variables,
// x_independent_features: independentVariables,
// y_dependent_labels: dependentVariables,
});
await m1.trainModel();
// console.log({ m1 });
expect(m1.Model.trained).toBe(true);
expect(m1.Model.compiled).toBe(true);
expect(m1.x_raw_independent_features).toMatchObject(['year', 'month', 'day', 'late_payments']);
expect(m1.y_raw_dependent_labels).toMatchObject(['amount']);
expect(m1.preprocessing_feature_column_options).toMatchObject({ amount: ['median'] });
expect(m1.training_feature_column_options).toMatchObject({
year: ['onehot'],
month: ['onehot'],
day: ['onehot'],
late_payments: ['label', { binary: true }],
amount: ['scale', 'standard']
});
expect(m1.y_dependent_labels).toMatchObject(['amount']);
// expect(m1.x_independent_features).toMatchObject(['year_2020', 'month_4', 'month_1', 'month_2', 'month_3', 'month_5', 'month_6', 'month_7', 'month_8', 'month_9', 'month_10', 'month_11', 'month_12', 'day_3', 'day_4', 'day_5', 'day_6', 'day_7', 'day_8', 'day_9', 'day_10', 'day_11', 'day_12', 'day_13', 'day_14', 'day_15', 'day_16', 'day_17', 'day_18', 'day_19', 'day_20', 'day_21', 'day_22', 'day_1', 'day_2', 'day_23', 'day_24', 'day_25', 'day_26', 'day_27', 'day_28', 'day_29', 'day_30', 'day_31', 'late_payments']);
}, 120000);
});
describe('async getTrainingData', () => {
it('should do nothing if no training data is passed', async () => {
const m1 = new ModelX({
debug:false,
model_type: ModelTypes.REGRESSION,
});
expect(m1.trainingData.length).toBe(0);
await m1.getTrainingData();
expect(m1.trainingData.length).toBe(0);
});
it('should accept traningData via options', async () => {
const {data, }=getMockRegression();
const m1 = new ModelX({
debug:false,
model_type: ModelTypes.REGRESSION,
});
expect(m1.trainingData.length).toBe(0);
await m1.getTrainingData({trainingData:data});
expect(m1.trainingData.length).toBe(data.length);
});
it('should get trainingData via a getDataPromise function', async () => {
const {data, }=getMockRegression();
const m1 = new ModelX({
debug:false,
model_type: ModelTypes.REGRESSION,
});
expect(m1.trainingData.length).toBe(0);
async function getDataPromise() {
return data;
}
await m1.getTrainingData({getDataPromise,});
expect(m1.trainingData.length).toBe(data.length);
});
});
describe('async checkTrainingStatus', () => {
it('should return true if model is already trained', async () => {
const m1 = new ModelX({
debug:false,
model_type: ModelTypes.REGRESSION,
});
m1.status.trained = true;
m1.getTrainingData = jest.fn();
m1.trainModel = jest.fn();
const trainingStatus = await m1.checkTrainingStatus();
expect(trainingStatus).toBe(true);
expect(m1.getTrainingData).toBeCalledTimes(0);
expect(m1.trainModel).toBeCalledTimes(0);
});
it('should retrain on demand if model is not trained', async () => {
const m1 = new ModelX({
debug:false,
model_type: ModelTypes.REGRESSION,
});
// m1.status.trained = true;
m1.getTrainingData = jest.fn();
m1.trainModel = jest.fn();
const trainingStatus = await m1.checkTrainingStatus();
expect(trainingStatus).toBe(true);
expect(m1.getTrainingData).toBeCalledTimes(1);
expect(m1.trainModel).toBeCalledTimes(1);
m1.status.trained = true;
const trainingStatus2 = await m1.checkTrainingStatus();
expect(trainingStatus2).toBe(true);
expect(m1.getTrainingData).toBeCalledTimes(1);
expect(m1.trainModel).toBeCalledTimes(1);
const trainingStatus3 = await m1.checkTrainingStatus({ retrain: true });
expect(trainingStatus3).toBe(true);
expect(m1.getTrainingData).toBeCalledTimes(2);
expect(m1.trainModel).toBeCalledTimes(2);
});
});
});