Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions src/Microsoft.ML.TimeSeries/SrCnnEntireAnomalyDetector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -496,28 +496,32 @@ public void Train(double[] values, ref double[][] results)
_minimumOriginValue = Double.MaxValue;
_maximumOriginValue = Double.MinValue;

var sum = 0.0;
var squareSum = 0.0;

Array.Resize(ref _seriesToDetect, values.Length);
for (int i = 0; i < values.Length; ++i)
{
var value = values[i];
_seriesToDetect[i] = value;
_minimumOriginValue = Math.Min(_minimumOriginValue, value);
_maximumOriginValue = Math.Max(_maximumOriginValue, value);
sum += value;
squareSum += value * value;
}

_mean = sum / values.Length;
_std = Math.Sqrt((squareSum - (sum * sum) / values.Length) / values.Length);

if (_period > 0)
{
_deseasonalityFunction.Deseasonality(ref values, _period, ref _seriesToDetect);
}

var sum = 0.0;
var squareSum = 0.0;
for (int i = 0; i < values.Length; ++i)
{
var value = _seriesToDetect[i];
sum += value;
squareSum += value * value;
}

_mean = sum / values.Length;
_std = Math.Sqrt((squareSum - (sum * sum) / values.Length) / values.Length);

SpectralResidual(_seriesToDetect, results, _threshold);

//Optional Steps
Expand Down Expand Up @@ -812,7 +816,7 @@ private void GetMarginPeriod(double[] values, double[][] results, IReadOnlyList<
//Step 11: Update Anomaly Score, Expected Value and Boundaries
for (int i = 0; i < results.Length; ++i)
{
results[i][1] = CalculateAnomalyScore(values[i], _ifftRe[i], _units[i], results[i][0] > 0);
results[i][1] = CalculateAnomalyScore(values[i], results[i][3], _units[i], results[i][0] > 0);

// adjust the expected value if the point is not anomaly
if (results[i][0] == 0)
Expand Down
60 changes: 60 additions & 0 deletions test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -772,6 +772,66 @@ public void TestSrCnnAnomalyDetectorBigSpike(

}

private sealed class PhoneCallsData
{
#pragma warning disable CS0649
[LoadColumn(0)]
public string timestamp;

[LoadColumn(1)]
public double value;
#pragma warning restore CS0649
}

private sealed class PhoneCallsPrediction
{
[VectorType(7)]
public double[] Prediction { get; set; }
}

[NativeDependencyFact("MklImports")]
public void TestSrCnnAnomalyDetectorPhoneCalls()
{
var mlContext = new MLContext(1);

var dataPath = GetDataPath("Timeseries", "phone-calls.csv");

IDataView dataView = mlContext.Data.LoadFromTextFile<PhoneCallsData>(path: dataPath, hasHeader: true, separatorChar: ',');

int period = mlContext.AnomalyDetection.DetectSeasonality(dataView, nameof(PhoneCallsData.value));
Assert.Equal(7, period);

var options = new SrCnnEntireAnomalyDetectorOptions()
{
Threshold = 0.3,
Sensitivity = 87.0,
DetectMode = SrCnnDetectMode.AnomalyAndMargin,
Period = period,
};

var outputDataView = mlContext.AnomalyDetection.DetectEntireAnomalyBySrCnn(dataView, nameof(PhoneCallsPrediction.Prediction), nameof(PhoneCallsData.value), options);

var predictions = mlContext.Data.CreateEnumerable<PhoneCallsPrediction>(
outputDataView, reuseRowObject: false);

var anomalyIndices = new HashSet<int> { 28, 44, 56, 70 };

int k = 0;
foreach (var prediction in predictions)
{
if (anomalyIndices.Contains(k))
{
Assert.Equal(1, prediction.Prediction[0]);
}
else
{
Assert.Equal(0, prediction.Prediction[0]);
}

++k;
}
}

[NativeDependencyTheory("MklImports"), CombinatorialData]
public void TestSrCnnAnomalyDetectorWithSeasonalAnomalyData(
[CombinatorialValues(SrCnnDeseasonalityMode.Stl, SrCnnDeseasonalityMode.Mean, SrCnnDeseasonalityMode.Median)] SrCnnDeseasonalityMode mode
Expand Down
79 changes: 79 additions & 0 deletions test/data/Timeseries/phone-calls.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
timestamp,value
2018/9/3,36.69670857
2018/9/4,35.74160571
2018/9/5,34.11781143
2018/9/6,33.53363571
2018/9/7,29.19957714
2018/9/8,5.18315
2018/9/9,5.324905714
2018/9/10,36.69670857
2018/9/11,35.74160571
2018/9/12,34.11781143
2018/9/13,33.53363571
2018/9/14,29.19957714
2018/9/15,5.18315
2018/9/16,5.324905714
2018/9/17,36.69670857
2018/9/18,35.74160571
2018/9/19,34.11781143
2018/9/20,33.53363571
2018/9/21,29.19957714
2018/9/22,5.18315
2018/9/23,5.324905714
2018/9/24,36.69670857
2018/9/25,35.74160571
2018/9/26,34.11781143
2018/9/27,33.53363571
2018/9/28,29.19957714
2018/9/29,5.18315
2018/9/30,5.324905714
2018/10/1,31.34386429
2018/10/2,36.18100429
2018/10/3,34.49893429
2018/10/4,34.18594143
2018/10/5,29.96867143
2018/10/6,5.240491429
2018/10/7,5.304298571
2018/10/8,37.94839429
2018/10/9,36.3811
2018/10/10,35.76107429
2018/10/11,35.28894143
2018/10/12,31.08465286
2018/10/13,5.931802857
2018/10/14,5.476382857
2018/10/15,36.23001857
2018/10/16,35.51714
2018/10/17,21.95187143
2018/10/18,31.15111714
2018/10/19,28.21996429
2018/10/20,4.532814286
2018/10/21,5.376088571
2018/10/22,36.19748286
2018/10/23,36.05797571
2018/10/24,35.05092286
2018/10/25,34.78529
2018/10/26,31.19532857
2018/10/27,5.655607143
2018/10/28,5.925987143
2018/10/29,26.61867857
2018/10/30,35.35089571
2018/10/31,34.09542571
2018/11/1,28.74181
2018/11/2,28.20479429
2018/11/3,4.849405714
2018/11/4,5.444168571
2018/11/5,33.21586857
2018/11/6,35.69544714
2018/11/7,34.79379714
2018/11/8,34.26969714
2018/11/9,30.07392
2018/11/10,3.18219
2018/11/11,3.964938571
2018/11/12,45.21586857
2018/11/13,35.69544714
2018/11/14,34.79379714
2018/11/15,34.26969714
2018/11/16,30.07392
2018/11/17,5.266295714
2018/11/18,5.386695714
2018/11/19,33.80200857
Loading