I show a way to use 1.5 * IQR to flag points outside of those limits in the code below. However, I would be very cautious about eliminating data on the basis of such a test. As the data in your reprex show, the differences calculated from normally distributed data with only 50 points can exceed these limits. In a large normally distributed data set, there will almost always be data beyond 1.5 * IQR. And your data may not be normally distributed. My own practice is to keep data unless a definite problem is known (e. g. an instrument is found to be out of calibration) or the data are physically extremely implausible (e. g. a person who weighed 50 kg last month is recorded as weighing 100 kg this month).
library(dplyr)
library(ggplot2)
set.seed(123)
# Toy data
weight_actual_a = rnorm(50, mean = 10, sd = 4)
weight_actual_b = rnorm(50, mean = 4, sd = 2)
weight_expected_a = rnorm(50, mean = 10, sd = 1)
weight_expected_b = rnorm(50, mean = 4, sd = .2)
df <- tibble(
product = rep(c("A", "B"), each = 50),
weight_actual = c(weight_actual_a, weight_actual_b),
weight_expected = c(weight_expected_a, weight_expected_b)
) %>%
# difference between actual and expected weights
mutate(difference = weight_expected - weight_actual)
OutlierCalc <- df %>% group_by(product) %>%
summarize(UL = quantile(difference, 0.75) + (quantile(difference, 0.75) - quantile(difference, 0.25)) * 1.5,
LL = quantile(difference, 0.25) - (quantile(difference, 0.75) - quantile(difference, 0.25)) * 1.5)
df <- inner_join(df, OutlierCalc, by = "product")
df <- df %>% mutate(Outlier = ifelse(difference < LL | difference > UL, "Yes", "No"))
ggplot(df, aes(x = difference, fill = Outlier)) + geom_histogram(color = "white") +
facet_wrap(~product, nrow = 1)
#> `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Created on 2022-04-25 by the reprex package (v0.2.1)