in packages/charts/src/chart_types/xy_chart/utils/series.ts [117:238]
export function splitSeriesDataByAccessors(
spec: BasicSeriesSpec,
xValueSums: Map<string | number, number>,
insertOrder = 0,
isStacked = false,
isBanded = false,
stackMode?: StackMode,
groupBySpec?: SmallMultiplesGroupBy,
deselectedDataSeries: SeriesIdentifier[] = [],
): {
dataSeries: Map<SeriesKey, DataSeries>;
xValues: Array<string | number>;
} {
const {
seriesType,
id: specId,
groupId,
data,
xAccessor,
yAccessors,
y0Accessors,
markSizeAccessor,
splitSeriesAccessors = [],
} = spec;
const dataSeries = new Map<SeriesKey, DataSeries>();
const xValues: Array<string | number> = [];
const nonNumericValues: Map<unknown, number> = new Map();
if (isStacked && Boolean(y0Accessors?.length)) {
Logger.warn(
`y0Accessors are not allowed with stackAccessors. y0Accessors will be ignored but available under initialY0.`,
);
}
for (let i = 0; i < data.length; i++) {
const datum = data[i];
const splitAccessors = getSplitAccessors(datum, splitSeriesAccessors);
// if splitSeriesAccessors are defined we should have at least one split value to include datum
if (splitSeriesAccessors.length > 0 && splitAccessors.size < 1) {
continue;
}
// skip if the datum is not an object or null
if (typeof datum !== 'object' || datum === null) {
continue;
}
const x = getAccessorValue(datum, xAccessor);
// skip if the x value is not a string or a number
if (typeof x !== 'string' && typeof x !== 'number') {
continue;
}
xValues.push(x);
let sum = xValueSums.get(x) ?? 0;
// extract small multiples aggregation values
const smH = groupBySpec?.horizontal?.by?.(spec, datum);
const smV = groupBySpec?.vertical?.by?.(spec, datum);
const xAccessorStr = getAccessorFieldName(xAccessor, 0);
yAccessors.forEach((accessor, index) => {
const cleanedDatum = extractYAndMarkFromDatum(
datum,
accessor,
nonNumericValues,
isBanded,
y0Accessors && y0Accessors[index],
markSizeAccessor,
);
const yAccessorStr = getAccessorFieldName(accessor, index);
const splitAccessorStrs = [...splitAccessors.values()].map((a, si) => getAccessorFieldName(a, si));
const seriesKeys = [...splitAccessorStrs, yAccessorStr];
const seriesIdentifier: Omit<XYChartSeriesIdentifier, 'key'> = stripUndefined({
specId,
seriesKeys,
xAccessor: xAccessorStr,
yAccessor: yAccessorStr,
splitAccessors,
smVerticalAccessorValue: smV,
smHorizontalAccessorValue: smH,
});
const seriesKey = getSeriesKey(seriesIdentifier, groupId);
sum += cleanedDatum.y1 ?? 0;
const newDatum = { x, ...cleanedDatum, smH, smV };
const series = dataSeries.get(seriesKey);
if (series) {
series.data.push(newDatum);
} else {
dataSeries.set(seriesKey, {
...seriesIdentifier,
groupId,
seriesType,
stackMode,
isStacked,
seriesKeys,
key: seriesKey,
data: [newDatum],
spec,
insertOrder: insertOrder + dataSeries.size,
sortOrder: 0,
isFiltered: deselectedDataSeries.some(({ key: deselectedKey }) => seriesKey === deselectedKey),
});
}
xValueSums.set(x, sum);
});
}
if (nonNumericValues.size > 0) {
const values = [...nonNumericValues.entries()];
const total = values.reduce((sum, [, v]) => sum + v, 0);
Logger.warn(
`Found ${total} non-numeric y value${total > 1 ? 's' : ''} in dataset for spec "${specId}"`,
`(${values.map(([k, v]) => `${v}: ${JSON.stringify(k)}`).join(', ')})`,
);
}
return {
dataSeries,
xValues,
};
}