Skip to content

Commit ec2a012

Browse files
authored
Merge pull request #225 from labmlai/smooth
Smooth
2 parents 6ab8dfa + 9add6b9 commit ec2a012

File tree

2 files changed

+87
-95
lines changed
  • app/ui/src

2 files changed

+87
-95
lines changed

app/ui/src/analyses/experiments/chart_wrapper/view.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export class ViewWrapper {
239239
let changeHandler = new ChangeHandlers.TrimSmoothToggleHandler(this)
240240
changeHandler.change()
241241
},
242-
text: 'Trim Smooth Ends',
242+
text: 'Clip',
243243
isToggled: this.dataStore.trimSmoothEnds,
244244
parent: this.constructor.name
245245
})

app/ui/src/components/charts/utils.ts

+86-94
Original file line numberDiff line numberDiff line change
@@ -93,36 +93,6 @@ export function toDate(time: number) {
9393
return new Date(time * 1000)
9494
}
9595

96-
function smoothSeries(series: PointValue[], windowSize: number): PointValue[] {
97-
let result: PointValue[] = []
98-
windowSize = ~~windowSize
99-
if (series.length < windowSize) {
100-
windowSize = series.length
101-
}
102-
let extraWindow = windowSize / 2
103-
extraWindow = ~~extraWindow
104-
105-
106-
let count = 0
107-
let total = 0
108-
109-
for (let i = 0; i < series.length + extraWindow; i++) {
110-
let j = i - extraWindow
111-
if (i < series.length) {
112-
total += series[i].value
113-
count++
114-
}
115-
if (j - extraWindow - 1 >= 0) {
116-
total -= series[j - extraWindow - 1].value
117-
count--
118-
}
119-
if (j>=0) {
120-
result.push({step: series[j].step, value: series[j].value, smoothed: total / count, lastStep: series[j].lastStep})
121-
}
122-
}
123-
return result
124-
}
125-
12696
export function fillPlotPreferences(series: Indicator[], currentPlotIdx: number[] = []) {
12797
if (currentPlotIdx.length != 0) {
12898
if (currentPlotIdx.length == series.length) {
@@ -213,57 +183,6 @@ export function smoothAndTrimAllCharts(series: Indicator[], baseSeries: Indicato
213183
}
214184
}
215185

216-
function trimSteps(series: Indicator[], min: number, max: number, smoothWindow: number[], trimSmoothEnds: boolean = true) {
217-
series.forEach((s, i) => {
218-
let localSmoothWindow = smoothWindow[i] / 2 // remove half from each end
219-
localSmoothWindow = Math.floor(localSmoothWindow)
220-
if (localSmoothWindow < 0) {
221-
localSmoothWindow = 0
222-
}
223-
if (s.series.length <= 1) {
224-
localSmoothWindow = 0
225-
} else if (smoothWindow[i] >= s.series.length) {
226-
localSmoothWindow = Math.floor(s.series.length/2)
227-
}
228-
229-
230-
let localMin = min
231-
let localMax = max
232-
233-
if (localMin == -1) {
234-
localMin = s.series[0].step
235-
}
236-
if (trimSmoothEnds) {
237-
localMin = Math.max(localMin, s.series[localSmoothWindow].step)
238-
}
239-
240-
if (localMax == -1) {
241-
localMax = s.series[s.series.length - 1].step
242-
}
243-
if (trimSmoothEnds) {
244-
localMax = Math.min(localMax, s.series[s.series.length - 1 - localSmoothWindow +
245-
(s.series.length%2 == 0 && localSmoothWindow != 0 ? 1 : 0)].step) // get the mid value for even length series
246-
}
247-
248-
localMin = Math.floor(localMin-1)
249-
localMax = Math.ceil(localMax+1)
250-
251-
let minIndex = s.series.length - 1
252-
let maxIndex = 0
253-
254-
for (let i = 0; i < s.series.length; i++) {
255-
let p = s.series[i]
256-
if (p.step >= localMin && p.step <= localMax) {
257-
minIndex = Math.min(i, minIndex)
258-
maxIndex = Math.max(i, maxIndex)
259-
}
260-
}
261-
262-
s.lowTrimIndex = minIndex
263-
s.highTrimIndex = maxIndex
264-
})
265-
}
266-
267186
/**
268187
* Calculates the smooth window size for each series in the current and base series.
269188
* The smooth window size is determined based on the minimum range of steps in the series and the provided smooth value.
@@ -274,7 +193,7 @@ function trimSteps(series: Indicator[], min: number, max: number, smoothWindow:
274193
* @returns {[number[][], number]} - Returns an array of smooth window sizes for each series. and the smooth window size in steps.
275194
* (ret[0] = smooth window for current series, ret[1] = smooth window for base series
276195
*/
277-
export function getSmoothWindow(currentSeries: Indicator[], baseSeries: Indicator[], smoothValue: number): [number[][], number] {
196+
function getSmoothWindow(currentSeries: Indicator[], baseSeries: Indicator[], smoothValue: number): [number[][], number] {
278197
let maxRange: number = Number.MIN_SAFE_INTEGER
279198
for (let s of currentSeries) {
280199
if (s.series.length > 1 && !s.is_summary) {
@@ -286,7 +205,7 @@ export function getSmoothWindow(currentSeries: Indicator[], baseSeries: Indicato
286205
maxRange = Math.max(maxRange, s.series[s.series.length - 1].step - s.series[0].step)
287206
}
288207
}
289-
if (maxRange == Number.MIN_SAFE_INTEGER) {
208+
if (maxRange == Number.MIN_SAFE_INTEGER) { // all single points. -> can't smooth
290209
let stepRange = [[],[]]
291210
for (let s of currentSeries) {
292211
stepRange[0].push(1)
@@ -297,35 +216,108 @@ export function getSmoothWindow(currentSeries: Indicator[], baseSeries: Indicato
297216
return [stepRange, 0]
298217
}
299218

300-
let smoothRange = mapRange(smoothValue, 1, 100, 1, maxRange)
219+
let smoothRange = mapRange(smoothValue, 1, 100, 1, 2*maxRange)
301220

302221
let stepRange = [[],[]]
222+
303223
for (let s of currentSeries) {
304-
if (smoothValue == 100) { // hardcode to max range in case not range due to step inconsistencies
305-
stepRange[0].push(s.series.length)
306-
} else if (s.series.length >= 2 && !s.is_summary) {
224+
if (s.series.length >= 2 && !s.is_summary) {
307225
let stepGap = s.series[1].step - s.series[0].step
308-
let numSteps = Math.max(1, Math.floor(smoothRange / stepGap))
226+
let numSteps = Math.max(1, Math.ceil(smoothRange / stepGap))
309227
stepRange[0].push(numSteps)
310-
} else {
228+
} else { // can't smooth - just a single point
311229
stepRange[0].push(1)
312230
}
313231
}
232+
314233
for (let s of baseSeries) {
315-
if (smoothValue == 100) {
316-
stepRange[1].push(s.series.length)
317-
} else if (s.series.length >= 2 && !s.is_summary) {
234+
if (s.series.length >= 2 && !s.is_summary) {
318235
let stepGap = s.series[1].step - s.series[0].step
319-
let numSteps = Math.max(1, Math.floor(smoothRange / stepGap))
236+
let numSteps = Math.max(1, Math.ceil(smoothRange / stepGap))
320237
stepRange[1].push(numSteps)
321-
} else {
238+
} else { // can't smooth - just a single point
322239
stepRange[1].push(1)
323240
}
324241
}
325242

326243
return [stepRange, smoothRange]
327244
}
328245

246+
function smoothSeries(series: PointValue[], windowSize: number): PointValue[] {
247+
let result: PointValue[] = []
248+
windowSize = ~~windowSize
249+
let extraWindow = windowSize / 2
250+
extraWindow = ~~extraWindow
251+
252+
253+
let count = 0
254+
let total = 0
255+
256+
for (let i = 0; i < series.length + extraWindow; i++) {
257+
let j = i - extraWindow
258+
if (i < series.length) {
259+
total += series[i].value
260+
count++
261+
}
262+
if (j - extraWindow - 1 >= 0) {
263+
total -= series[j - extraWindow - 1].value
264+
count--
265+
}
266+
if (j>=0) {
267+
result.push({step: series[j].step, value: series[j].value, smoothed: total / count, lastStep: series[j].lastStep})
268+
}
269+
}
270+
return result
271+
}
272+
273+
274+
function trimSteps(series: Indicator[], min: number, max: number, smoothWindow: number[], trimSmoothEnds: boolean = true) {
275+
series.forEach((s, i) => {
276+
let localSmoothWindow = Math.floor(smoothWindow[i] / 2) // remove half from each end
277+
278+
if (s.series.length <= 1) {
279+
localSmoothWindow = 0
280+
} else if (smoothWindow[i] >= s.series.length) {
281+
localSmoothWindow = Math.floor(s.series.length/2)
282+
}
283+
284+
let localMin = min
285+
let localMax = max
286+
287+
if (localMin == -1) {
288+
localMin = s.series[0].step
289+
}
290+
if (trimSmoothEnds) {
291+
localMin = Math.max(localMin, s.series[localSmoothWindow].step)
292+
}
293+
294+
if (localMax == -1) {
295+
localMax = s.series[s.series.length - 1].step
296+
}
297+
if (trimSmoothEnds) {
298+
localMax = Math.min(localMax, s.series[s.series.length - 1 - localSmoothWindow +
299+
(s.series.length%2 == 0 && localSmoothWindow != 0 ? 1 : 0)].step) // get the mid value for even length series
300+
}
301+
302+
localMin = Math.floor(localMin) - 0.5
303+
localMax = Math.ceil(localMax) + 0.5
304+
305+
let minIndex = s.series.length - 1
306+
let maxIndex = 0
307+
308+
for (let i = 0; i < s.series.length; i++) {
309+
let p = s.series[i]
310+
if (p.step >= localMin && p.step <= localMax) {
311+
minIndex = Math.min(i, minIndex)
312+
maxIndex = Math.max(i, maxIndex)
313+
}
314+
}
315+
316+
s.lowTrimIndex = minIndex
317+
s.highTrimIndex = maxIndex
318+
})
319+
}
320+
329321
// Default smoothing function from backend
330322
function meanAngle(smoothed: PointValue[], aspectRatio: number) {
331323
let xRange = smoothed[smoothed.length - 1].lastStep - smoothed[0].lastStep

0 commit comments

Comments
 (0)