Detect and Remove Outliers
Outliers skew analysis and models. This lesson shows you how to find them with IQR and z-scores, then remove them cleanly in Python. Hands-on, with troubleshooting, part of the Applied AI engineering track.
Focus: detect and remove outliers in data
You've cleaned missing values and standardized your columns, but then a single rogue value — a transaction of $99,999 in a column that averages $50 — silently shifts your mean, inflates your model's error, and makes your dashboard tell a lie. Outliers aren't just noise; they're one of the most common reasons models underperform and business decisions go wrong. In this lesson, you'll learn how to detect and remove outliers in data using two battle-tested statistical methods — the interquartile range (IQR) and z-scores — and apply them in Python to make your dataset robust and your models trustworthy.
The problem this lesson solves
Imagine you're building a recommendation engine for an e-commerce site. Your dataset contains purchase amounts, and most customers spend between $10 and $200. But one row shows a purchase of $5,000 — maybe a data-entry error, maybe a legitimate bulk order. Without handling this outlier, your mean purchase amount jumps, your model's loss function is dominated by that single point, and your recommendations become skewed toward high-ticket items that most users never buy.
Outliers create three specific problems:
- They distort summary statistics — the mean and standard deviation become unreliable, which affects any downstream normalization or feature scaling.
- They bias model training — algorithms like linear regression and k-nearest neighbors are especially sensitive to extreme values, pulling the decision boundary or fit line away from the bulk of the data.
- They hide real patterns — a cluster of legitimate small-value transactions can be overshadowed by one extreme value, making it harder to discover meaningful segments.
The stakes are real: in a 2020 analysis of credit-card fraud, even a single outlier can flip a model's precision from 95% to 70%. If you skip this step, every subsequent analysis — from a simple bar chart to a deployed model — inherits the distortion.
By the end of this lesson, you'll be able to detect and remove outliers in data with confidence, using both visual and statistical methods, and you'll know exactly when to apply each technique.
Core concept / mental model
Think of your dataset as a room full of people. Most are of typical height, but a few are unusually tall or short. You want to describe the average person in the room — so you decide to exclude the extremes. The IQR method is like measuring the middle 50% of the group and drawing a boundary: anyone who stands more than 1.5 times that middle-spread away from the central half is considered an outlier. The z-score method is more like a height gauge: it measures how many standard deviations a person is from the average, and anyone beyond, say, 3 standard deviations is flagged.
Here's the mental model in words:
- IQR (interquartile range) = Q3 − Q1, the spread of the middle 50% of your data. Outlier boundaries are typically Q1 − 1.5 × IQR and Q3 + 1.5 × IQR. This method is robust to skew because it's based on percentiles, not the mean.
- Z-score = (x − mean) / standard deviation. The classic threshold is |z| > 3, which assumes roughly normal data. This method is sensitive to extreme outliers themselves — one huge value can inflate the mean and standard deviation, masking other outliers.
Both methods are about flagging extreme values. Removing them is a separate decision, and we'll discuss when that's appropriate.
How it works step by step
Here's the general workflow you'll follow every time you need to detect and remove outliers in data:
- Inspect the distribution — plot histograms or boxplots to spot obvious extremes and understand the shape (normal, skewed, heavy-tailed).
- Choose a detection method — use IQR for skewed data, z-scores for roughly normal data, or both as a cross-check.
- Calculate the thresholds — for IQR, compute Q1, Q3, and IQR; for z-scores, compute the mean and standard deviation.
- Flag outliers — create a boolean mask that marks rows outside the accepted range.
- Review and decide — look at the flagged rows to see if they're errors or legitimately rare events. Don't blindly delete.
- Remove or cap — filter out rows, or clip values to a threshold (winsorizing) if you need to preserve data.
- Validate — re-check the distribution and summary stats to confirm the removal achieved the desired effect.
Cause and effect — the method you choose directly affects which rows are removed. IQR is more conservative for symmetric data (it keeps more points), while z-score with a threshold of 3 is stricter. Understanding this helps you avoid removing legitimate data.
Hands-on walkthrough
Let's put the theory into practice. We'll start with a toy dataset that mimics a real-world column: product prices with a few injected outliers.
Setting up
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# Create a dataset with a normal distribution plus a few outliers
np.random.seed(42)
values = np.random.normal(50, 10, 200) # mean=50, std=10
# Inject 5 outliers
outlier_indices = np.random.choice(range(200), 5, replace=False)
values[outlier_indices] = np.array([120, 180, 2, 95, 0.5])
df = pd.DataFrame({'price': values})
print(df.describe())
Expected output (first few lines):
price
count 200.000000
mean 49.975200
std 14.234843
min 0.500000
25% 43.000000
50% 50.000000
75% 57.000000
max 180.000000
You can already see the max is 180, way beyond the 75th percentile of 57. Let's visualize.
Visual inspection
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
sns.histplot(df['price'], bins=30, ax=axes[0])
axes[0].set_title('Histogram of prices')
sns.boxplot(x=df['price'], ax=axes[1])
axes[1].set_title('Boxplot of prices')
plt.tight_layout()
plt.show()
You'll see a roughly normal distribution but with a few points far to the right (and one tiny one). Those are your outliers.
Method 1: IQR
Q1 = df['price'].quantile(0.25)
Q3 = df['price'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
print(f"IQR = {IQR:.2f}, bounds: [{lower_bound:.2f}, {upper_bound:.2f}]")
outliers_iqr = df[(df['price'] < lower_bound) | (df['price'] > upper_bound)]
print(f"IQR flagged {len(outliers_iqr)} outliers")
# Remove them
clean_df_iqr = df[(df['price'] >= lower_bound) & (df['price'] <= upper_bound)]
print(f"Rows retained: {len(clean_df_iqr)}")
Expected output:
IQR = 14.00, bounds: [22.00, 78.00]
IQR flagged 5 outliers
Rows retained: 195
Method 2: Z-score
from scipy import stats
z_scores = np.abs(stats.zscore(df['price']))
threshold = 3
outliers_z = df[z_scores > threshold]
print(f"Z-score flagged {len(outliers_z)} outliers")
# Remove
clean_df_z = df[z_scores <= threshold]
print(f"Rows retained: {len(clean_df_z)}")
Expected output (varies slightly due to seed, but similar):
Z-score flagged 4 outliers
Rows retained: 196
Notice the z-score method caught 4 while IQR caught 5. Why? Because the extreme value 180 inflates the standard deviation, which shrinks the z-scores of other points. This is a classic pitfall.
Putting it together: a reusable function
Here's a function you can lift into your own projects:
def remove_outliers(df, column, method='iqr', threshold=1.5):
"""Remove outliers from a dataframe column.
method: 'iqr' or 'zscore'
"""
if method == 'iqr':
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - threshold * IQR
upper = Q3 + threshold * IQR
return df[(df[column] >= lower) & (df[column] <= upper)]
elif method == 'zscore':
z = np.abs(stats.zscore(df[column]))
return df[z <= threshold]
else:
raise ValueError("Method must be 'iqr' or 'zscore'")
clean = remove_outliers(df, 'price', method='iqr')
print(f"Removed {len(df) - len(clean)} outliers")
This function is your workhorse. Now you can apply it to any dataset column in one line.
Compare options / when to choose what
Different scenarios call for different methods. Here's a quick comparison:
| Method | When to use | Strengths | Weaknesses |
|---|---|---|---|
| IQR | Skewed data, non-normal distributions | Robust to extreme values, no normality assumption | May flag too many points in tight data |
| Z-score | Roughly normal data | Simple, interpretable, good for symmetric distributions | Sensitive to outliers in the mean/std calculation |
| MAD (Median Absolute Deviation) | Very robust outlier detection | Even more robust than IQR | Less common, requires extra code |
| DBSCAN | Multivariate outlier detection | Handles clusters and mixed features | Needs parameter tuning, not for univariate |
Rule of thumb: Start with IQR for most real-world columns because data is rarely perfectly normal. Use z-score when you know your data is Gaussian (e.g., measurement errors, some sensor data). If you have multiple related columns, consider multivariate methods like Mahalanobis distance or DBSCAN.
Variations worth knowing
- Winsorizing — instead of deleting outliers, cap them at the boundary values. This preserves data size and reduces distortion.
- Log transformation — applying
np.log1p()can compress huge values, making them less extreme without removal. - Isolation Forest — a machine learning–based detector that works well with high-dimensional data.
Troubleshooting & edge cases
Outlier removal isn't always smooth. Here are the most common pitfalls and how to fix them.
1. Z-score missed an obvious outlier
Symptom: Your plot shows a clear outlier, but z-score says it's within 3 standard deviations. Cause: The outlier itself inflates the standard deviation, making the score smaller. Fix: Use IQR or first remove extreme outliers before computing z-scores (iterative approach).
# Iterative z-score removal
def remove_outliers_iterative(df, column, max_iter=5):
for _ in range(max_iter):
z = np.abs(stats.zscore(df[column]))
clean = df[z <= 3]
if len(clean) == len(df):
break
df = clean
return df
2. Removing legitimate data
Symptom: You removed 10% of your dataset, and your model got worse. Cause: You treated rare but valid events (e.g., fraud, high-value sales) as noise. Fix: Always inspect flagged outliers. If they look real, consider capping or leaving them — or use a separate category flag.
3. Data skewness and log transform
Symptom: IQR flags too many points on a log-normal distribution. Fix: Apply a log transform first, then run IQR, or use a higher IQR multiplier (e.g., 3.0) for skewed data.
# Log-transform before detection
df['log_price'] = np.log1p(df['price'])
Q1 = df['log_price'].quantile(0.25)
Q3 = df['log_price'].quantile(0.75)
IQR = Q3 - Q1
# ... then filter based on log_price
4. Categorical or discrete data
Symptom: IQR flags every '0' in a binary column. Fix: Only run outlier detection on continuous numerical features, not categorical or discrete ones.
5. Small datasets
Symptom: Removing 3 outliers out of 20 rows changes your model drastically. Fix: Be conservative — consider winsorizing or using robust statistics (median, MAD) instead of deletion.
What you learned & what's next
You now have a solid grasp of how to detect and remove outliers in data. You can:
- Use IQR to find outliers in skewed distributions.
- Use z-scores for normally distributed data.
- Visualize outliers with histograms and boxplots.
- Build a reusable Python function to handle this in any pipeline.
- Avoid the classic pitfall of z-score masking with an iterative approach.
- Decide between removal, capping, or transformation based on the situation.
What's next: In the next lesson, you'll move beyond data cleaning to feature scaling — transforming your cleaned data into a range that models love. You'll apply StandardScaler and MinMaxScaler to your outlier-free dataset, and you'll see how the clean data improves your model's convergence and performance. Now that your outliers are gone, your features will be ready for prime time.
Practice recap
Try this: load the built-in seaborn.load_dataset('tips') and apply IQR outlier removal to the total_bill column. How many rows were removed? Then try z-score with threshold 3 — does it remove the same number? Plot the before and after distributions to see the difference. This hands-on exercise will cement your understanding of both methods.
Common mistakes
- Blindly removing all rows flagged as outliers without inspecting them — you may delete legitimate rare events (e.g., high-value purchases) and degrade model performance.
- Using z-score on skewed data — the mean and standard deviation get inflated, hiding true outliers; IQR is more robust.
- Forgetting that z-score is iterative — a single extreme outlier can mask others; apply iterative removal or use IQR instead.
- Applying outlier detection to categorical or discrete columns like 'gender' or 'country_code' — these methods are only for continuous numerical data.
- Removing outliers and not rechecking the distribution — you might have removed too much or left some behind; always validate with a new plot.
Variations
- Use median absolute deviation (MAD) instead of IQR for even more robust detection on heavily skewed data.
- Winsorize (cap) outliers at the boundary values instead of deleting rows to preserve sample size.
- Use a machine learning approach like Isolation Forest for multivariate outlier detection when you have many correlated features.
Real-world use cases
- Cleaning e-commerce transaction data to build a reliable customer-spend model for personalized recommendations.
- Filtering sensor readings (e.g., temperature or pressure) in an IoT pipeline before feeding them into a predictive maintenance model.
- Detecting fraudulent credit card transactions by flagging unusually large purchases as potential outliers for manual review.
Key takeaways
- Outliers distort summary statistics and model training, so detecting and removing them is a critical preprocessing step.
- IQR is robust for skewed data and uses the interquartile range to define boundaries (Q1 - 1.5IQR, Q3 + 1.5IQR).
- Z-score is best for normally distributed data but can be fooled by extreme outliers — use iteratively or switch to IQR.
- Always inspect flagged outliers before removing them; some may be legitimate rare events.
- Reusable functions, like the
remove_outliers()example, let you apply these methods consistently across projects. - Validating the cleaned dataset with plots and summary statistics ensures your removal was effective and not excessive.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.