Lets illustrate the approach proposed in the other answer with an simple example
Get Data
We will simulate tha data with seven chunks produced with normal distribution with different means.
This is important as it allow us to cleanly distinct between the groups and to simple detect the breaking points. This answer use elementary threshold approach, some more advanced way might be required for your real data.
dt <- rbind(
data.frame(color=1, x = round(runif(50, min = 0, max = 50)), y = rnorm (50,mean=3.9, sd=.03)),
data.frame(color=2, x = round(runif(15, min = 50, max = 65)), y = rnorm (15,mean=4.5, sd=.03)),
data.frame(color=2, x = round(runif(15, min = 65, max = 80)), y = rnorm (15,mean=3.3, sd=.03)),
data.frame(color=1, x = round(runif(70, min = 80, max = 150)), y = rnorm (70,mean=3.9, sd=.03)),
data.frame(color=2, x = round(runif(15, min = 150, max = 165)), y = rnorm (15,mean=3.3, sd=.03)),
data.frame(color=3, x = round(runif(15, min = 165, max = 180)), y = rnorm (15,mean=2.9, sd=.03)),
data.frame(color=1, x = round(runif(120, min = 180, max = 300)), y = rnorm (120,mean=3.9, sd=.03))
)
dt$color <- as.factor(dt$color)
dt <- as_tibble(dt)
Derive the Breaking Points
With a simple difference to the preceeding point lag(y) we get the outliers. They are classsified using a threshold.
Change of Behaviour Classification
Base on the rules you described, the breaking points are classified as OK and problem.
The rule states that no two changes in the same direction are allowed. The second move in teh previos direction is considered as a problem.
You may need to adjust this simple interpretation if your logik is more advanced.
## extract outliers and get previous value
dt2 <- filter(dt2, diff != 0) %>%
mutate(cs = cumsum(diff),
prev = lag(diff),
cls = case_when(
diff * prev > 0 ~ "problem",
TRUE ~ "OK"))
## show
dt2 %>% select(x,y,diff,prev,cls)
## # A tibble: 6 x 5
## x y diff prev cls
## <dbl> <dbl> <dbl> <dbl> <chr>
## 1 50 4.53 1 NA OK
## 2 66 3.32 -1 1 OK
## 3 80 3.87 1 -1 OK
## 4 151 3.32 -1 1 OK
## 5 167 2.91 -1 -1 problem
## 6 180 3.87 1 -1 OK
Presentation
Finaly you projects the recognised outliers to the original data
## project in the original data
ggplot(data=dt, mapping = aes(x=x, y=y) ) +
geom_point(mapping = aes(color = color) ) +
scale_color_manual(values=c("blue", "yellow", "red","green","red")) +
theme(legend.position="none") +
geom_vline(data=dt2, aes(xintercept=x, color=cls),
linetype="dashed", size = 2) 60