Written and maintained by CASRAI Editorial Board
Last updated
Finding an outlier in R and removing one are two separate decisions. Detection is a few lines of code: boxplot.stats(x)$out returns the values that fall beyond the whiskers. Removal is where analyses go wrong, because the filter you write can silently drop rows you never meant to touch, and because the statistical question, whether the point should go at all, comes first. This guide covers both, with code you can run on a small example, and shows where the common shortcuts disagree with each other.
What it is used for in research
NIST/SEMATECH defines an outlier as “an observation that lies an abnormal distance from other values in a random sample from a population.” In research data that usually means one of three things: a recording or entry error, a measurement from a different process than the rest of the sample, or a genuine extreme value. The NIST e-Handbook warns that outliers “should be investigated carefully,” that “often they contain valuable information about the process under investigation,” and that “before considering the possible elimination of these points from the data, one should try to understand why they appeared.” It adds, “of course, outliers are often bad data points.”
So the workflow in this guide is: detect, flag, investigate, then decide. For the statistics behind the rule, see how to find an outlier in statistics; for the plot itself, see box plots in R.
Install or access (official source only)
Everything in the detection section uses base R and the grDevices package that ships with it. The removal examples that use the pipe-friendly filter() need the dplyr package from CRAN:
install.packages("dplyr")
library(dplyr)
R itself is distributed from the Comprehensive R Archive Network at cran.r-project.org.
Step-by-step task: how to find an outlier in R
Use this small example throughout. It has 12 values and one that looks far from the rest.
df <- data.frame(
id = 1:12,
value = c(10, 12, 13, 14, 15, 16, 17, 18, 19, 21, 23, 33)
)
- Look first. Run
boxplot(df$value). Points drawn beyond the whiskers are the candidates. - Extract the values the plot treats as outliers.
out <- boxplot.stats(df$value)$outreturns33here. According to the R manual,$outholds “the values of any data points which lie beyond the extremes of the whiskers.” - Find the rows, not just the values.
df[df$value %in% out, ]returns the full record, so you can check the id against your source data. - Change the whisker length if you need to. The signature is
boxplot.stats(x, coef = 1.5, do.conf = TRUE, do.out = TRUE), andcoef“determines how far the plot ‘whiskers’ extend out from the box.” Settingcoef = 3gives a stricter, hinge-based version of the outer fence described next.
Mild and extreme outliers
The NIST handbook uses two pairs of fences. The inner fences sit at Q1 minus 1.5 times the interquartile range and Q3 plus 1.5 times the interquartile range; points beyond them are “mild outliers.” The outer fences use a multiplier of 3; points beyond them are “extreme outliers.” In boxplot.stats() terms, the default coef = 1.5 lists mild and extreme points together, and coef = 3 lists only the points beyond the wider fence.
Why two R methods can disagree
Many tutorials compute the fences by hand with quantile() or IQR() and treat the result as the same thing boxplot.stats() returns. They are close but not identical. The R manual says boxplot.stats() uses hinges, “versions of the first and third quartile,” and that the hinges equal the quartiles for odd n and differ for even n.
On the 12-value example above, the hinges are 13.5 and 20, giving fences of 3.75 and 29.75. The default quantile() quartiles are 13.75 and 19.5, giving fences of 5.125 and 28.125. Both flag 33 and nothing else here, but a value sitting between the two sets of fences would be flagged by one method and not the other. Pick one method, state it in your methods section, and use it for the whole dataset. The interquartile range in R guide covers the IQR() function and its type argument.
A z-score alternative
A second common approach standardizes the variable and flags values far from the mean:
z <- (df$value - mean(df$value)) / sd(df$value)
df[abs(z) > 3, ]
The cutoff of 3 is a convention you choose and report, not a property of the data. Because the mean and standard deviation are themselves pulled by the extreme value, a z-score can understate how unusual a single large point is in a small sample. For model-based checks on regression residuals, see standardized residuals.
How to remove an outlier in R
Remove only after you have a reason that is independent of the data being inconvenient, such as a confirmed entry error. Three patterns follow, from safest to most destructive.
1. Flag, do not delete
df$is_outlier <- df$value %in% out
A flag column keeps the original data intact, lets you run the analysis with and without the point, and makes the decision auditable.
2. Drop the flagged rows
df_clean <- df[!df$value %in% out, ]
# or, with dplyr
df_clean <- df |> filter(!value %in% out)
3. Filter on the fences
lo <- 5.125; hi <- 28.125 # from your own quartile calculation
df_clean <- df |> filter(value >= lo, value <= hi)
The missing-value trap. The dplyr documentation states that filter() keeps the rows where the condition is true and that it “drops both NA and FALSE.” A fence filter such as value >= lo, value <= hi therefore deletes every row where value is NA along with the outliers, so your row count falls by more than the number of outliers. Check with sum(is.na(df$value)) before and after, or use the %in% out pattern above, which leaves missing values in place.
Replace instead of remove
If deleting the point would bias the sample, the alternatives are to cap it (winsorizing) or to trim a fixed share of each tail. The trade-offs are covered in winsorizing vs trimming for outlier handling, and the effect on a summary statistic in the trimmed mean formula. For where outlier handling sits in a wider workflow, see data cleaning steps.
Output and how to read it
$statsis a vector of five numbers: the extreme of the lower whisker, the lower hinge, the median, the upper hinge, and the extreme of the upper whisker.$outlists the points beyond the whiskers. An empty result (numeric(0)) means nothing falls outside the fences, not that the data are free of problems.- A flagged value is a prompt to investigate. NIST’s guidance is to understand why it appeared before eliminating it.
Troubleshooting
- The row count dropped by more than the number of outliers. Missing values were removed by
filter(); see the trap above. - Your hand-computed fences disagree with
boxplot.stats(). Hinges andquantile()quartiles can differ for even sample sizes; use one method consistently. - New outliers appear after you remove the first. Quartiles change when rows are removed, so repeated rounds of removal will keep finding points. Decide on one pass and document it.
Alternatives for the same task
| Approach | What it does | Source |
|---|---|---|
boxplot.stats(x)$out |
Returns values beyond the whiskers, using hinges, with coef controlling whisker length |
R manual, grDevices |
Fences from quantile() or IQR() |
Computes Q1 and Q3 minus/plus 1.5 times the interquartile range (3 for extreme outliers) | NIST/SEMATECH e-Handbook |
dplyr::filter() |
Subsets rows by condition; drops rows where the condition is NA or FALSE | dplyr reference |
FAQ
How to remove an outlier in R?
Identify the values with out <- boxplot.stats(df$value)$out, then drop those rows with df[!df$value %in% out, ] or filter(df, !value %in% out). Investigate the cause first, and keep a flag column or the original data so the step can be reversed.
How to find an outlier in R?
Run boxplot.stats(x)$out on a numeric vector, or plot it with boxplot(x) and read the points beyond the whiskers. Set coef = 3 to list only extreme outliers. See also box plots in R.
Should I remove outliers at all?
Not automatically. NIST notes that outliers often carry information about the process, and that you should understand why they appeared before eliminating them.
Sources (official docs)
- R documentation, boxplot.stats, checked 2026-10-29.
- NIST/SEMATECH e-Handbook of Statistical Methods, Box plot and outliers, checked 2026-10-29.
- dplyr reference, filter(), checked 2026-10-29.








