feat: enable custom baseline periods for Experiments to solve seasonality

This commit is contained in:
Christian Vidal Wolf
2026-02-23 18:34:04 +01:00
parent 69f4456942
commit 4935112a55
14 changed files with 428 additions and 1144 deletions
+83
View File
@@ -0,0 +1,83 @@
import fs from 'fs';
const experiment = {
start_date: "2026-01-18",
end_date: null,
asins: ["LINE:LEGENDS"],
marketplace: "ES"
};
const salesData = [
{ year: 2026, week: 7, asin: "B0CHW338VR", line: "LEGENDS", unitsTotal: 4, salesTotal: 24.40, marketplace: "ES" },
{ year: 2026, week: 7, asin: "B0CHW338VR", line: "LEGENDS", unitsTotal: 4, salesTotal: 24.40, marketplace: "GB" }
];
const getExperimentAsins = (experimentAsins, salesData) => {
const explicitAsins = new Set();
const lines = new Set();
(experimentAsins || []).forEach(a => {
const val = (a || '').trim().toUpperCase();
if (val.startsWith('LINE:')) {
lines.add(val.substring(5).trim());
} else if (val) {
explicitAsins.add(val);
}
});
const asinSet = new Set(explicitAsins);
if (lines.size > 0 && salesData) {
salesData.forEach(r => {
const line = (r.line || '').trim().toUpperCase();
if (line && lines.has(line)) {
if (r.asin) asinSet.add(r.asin.toUpperCase());
}
});
}
return asinSet;
};
const parseLocalDate = (dateStr) => {
if (!dateStr) return new Date();
const [y, m, d] = dateStr.split('T')[0].split('-');
return new Date(Number(y), Number(m) - 1, Number(d));
};
const calculate = (experiment, salesData) => {
const startDate = parseLocalDate(experiment.start_date);
const endDate = experiment.end_date ? parseLocalDate(experiment.end_date) : new Date();
console.log('Parsed start date:', startDate);
console.log('Parsed end date:', endDate);
const durationMs = endDate.getTime() - startDate.getTime();
const baselineStart = new Date(startDate.getTime() - durationMs);
const baselineEnd = startDate;
console.log('Parsed baseline start:', baselineStart);
console.log('Parsed baseline end:', baselineEnd);
const asinSet = getExperimentAsins(experiment.asins, salesData);
console.log('Resolved ASINs:', Array.from(asinSet));
const baselineData = salesData.filter(r => {
const recordDate = new Date(r.year, 0, 1 + (r.week - 1) * 7);
const inRange = recordDate >= baselineStart && recordDate < baselineEnd;
const isAsin = asinSet.has(r.asin.toUpperCase());
return isAsin && inRange && (experiment.marketplace === 'All' || r.marketplace === experiment.marketplace);
});
const experimentData = salesData.filter(r => {
const recordDate = new Date(r.year, 0, 1 + (r.week - 1) * 7);
const inRange = recordDate >= startDate && recordDate <= endDate;
const isAsin = asinSet.has(r.asin.toUpperCase());
return isAsin && inRange && (experiment.marketplace === 'All' || r.marketplace === experiment.marketplace);
});
console.log('Baseline records matched:', baselineData.length);
console.log('Experiment records matched:', experimentData.length);
};
calculate(experiment, salesData);