Skip to content

Commit

Permalink
fix: fixed integral calculation (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
zefirka authored Mar 27, 2024
1 parent a2194f1 commit 888b483
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 38 deletions.
26 changes: 13 additions & 13 deletions demo/examples/tooltip-with-aggregates.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ <h1>Tooltip Plugin</h1>
},
timeline: new Array(100).fill().map((_, i) => i * 1000),
series: [
{data: new Array(100).fill().map((_, i) => Math.random() * 6), color: 'red'},
{data: new Array(100).fill().map((_, i) => i > 20 && i < 30 ? Math.random() * 6 : null), color: 'red'},
{data: new Array(100).fill().map((_, i) => Math.random() * 6), color: 'green'},
],
chart: {
Expand Down Expand Up @@ -112,23 +112,23 @@ <h1>Tooltip Plugin</h1>
return `
<tr>
<td>${id}</td>
<td>${ref.min.toFixed(1) ?? '-'}</td>
<td>${ref.max.toFixed(1) ?? '-'}</td>
<td>${ref.avg.toFixed(1) ?? '-'}</td>
<td>${ref.sum.toFixed(1) ?? '-'}</td>
<td>${ref.count.toFixed(1) ?? '-'}</td>
<td>${ref.integral.toFixed(1) ?? '-'}</td>
<td>${ref.min?.toFixed(1) ?? '-'}</td>
<td>${ref.max?.toFixed(1) ?? '-'}</td>
<td>${ref.avg?.toFixed(1) ?? '-'}</td>
<td>${ref.sum?.toFixed(1) ?? '-'}</td>
<td>${ref.count?.toFixed(1) ?? '-'}</td>
<td>${ref.integral?.toFixed(1) ?? '-'}</td>
<td>${ref.last?.toFixed(1) ?? '-'}</td>
</tr>`;
})}
<tr>
<td>Total</td>
<td>${refs.y.total.min.toFixed(1)}</td>
<td>${refs.y.total.max.toFixed(1)}</td>
<td>${refs.y.total.avg.toFixed(1)}</td>
<td>${refs.y.total.sum.toFixed(1)}</td>
<td>${refs.y.total.count.toFixed(1)}</td>
<td>${refs.y.total.integral.toFixed(1)}</td>
<td>${refs.y.total.min?.toFixed(1)}</td>
<td>${refs.y.total.max?.toFixed(1)}</td>
<td>${refs.y.total.avg?.toFixed(1)}</td>
<td>${refs.y.total.sum?.toFixed(1)}</td>
<td>${refs.y.total.count?.toFixed(1)}</td>
<td>${refs.y.total.integral?.toFixed(1)}</td>
<td>${refs.y.total.last ?? '-'}</td>
</tbody>
</table>`;
Expand Down
1 change: 1 addition & 0 deletions src/YagrCore/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,4 @@ export default class ThemedDefaults {
export const TOOLTIP_Y_OFFSET = 24;
export const TOOLTIP_X_OFFSET = 24;
export const TOOLTIP_DEFAULT_MAX_LINES = 10;
export const TIME_MULTIPLIER = 1;
5 changes: 3 additions & 2 deletions src/YagrCore/mixins/create-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
DEFAULT_X_SCALE,
DEFAULT_X_SERIE_NAME,
MIN_SELECTION_WIDTH,
TIME_MULTIPLIER,
} from '../defaults';
import {configureSeries} from '../utils/series';
import markersPlugin from '../plugins/markers';
Expand Down Expand Up @@ -72,7 +73,7 @@ export class CreateUplotOptionsMixin<T extends MinimalValidConfig> {
this._uHooks.setSelect = (u: uPlot) => {
const {left, width} = u.select;
const [_from, _to] = [u.posToVal(left, DEFAULT_X_SCALE), u.posToVal(left + width, DEFAULT_X_SCALE)];
const {timeMultiplier = 1} = this.config.chart || {};
const {timeMultiplier = TIME_MULTIPLIER} = this.config.chart || {};

this.execHooks('onSelect', {
from: Math.ceil(_from / timeMultiplier),
Expand Down Expand Up @@ -136,7 +137,7 @@ export class CreateUplotOptionsMixin<T extends MinimalValidConfig> {
count: config.timeline.length,
} as Series,
],
ms: chart.timeMultiplier || 1,
ms: chart.timeMultiplier || TIME_MULTIPLIER,
hooks: config.hooks || {},
};

Expand Down
4 changes: 2 additions & 2 deletions src/YagrCore/utils/axes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ function getTimeFormatterByRange(range: number, ticksCount: number) {
}

export const getTimeFormatter = (config: YagrConfig) => {
const msm = config.chart.timeMultiplier || 1;
const msm = config.chart.timeMultiplier || defaults.TIME_MULTIPLIER;
return (_: unknown, ticks: number[]) => {
const range = ticks[ticks.length - 1] - ticks[0];
const rangeMs = range / msm;
Expand Down Expand Up @@ -145,7 +145,7 @@ function getAxis(axisConfig: AxisOptions, yagr: Yagr): Axis {
ticks: axisConfig.ticks ? {...theme.X_AXIS_TICKS, ...axisConfig.ticks} : theme.X_AXIS_TICKS,
scale: defaults.DEFAULT_X_SCALE,
space: axisConfig.space || (() => defaults.X_AXIS_SPACE),
incrs: axisConfig.incrs || (() => defaults.X_AXIS_INCRS.map((i) => i * (config.chart.timeMultiplier || 1))),
incrs: axisConfig.incrs || (() => defaults.X_AXIS_INCRS.map((i) => i * (config.chart.timeMultiplier || defaults.TIME_MULTIPLIER))),
side: 2,
stroke: axisConfig.stroke || (() => theme.AXIS_STROKE),
});
Expand Down
3 changes: 1 addition & 2 deletions src/plugins/aggregates/aggregates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const DataRef = (opst: AggregatesPluginOptions) => {
const seriesIdx = yagr.state.y2uIdx[seriesId];
const timestamps = yagr.uplot.data[0].slice(fromIdx, toIdx + 1) as number[];
const values = yagr.uplot.series[seriesIdx].$c;
const integral = integrate(timestamps, values, yagr.config.chart?.timeMultiplier);
const integral = integrate(timestamps, values.slice(fromIdx, toIdx + 1));
const sum = safeSum(values, fromIdx, toIdx);
const min = safeMin(values, fromIdx, toIdx);
const max = safeMax(values, fromIdx, toIdx);
Expand Down Expand Up @@ -136,7 +136,6 @@ const DataRef = (opst: AggregatesPluginOptions) => {
const rowIntegral = integrate(
u.data[0] as number[],
$c as DataSeriesExtended,
yagr.config.chart?.timeMultiplier,
);
aggrs[scale].integral = integral === null ? rowIntegral : integral + rowIntegral;
});
Expand Down
6 changes: 4 additions & 2 deletions src/plugins/aggregates/utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import {DataSeriesExtended} from '../../types';

export function integrate(timestamps: number[], values: DataSeriesExtended, timeMultiplier = 0.001) {
export function integrate(timestamps: number[], values: DataSeriesExtended) {
if (timestamps.length < 2) {
return 0;
}

let t0 = timestamps[0];
let x0 = Number(values[0]);
let t1: number;
let x1: number;
let integral = 0;

for (let i = 1; i < timestamps.length; i++) {
x1 = Number(values[i]);
t1 = timestamps[i];
Expand All @@ -19,7 +21,7 @@ export function integrate(timestamps: number[], values: DataSeriesExtended, time
if (!Number.isNaN(x1) && !Number.isNaN(x0)) {
const dt = t1 - t0;
const dx = x1 - x0;
const area = (x0 + dx / 2) * dt * timeMultiplier; // convert milliseconds to seconds
const area = (x0 + dx / 2) * dt;
integral += area;
}

Expand Down
3 changes: 2 additions & 1 deletion src/plugins/weekends/weekends.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type {YagrPlugin} from '../../types';
import type Yagr from '../../index';
import {TIME_MULTIPLIER} from '../../YagrCore/defaults';

export interface WeekendsPluginOptions {
color?: string;
Expand Down Expand Up @@ -38,7 +39,7 @@ export default function WeekendsPlugin({
if (predicate) {
isWeekend = predicate(ts);
} else {
const date = new Date(ts / (yagr.config.chart?.timeMultiplier || 1));
const date = new Date(ts / (yagr.config.chart?.timeMultiplier || TIME_MULTIPLIER));
const dayOfWeek = date.getDay();
isWeekend = dayOfWeek === 6 || dayOfWeek === 0;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/core/plugins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('yagr plugins', () => {
min: 1,
sum: 6,
last: null,
integral: 0.004,
integral: 4,
},
});
});
Expand Down
30 changes: 15 additions & 15 deletions tests/plugins/aggregates.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ describe('Aggregates plugin', () => {
it('should calc refs for series perScale', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(y.plugins.aggr?.get()).toEqual({
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},
y: {min: 1, max: 4, count: 8, last: null, avg: 2.75, sum: 22, integral: 16.5},
r: {min: 5, max: 8, count: 4, last: null, avg: 6.5, sum: 26, integral: 19.5},
});
});

Expand All @@ -32,7 +32,7 @@ describe('Aggregates plugin', () => {
avg: 1.5,
last: 2,
sum: 3,
integral: 0.0015,
integral: 1.5,
});
});

Expand All @@ -47,13 +47,13 @@ describe('Aggregates plugin', () => {
count: 2,
avg: 1.5,
sum: 3,
integral: 0.0015,
integral: 1.5,
last: 2,
},
two: {
avg: 3,
count: 2,
integral: 0.003,
integral: 3,
last: 3,
max: 3,
min: 3,
Expand All @@ -63,7 +63,7 @@ describe('Aggregates plugin', () => {
total: {
avg: 2.25,
count: 4,
integral: 0.0045000000000000005,
integral: 4.5,
last: null,
max: 3,
min: 1,
Expand All @@ -75,7 +75,7 @@ describe('Aggregates plugin', () => {
three: {
avg: 5.5,
count: 2,
integral: 0.0055,
integral: 5.5,
last: 6,
max: 6,
min: 5,
Expand All @@ -85,7 +85,7 @@ describe('Aggregates plugin', () => {
total: {
avg: 5.5,
count: 2,
integral: 0.0055,
integral: 5.5,
last: null,
max: 6,
min: 5,
Expand Down Expand Up @@ -114,7 +114,7 @@ describe('Aggregates plugin', () => {
it('should calc refs for series perScale', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(y.plugins.aggr?.get()).toEqual({
y: {min: 1, max: 4, count: 8, last: null, avg: 2.75, sum: 22, integral: 0.0165},
y: {min: 1, max: 4, count: 8, last: null, avg: 2.75, sum: 22, integral: 16.5},
});
});

Expand All @@ -127,7 +127,7 @@ describe('Aggregates plugin', () => {
avg: 1.5,
last: 2,
sum: 3,
integral: 0.0015,
integral: 1.5,
});
expect(y.plugins.aggr?.calc(0, 1, 'two')).toEqual({
min: 3,
Expand All @@ -136,7 +136,7 @@ describe('Aggregates plugin', () => {
avg: 3,
last: 3,
sum: 6,
integral: 0.003,
integral: 3,
});
});
});
Expand All @@ -160,7 +160,7 @@ describe('Aggregates plugin', () => {
it('should calc refs for series perScale', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(y.plugins.aggr?.get()).toEqual({
y: {min: 1, max: 4, count: 4, last: null, avg: 2.75, sum: 11, integral: 0.006999999999999999},
y: {min: 1, max: 4, count: 4, last: null, avg: 2.75, sum: 11, integral: 7},
});
});

Expand All @@ -173,7 +173,7 @@ describe('Aggregates plugin', () => {
avg: 1,
last: 1,
sum: 1,
integral: 0.0005,
integral: 0.5,
});
expect(y.plugins.aggr?.calc(0, 1, 'two')).toEqual({
min: null,
Expand Down Expand Up @@ -211,7 +211,7 @@ describe('Aggregates plugin', () => {
it('should calc refs for series perScale', async () => {
await new Promise((resolve) => setTimeout(resolve, 100));
expect(y.plugins.aggr?.get()).toEqual({
y: {min: 1, max: 4, count: 5, last: null, avg: 2.6, sum: 13, integral: 0.005},
y: {min: 1, max: 4, count: 5, last: null, avg: 2.6, sum: 13, integral: 5},
});
});

Expand All @@ -233,7 +233,7 @@ describe('Aggregates plugin', () => {
avg: 3,
last: 3,
sum: 3,
integral: 0,
integral: 1.5,
});
});
});
Expand Down

0 comments on commit 888b483

Please sign in to comment.