If a single value in a dataset looks too extreme to trust, Grubbs' test is the formal way to check it — not eyeball it on a boxplot. In R, that means the grubbs.test() function from the outliers package. It tests one specific question: is the most extreme value in a sample from a normal distribution actually inconsistent with that distribution, or is it just a legitimate high or low observation?
This walks through the syntax, runs it on a real dataset with a real flagged outlier (not a made-up toy vector), and covers the part most tutorials skip: what the test's G statistic actually is, and why it isn't the same thing as "z-score above 3."
What Grubbs' test actually checks
Grubb'stest (Grubbs, 1950) assumes your data come from an approximately normal distribution and tests whether the single most extreme point — the highest or lowest value — is statistically inconsistent with that assumption.
Hypotheses
H0: All values, including the extreme one, come from the same normal population (no outlier).HA: The most extreme value is not from that population (it's an outlier).
That last part matters: the test is built for one outlier at a time, in data you're willing to assume is otherwise normal. If either of those doesn't hold, the result isn't reliable — more on both below.
grubbs.test() syntax
The function lives in the outliers package:
install.packages("outliers")
library(outliers)
grubbs.test(x, type = 10, opposite = FALSE, two.sided = FALSE)
| Argument | What it does |
|---|---|
x | A numeric vector. |
type = 10 | Default. Tests whether the single most extreme value (highest or lowest, chosen automatically) is an outlier. |
type = 11 | Tests whether the minimum and maximum are both outliers, on opposite tails. |
type = 20 | Tests whether the two most extreme values on the same tail are both outliers. |
opposite | Forces the test to check the value on the opposite side of the one it would normally pick. |
two.sided | Doubles the p-value to treat the test as two-tailed rather than one-tailed. |
For most cases — "is my single biggest or smallest value a problem?" - the defaults (type = 10) are what you want.
A real example: mtcars$carb
Most Grubbs' test tutorials run this on a short, invented vector with an obviously planted outlier. That proves the function works; it doesn't show you what a real flag looks like. Here it is on carb (number of carburetors) from R's built-in mtcars dataset — 32 real cars, no values altered:
data(mtcars)
grubbs.test(mtcars$carb)
Grubbs test for one outlier
data: mtcars$carb
G = 3.21168, U = 0.65653, p-value = 0.006787
alternative hypothesis: highest value 8 is an outlier
Mean carb across the 32 cars is 2.81 (SD 1.62). The flagged value, 8, belongs to the Maserati Bora — more than double the next-highest car in the dataset. With p = 0.0068, well under 0.05, the test rejects the null: this isn't ordinary sampling variation, it's a genuine outlier relative to the rest of the sample.
What G actually is
Here's the part most explanations leave out. G is calculated as:
G = |extreme value − mean| / SD
That's just the z-score of the most extreme point. For the Maserati Bora's carb value: (8 − 2.8125) / 1.6152 = 3.2117 — identical to G. So why does Grubbs' test need its own critical-value table instead of just flagging anything with |z| > 2 or 3?
Because G isn't being compared to a plain normal distribution — it's being compared to the distribution of the largest z-score you'd expect to see after specifically searching for it among n values. Picking out the most extreme point in a sample and then testing it against a generic z cutoff overstates significance; Grubbs' critical values correct for that search. It's the same reason a spelling-bee winner's score looks more impressive than a random speller's, even if both got the same raw number of words right — one of them was selected for being the best of many.
Grubbs' test vs. Z-score vs. IQR
All three methods agreed on the Maserati Bora — that won't always happen. Here's what changes between them:
| Method | On mtcars$carb | Best suited for |
|---|---|---|
| Grubbs' test | G = 3.21, p = 0.0068 → flagged | Formal significance test for one outlier, assumes normality |
| Z-score (|z| > 3 rule of thumb) | z = 3.21 → flagged, but only by a hair past an arbitrary cutoff | Quick scan; see calculating Z-scores in R |
| IQR / boxplot rule | Upper fence = 7.0 (Q3 + 1.5×IQR) → 8 flagged | Non-normal or skewed data, doesn't assume a distribution |
The Z-score rule is really Grubbs' G statistic without the correction for having searched for the extreme value — which is why it and Grubbs agree here but won't always. The IQR rule doesn't assume normality at all, which makes it the safer default on skewed variables. For a broader workflow covering all three plus how to handle flagged points in R, see removing outliers and data cleaning in R.
What to do after Grubbs' test flags a value
- Check it isn't a data-entry or measurement error first — that's the cheapest explanation and the most common cause.
- Confirm the normality assumption actually holds before trusting the p-value; if the data are skewed, run a normality check like the Shapiro-Wilk test rather than assuming it.
- Decide whether the point reflects real variation worth keeping (a genuinely unusual but valid case) or a distortion worth removing/winsorizing — this is a domain judgment, not something the p-value makes for you.
- If you're about to run ANOVA, a t-test, or a regression on this variable, re-check the relevant assumption afterward — for ANOVA specifically, see what happens when ANOVA assumptions are violated.
Deciding whether to keep, adjust, or exclude a flagged outlier changes your downstream results — for dissertation or publication-bound analysis, this is the step worth getting a second opinion on before you write it up.
Limitations worth knowing before you rely on it
type = 11 or type = 20 test for two directly rather than relying on repeated single-outlier runs.FAQ
Does Grubbs' test require normally distributed data?
Yes. The test's critical values are derived assuming the underlying data are normal aside from the single suspected outlier. If the variable is meaningfully skewed, run a normality check first — the p-value from Grubbs' test isn't trustworthy on data that doesn't fit that assumption, and the IQR method is a safer default there instead.
What is Grubbs' test?
Grubbs' test (Grubbs, 1950) is a hypothesis test for a single outlier in a sample assumed to be otherwise normally distributed. In R it's implemented as grubbs.test() in the outliers package. It's one of several formal outlier tests — Dixon's Q test and the chi-squared outlier test are others in the same package — but Grubbs' is the one built specifically around the most extreme value and a normal-distribution assumption.
What does the Grubbs test tell you?
It tells you whether the single most extreme value in your data is statistically inconsistent with the rest of the sample, given the normality assumption — not just "unusual-looking," but unlikely enough that you can reject the idea it came from the same population. The G statistic measures how extreme the value is; the p-value tells you how unlikely that's due to chance alone. A significant result (typically p < 0.05) means the value is a statistical outlier — it doesn't automatically mean the value is wrong or should be deleted.
Can grubbs.test() find more than one outlier at once?
The default (type = 10) checks exactly one. For two suspected outliers on opposite ends of the data, use type = 11; for two suspected outliers on the same end, use type = 20. Iteratively removing one flagged value and re-testing is also common, but watch for the masking effect described above.
How do I run a Grubbs test in R?
Install and load the outliers package, then call grubbs.test() on a numeric vector:
install.packages("outliers")
library(outliers)
grubbs.test(your_data)
That runs the default one-outlier test. See the syntax table and the worked mtcars$carb example above for what to do with the output.
How do I calculate the Grubbs value (G)?
G = |extreme value − sample mean| / sample standard deviation. It's the z-score of the most extreme point in the data — the part that's easy to miss is that G alone doesn't tell you significance; it has to be compared against Grubbs' critical value table (or the p-value grubbs.test() returns), which corrects for the fact that you specifically searched for the most extreme point rather than testing a value picked in advance. See "What G actually is" above for the worked calculation.
Is Grubbs' test the same as just checking for a Z-score above 3?
Not quite. Grubbs' G statistic is calculated exactly like a z-score, but it's compared against a critical value that accounts for the fact you deliberately picked the most extreme point out of n observations rather than testing a value chosen in advance. A flat "z > 3" rule doesn't make that correction, so it can be more permissive or more strict than Grubbs' test depending on sample size.
What other ways can I detect outliers in R?
Besides Grubbs' test: a Z-score cutoff (see calculating Z-scores in R), the IQR/boxplot rule using base R's boxplot.stats(x)$out or a visual boxplot, and Dixon's Q test for small samples. None of them agree in every case — Grubbs and Z-score assume normality, IQR doesn't. The full comparison and removal workflow is in removing outliers and data cleaning in R.
What is the 1.5 IQR rule?
It's the standard boxplot cutoff: anything below Q1 − 1.5×IQR or above Q3 + 1.5×IQR is flagged as an outlier, where IQR = Q3 − Q1. It doesn't assume a normal distribution, which is why it's a safer default than Grubbs' test on skewed data. On the mtcars$carb example above, the upper fence works out to 7.0 — the flagged value of 8 clears it, same conclusion as Grubbs' test in this case.
Should I remove, fix, or keep a value once it's flagged as an outlier?
Confirm what it is before deciding what to do with it — start with the four-step process above. Removing it changes the mean, standard deviation, and the power of whatever test runs next, and can occasionally mask a second real outlier that was being hidden by the first. Fixing (correcting a data-entry error) is different from removing (excluding a genuine but extreme case) — treat them as separate decisions, not one default action. For the actual removal code once you've decided, see removing outliers and data cleaning in R.
Grubbs' test tells you whether one value is a statistical outlier. It doesn't tell you what to do with it, and that decision shapes whatever test comes after it. If you're working through outlier handling as part of a larger analysis — thesis, dissertation, or a paper under review — get it checked on WhatsApp before you build the rest of the analysis on top of it.