# Anomaly detection with Z-score

Anomaly detection enables abnormal metric trends in your system to be detected by analysing historical data. It is also an effective tool for detecting frauds and mitigating risks.

A change in a metric could be benign or harmful. As a result, it is crucial to have a robust anomaly detection system in place that can differentiate between unusual trends and minor changes around the clock and notify relevant parties to investigate the cause of anomalies.

There are many techniques to determine what an anomaly is depending on the monitoring tool that you’re using. In Ascenda, we leverage on Grafana’s interactive visuals coupled with Prometheus’s powerful querying language to build a time series anomaly detection within our system using z-score.

## Demystifying Z-score

Image by Florencia Mangini

Numbers aren’t as intimidating once you put them into words. Z-score uses standard deviation (SD) as a measure to determine how far the value is from the average. The peak of the normal distribution curve represents the average therefore, the further the value is from the average, the more likely it is considered to be an anomaly.

According to the 3-sigma rule that is commonly associated with normal distribution, 99.7% of our data should be within 3 SD. Any other values greater than 3 SD would be considered an anomaly. However, this can be tweaked according to your desired sensitivity.

## Anomaly detection is the way to go, true or false?

### What should you know before building your own anomaly detection with z-score?

Before we dig into its use cases in Ascenda, there’s one caveat to note to determine if anomaly detection using z-score is suitable for your use case: anomaly detection with z-score only works for normal distribution.

How do you know if your data is normally distributed? Likewise, there are numerous techniques that you can use to check if your data is normally distributed. The simplest approach would be to test if your data has a z-score of between +4 to -4 (don’t worry, we’ll get to this in the later section).

A z-score that is excessively large e.g. a range of -15 to +15, signifies that your data has a long tailed distribution where the tail of the curve stretches further in one direction. This means that events occurring further away from the head are rare. To illustrate this further using request response time as an example, we would expect most requests to have a fast response time (represented by the head of the curve) and slow response time should be a rare case (represented by the tail of the curve). Thus, suggesting that threshold alerts would be a better option compared to anomaly detection as we can determine the threshold for a slow response time.

### Does z-score always go hand-in-hand with anomaly detection?

There are other techniques that you can use for anomaly detection such as seasonality. This topic deserves a post on its own where we can have a deeper dive into this but as a little segue into anomaly detection with seasonality, seasonality is suitable for metrics that have predictable data. Notice that the graph below depicts that there’s a similar shape on weekdays and little to no activities on the weekends (7th to 8th May). This denotes that our data has a predictable pattern and seasonality would be an effective technique for anomaly detection for this metric.

## Ascenda’s anomaly detection in action

Next, we’ll take a look at how this can be done.

### A peek into z-score with Grafana x Prometheus

Let’s walk through the use case of detecting anomalies in password reset failure as an abnormal amount of password reset failure could signify suspicious activity.

Here, we’ve tested with 1 weeks’ worth of data to determine if our data is normally distributed. Since it’s between +4 to -4, our dataset is suitable for anomaly detection using z-score.

``````(max_over_time(Web_Controllers_PasswordResets_Create_Failed{environment="production"}[1w]))
) /
``````

Let’s check for the number of failed password resets over the past 5 minutes in comparison to the last 12 hours and set an alert when our z-score exceeds 3 in accordance to the 3-sigma rule. From 7am onwards, notice that our z-score has exceeded 3.

``````(avg_over_time(Web_Controllers_PasswordResets_Create_Failed{environment="production"}[5m]))
) /
``````

However, if we look at the datapoint from 7pm the previous day to 7am the next day in the image below, the spike from 6am onwards would cause a false positive because the data spanned across the last 12h wasn’t sufficient to detect abnormal trend accurately since activity is minimal during the night.

``````avg(increase(Web_Controllers_PasswordResets_Create_Failed{environment="production"}[5m]))
``````

To reduce such cases of false positives, we modify our parameters to compare it with 1 week worth of data. Notice that the graph becomes smoother and the false positive is avoided.

``````(avg_over_time(Web_Controllers_PasswordResets_Create_Failed{environment="production"}[5m]))
)