Skip to content

Commit

Permalink
fix: updated data refs notations (#90)
Browse files Browse the repository at this point in the history
* fix: updated data refs notations
* fix(data-refs): fixed sum of integrals on scale
  • Loading branch information
zefirka authored Jul 11, 2023
1 parent 66d9cad commit 79437df
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 14 deletions.
58 changes: 52 additions & 6 deletions demo/examples/tooltip-with-datarefs.html
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ <h1>Tooltip Plugin</h1>
text: 'By scale, constant',
},
timeline: new Array(20).fill().map((_, i) => i * 1000),
series: [{data: new Array(20).fill().map((_, i) => Math.random() * 6), color: 'red'}],
series: [
{data: new Array(20).fill().map((_, i) => Math.random() * 6), color: 'red'},
{data: new Array(20).fill().map((_, i) => Math.random() * 6), color: 'green'},
],
chart: {
select: {zoom: false},
},
Expand Down Expand Up @@ -79,11 +82,54 @@ <h1>Tooltip Plugin</h1>
return r;
}

y1.options.series.forEach((s) => {
if (s.id === 'date') return;
refs = y1.plugins.refs.calcRefs(range[0].idx, range[1].idx, s.id);
appendix += `<pre>${s.id}-refs: ${JSON.stringify(refs, null, 2)}\n<pre>`;
});
let rng = [range[0], range[1]];

if (range[0].idx >= range[1].idx) {
rng[0] = range[1];
rng[1] = range[0];
}

refs = y1.plugins.refs.getRefs(rng[0].idx, rng[1].idx);
console.log('refs', refs);
appendix += `
<table>
<thead>
<tr>
<th>Id</th>
<th>Min</th>
<th>Max</th>
<th>Avg</th>
<th>Sum</th>
<th>Count</th>
<th>Integral</th>
<th>Last</th>
</tr>
</thead>
<tbody>
${Object.entries(refs.y.series).map(([id, ref]) => {
return `
<tr>
<td>${id}</td>
<td>${ref.min.toFixed(2) ?? '-'}</td>
<td>${ref.max.toFixed(2) ?? '-'}</td>
<td>${ref.avg.toFixed(2) ?? '-'}</td>
<td>${ref.sum.toFixed(2) ?? '-'}</td>
<td>${ref.count.toFixed(2) ?? '-'}</td>
<td>${ref.integral.toFixed(2) ?? '-'}</td>
<td>${ref.last ?? '-'}</td>
</tr>`;
})}
<tr>
<td>Total</td>
<td>${refs.y.scaleRefs.min.toFixed(2)}</td>
<td>${refs.y.scaleRefs.max.toFixed(2)}</td>
<td>${refs.y.scaleRefs.avg.toFixed(2)}</td>
<td>${refs.y.scaleRefs.sum.toFixed(2)}</td>
<td>${refs.y.scaleRefs.count.toFixed(2)}</td>
<td>${refs.y.scaleRefs.integral.toFixed(2)}</td>
<td>${refs.y.scaleRefs.last ?? '-'}</td>
</tbody>
</table>`;
}

return r + '\n' + appendix + '\n';
Expand Down
81 changes: 76 additions & 5 deletions src/plugins/dataRefs/dataRefs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,23 @@ import uPlot, {Series} from 'uplot';
import {DEFAULT_X_SCALE} from '../../YagrCore/defaults';
import type Yagr from '../../YagrCore/index';

export type DataRefs = {min: number; max: number; sum: number; avg: number; count: number; integral: number};
export type DataRefs = {
min: number;
max: number;
sum: number;
avg: number;
count: number;
integral: number;
last: number | null;
};
export type DataRefsPerScale = Record<string, DataRefs>;
export type DataRefsPerSeries = Record<
string,
{
series: Record<string, DataRefs>;
total: DataRefs;
}
>;

