Skip to content

Commit 2e68d1f

Browse files
committed
fix: correct off-by-one bug in deepant anomaly detection methods
1 parent 21c4929 commit 2e68d1f

1 file changed

Lines changed: 20 additions & 25 deletions

File tree

src/TimeSeries/AnomalyDetection/DeepANT.cs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -185,29 +185,26 @@ public override T PredictSingle(Vector<T> input)
185185
/// <returns>Boolean array where true indicates an anomaly.</returns>
186186
public bool[] DetectAnomalies(Vector<T> data)
187187
{
188-
if (data.Length < _options.WindowSize)
189-
throw new ArgumentException($"Data length must be at least {_options.WindowSize}");
188+
if (data.Length <= _options.WindowSize)
189+
throw new ArgumentException($"Data length must be greater than {_options.WindowSize}");
190190

191-
bool[] anomalies = new bool[data.Length - _options.WindowSize + 1];
191+
// Each anomaly score corresponds to comparing prediction from window ending at i with actual value at i
192+
int numResults = data.Length - _options.WindowSize;
193+
bool[] anomalies = new bool[numResults];
192194

193-
for (int i = 0; i <= data.Length - _options.WindowSize; i++)
195+
for (int i = 0; i < numResults; i++)
194196
{
195-
// Extract window
197+
// Extract window ending at position i + WindowSize - 1
196198
Vector<T> window = new Vector<T>(_options.WindowSize);
197199
for (int j = 0; j < _options.WindowSize; j++)
198200
window[j] = data[i + j];
199201

200-
// Predict next value
202+
// Predict next value and compare with actual
201203
T prediction = PredictSingle(window);
204+
T actual = data[i + _options.WindowSize];
205+
T error = _numOps.Abs(_numOps.Subtract(actual, prediction));
202206

203-
// Compare with actual next value (if available)
204-
if (i + _options.WindowSize < data.Length)
205-
{
206-
T actual = data[i + _options.WindowSize];
207-
T error = _numOps.Abs(_numOps.Subtract(actual, prediction));
208-
209-
anomalies[i] = _numOps.GreaterThan(error, _anomalyThreshold);
210-
}
207+
anomalies[i] = _numOps.GreaterThan(error, _anomalyThreshold);
211208
}
212209

213210
return anomalies;
@@ -218,25 +215,23 @@ public bool[] DetectAnomalies(Vector<T> data)
218215
/// </summary>
219216
public Vector<T> ComputeAnomalyScores(Vector<T> data)
220217
{
221-
if (data.Length < _options.WindowSize)
222-
throw new ArgumentException($"Data length must be at least {_options.WindowSize}");
218+
if (data.Length <= _options.WindowSize)
219+
throw new ArgumentException($"Data length must be greater than {_options.WindowSize}");
223220

224-
var scores = new Vector<T>(data.Length - _options.WindowSize + 1);
221+
// Each score corresponds to comparing prediction from window ending at i with actual value at i
222+
int numResults = data.Length - _options.WindowSize;
223+
var scores = new Vector<T>(numResults);
225224

226-
for (int i = 0; i <= data.Length - _options.WindowSize; i++)
225+
for (int i = 0; i < numResults; i++)
227226
{
228227
Vector<T> window = new Vector<T>(_options.WindowSize);
229228
for (int j = 0; j < _options.WindowSize; j++)
230229
window[j] = data[i + j];
231230

232231
T prediction = PredictSingle(window);
233-
234-
if (i + _options.WindowSize < data.Length)
235-
{
236-
T actual = data[i + _options.WindowSize];
237-
T error = _numOps.Abs(_numOps.Subtract(actual, prediction));
238-
scores[i] = error;
239-
}
232+
T actual = data[i + _options.WindowSize];
233+
T error = _numOps.Abs(_numOps.Subtract(actual, prediction));
234+
scores[i] = error;
240235
}
241236

242237
return scores;

0 commit comments

Comments
 (0)