export type DataRefsPluginOptions = {
/** Should calc ref points on ready event (true by default) */
Expand Down Expand Up @@ -41,15 +56,67 @@ export function integrate(timestamps: number[], values: DataSeriesExtended) {
return integral;
}

function getLast(values: DataSeriesExtended): number | null {
for (let i = values.length - 1; i >= 0; i--) {
const val = values[i];
if (val !== null && typeof val === 'number') {
return val;
}
}
return null;
}

const DataRef = (opst: DataRefsPluginOptions) => {
const plugin: YagrPlugin<{
getRefs: () => DataRefsPerScale;
getRefs: (from?: number, to?: number) => DataRefsPerScale | DataRefsPerSeries;
calcRefs: (from: number, to: number, id: string) => DataRefs;
}> = (_: Yagr) => {
const refs: DataRefsPerScale = {};

return {
getRefs: () => refs,
const pluginMethods = {
getRefs: (fromIdx?: number, toIdx?: number) => {
if (fromIdx === undefined && toIdx === undefined) {
return refs;
}

if (fromIdx === undefined) {
fromIdx = 0;
}

if (toIdx === undefined) {
toIdx = _.uplot.data[0].length - 1;
}

const result: DataRefsPerSeries = {};

_.uplot.series.forEach(({scale, id}) => {
if (scale === DEFAULT_X_SCALE || !scale) {
return;
}
result[scale] = result[scale] || {};
result[scale].series = result[scale].series || {};
result[scale].series[id] = pluginMethods.calcRefs(fromIdx as number, toIdx as number, id);
});

Object.keys(result).forEach((scale) => {
const total: DataRefs = {
min: Object.values(result[scale].series).reduce((acc, {min}) => Math.min(acc, min), Infinity),
max: Object.values(result[scale].series).reduce((acc, {max}) => Math.max(acc, max), -Infinity),
sum: Object.values(result[scale].series).reduce((acc, {sum}) => acc + sum, 0),
avg: 0,
count: Object.values(result[scale].series).reduce((acc, {count}) => acc + count, 0),
integral: Object.values(result[scale].series).reduce((acc, {integral}) => acc + integral, 0),
last: 0,
};

total.avg = total.sum / total.count;
total.last = null;

result[scale].total = total;
});

return result;
},
calcRefs: (fromIdx: number, toIdx: number, seriesId: string) => {
const seriesIdx = _.state.y2uIdx[seriesId];
const timestamps = _.uplot.data[0].slice(fromIdx, toIdx + 1) as number[];
Expand All @@ -60,8 +127,9 @@ const DataRef = (opst: DataRefsPluginOptions) => {
const max = Math.max(...(values.filter((v) => v !== null) as number[]));
const count = values.filter((v) => v !== null).length;
const avg = sum / count;
const last = getLast(values);

return {min, max, sum, avg, count, integral};
return {min, max, sum, avg, count, integral, last};
},

uplot: {
Expand All @@ -81,6 +149,7 @@ const DataRef = (opst: DataRefsPluginOptions) => {
avg: 0,
integral: 0,
count: 0,
last: null,
};
});

Expand All @@ -105,6 +174,8 @@ const DataRef = (opst: DataRefsPluginOptions) => {
},
},
};

return pluginMethods;
};
return plugin;
};
Expand Down
1 change: 1 addition & 0 deletions tests/core/plugins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ describe('yagr plugins', () => {
max: 3,
min: 1,
sum: 6,
last: null,
integral: 0.004,
},
});
Expand Down
67 changes: 64 additions & 3 deletions tests/plugins/dataRefs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ describe('DataRefs plugin', () => {
timeline: [1, 2, 3, 4],
series: [
{data: [1, 2, 3, 4], scale: 'y', id: 'one'},
{data: [5, 6, 7, 8], scale: 'r'},
{data: [3, 3, 3, 3], scale: 'y', id: 'two'},
{data: [5, 6, 7, 8], scale: 'r', id: 'three'},
],
plugins: {
refs: DataRefs({}),
Expand All @@ -16,8 +17,8 @@ describe('DataRefs plugin', () => {
it('should calc refs for series perScale', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(y.plugins.refs?.getRefs()).toEqual({
y: {min: 1, max: 4, count: 4, avg: 2.5, sum: 10, integral: 0.0075},
r: {min: 5, max: 8, count: 4, avg: 6.5, sum: 26, integral: 0.0195},
y: {min: 1, max: 4, count: 8, last: null, avg: 2.75, sum: 22, integral: 0.0165},
r: {min: 5, max: 8, count: 4, last: null, avg: 6.5, sum: 26, integral: 0.0195},
});
});

Expand All @@ -28,8 +29,68 @@ describe('DataRefs plugin', () => {
max: 2,
count: 2,
avg: 1.5,
last: 2,
sum: 3,
integral: 0.0015,
});
});

it('should calc refs by ranges', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(y.plugins.refs?.getRefs(0, 1)).toEqual({
y: {
series: {
one: {
min: 1,
max: 2,
count: 2,
avg: 1.5,
sum: 3,
integral: 0.0015,
last: 2,
},
two: {
avg: 3,
count: 2,
integral: 0.003,
last: 3,
max: 3,
min: 3,
sum: 6,
},
},
total: {
avg: 2.25,
count: 4,
integral: 0.0045000000000000005,
last: null,
max: 3,
min: 1,
sum: 9,
},
},
r: {
series: {
three: {
avg: 5.5,
count: 2,
integral: 0.0055,
last: 6,
max: 6,
min: 5,
sum: 11,
},
},
total: {
avg: 5.5,
count: 2,
integral: 0.0055,
last: null,
max: 6,
min: 5,
sum: 11,
},
},
});
});
});

0 comments on commit 79437df

Please sign in to comment.