Machine learning / Feature engineering and preprocessing
Outlier Detection and Removal in Python
Contents
- Introduction to Outlier Detection and Removal
- Z-Score Method for Outlier Detection
- Interquartile Range (IQR) Method
- Local Outlier Factor (LOF)
- Isolation Forest
- Removing Outliers
- Handling Outliers in Time Series Data
- Real-Life Example: Air Quality Monitoring
- Real-Life Example: Network Traffic Analysis
- Comparing Outlier Detection Methods
- Handling Outliers in Machine Learning
- Robust Statistical Methods
- Outlier Detection in High-Dimensional Data
- Handling Outliers in Time Series Forecasting
- Additional Resources
Introduction to Outlier Detection and Removal
Outliers are data points that significantly deviate from the normal pattern of a dataset. Detecting and handling outliers is crucial for maintaining data integrity and improving the accuracy of statistical analyses and machine learning models. In this presentation, we'll explore various techniques for outlier detection and removal using Python.
import numpy as np
import matplotlib.pyplot as plt
# Generate sample data with outliers
np.random.seed(42)
data = np.random.normal(0, 1, 1000)
outliers = np.random.uniform(-10, 10, 50)
data = np.concatenate([data, outliers])
# Plot histogram
plt.figure(figsize=(10, 6))
plt.hist(data, bins=50, edgecolor='black')
plt.title('Sample Data with Outliers')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()
Z-Score Method for Outlier Detection
The Z-score method identifies outliers by measuring how many standard deviations a data point is from the mean. Data points with a Z-score greater than a threshold (typically 3) are considered outliers.
from scipy import stats
def detect_outliers_zscore(data, threshold=3):
z_scores = np.abs(stats.zscore(data))
return np.where(z_scores > threshold)
outliers = detect_outliers_zscore(data)
print(f"Number of outliers detected: {len(outliers[0])}")
# Plot data with outliers highlighted
plt.figure(figsize=(10, 6))
plt.scatter(range(len(data)), data, c='blue', alpha=0.5)
plt.scatter(outliers, data[outliers], c='red', label='Outliers')
plt.title('Z-Score Outlier Detection')
plt.xlabel('Index')
plt.ylabel('Value')
plt.legend()
plt.show()
Interquartile Range (IQR) Method
The IQR method uses quartiles to identify outliers. Data points falling below Q1 - 1.5 * IQR or above Q3 + 1.5 * IQR are considered outliers. This method is less sensitive to extreme values compared to the Z-score method.
def detect_outliers_iqr(data):
Q1 = np.percentile(data, 25)
Q3 = np.percentile(data, 75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return np.where((data < lower_bound) | (data > upper_bound))
outliers_iqr = detect_outliers_iqr(data)
print(f"Number of outliers detected (IQR): {len(outliers_iqr[0])}")
# Plot data with outliers highlighted
plt.figure(figsize=(10, 6))
plt.scatter(range(len(data)), data, c='blue', alpha=0.5)
plt.scatter(outliers_iqr, data[outliers_iqr], c='red', label='Outliers (IQR)')
plt.title('IQR Outlier Detection')
plt.xlabel('Index')
plt.ylabel('Value')
plt.legend()
plt.show()
Local Outlier Factor (LOF)
LOF is a density-based method that compares the local density of a point with the local densities of its neighbors. Points with substantially lower density than their neighbors are considered outliers.
from sklearn.neighbors import LocalOutlierFactor
def detect_outliers_lof(data, n_neighbors=20, contamination=0.1):
lof = LocalOutlierFactor(n_neighbors=n_neighbors, contamination=contamination)
outlier_labels = lof.fit_predict(data.reshape(-1, 1))
return np.where(outlier_labels == -1)
outliers_lof = detect_outliers_lof(data)
print(f"Number of outliers detected (LOF): {len(outliers_lof[0])}")
# Plot data with outliers highlighted
plt.figure(figsize=(10, 6))
plt.scatter(range(len(data)), data, c='blue', alpha=0.5)
plt.scatter(outliers_lof, data[outliers_lof], c='red', label='Outliers (LOF)')
plt.title('LOF Outlier Detection')
plt.xlabel('Index')
plt.ylabel('Value')
plt.legend()
plt.show()
Isolation Forest
Isolation Forest is an unsupervised learning algorithm that isolates anomalies by randomly selecting a feature and then randomly selecting a split value between the maximum and minimum values of the selected feature.
from sklearn.ensemble import IsolationForest
def detect_outliers_iforest(data, contamination=0.1):
iforest = IsolationForest(contamination=contamination, random_state=42)
outlier_labels = iforest.fit_predict(data.reshape(-1, 1))
return np.where(outlier_labels == -1)
outliers_iforest = detect_outliers_iforest(data)
print(f"Number of outliers detected (Isolation Forest): {len(outliers_iforest[0])}")
# Plot data with outliers highlighted
plt.figure(figsize=(10, 6))
plt.scatter(range(len(data)), data, c='blue', alpha=0.5)
plt.scatter(outliers_iforest, data[outliers_iforest], c='red', label='Outliers (IForest)')
plt.title('Isolation Forest Outlier Detection')
plt.xlabel('Index')
plt.ylabel('Value')
plt.legend()
plt.show()
Removing Outliers
After detecting outliers, we can remove them from the dataset. Here's an example of how to remove outliers detected by the Z-score method:
def remove_outliers(data, outlier_indices):
return np.delete(data, outlier_indices)
# Remove outliers detected by Z-score method
clean_data = remove_outliers(data, outliers[0])
print(f"Original data shape: {data.shape}")
print(f"Clean data shape: {clean_data.shape}")
# Plot original and clean data
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.hist(data, bins=50, edgecolor='black')
plt.title('Original Data')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.subplot(122)
plt.hist(clean_data, bins=50, edgecolor='black')
plt.title('Clean Data (Outliers Removed)')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.tight_layout()
plt.show()
Handling Outliers in Time Series Data
When dealing with time series data, removing outliers might not be the best approach. Instead, we can use techniques like rolling median or exponential moving average to smooth out the outliers.
import pandas as pd
# Generate sample time series data with outliers
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', periods=1000, freq='D')
values = np.random.normal(0, 1, 1000)
outliers = np.random.uniform(-10, 10, 50)
values[np.random.choice(1000, 50, replace=False)] = outliers
ts_data = pd.Series(values, index=dates)
# Apply rolling median
rolling_median = ts_data.rolling(window=7, center=True).median()
# Apply exponential moving average
ema = ts_data.ewm(span=7, adjust=False).mean()
# Plot original and smoothed data
plt.figure(figsize=(12, 6))
plt.plot(ts_data, label='Original', alpha=0.5)
plt.plot(rolling_median, label='Rolling Median', linewidth=2)
plt.plot(ema, label='EMA', linewidth=2)
plt.title('Time Series Outlier Handling')
plt.xlabel('Date')
plt.ylabel('Value')
plt.legend()
plt.show()
Real-Life Example: Air Quality Monitoring
In air quality monitoring, sensor data may contain outliers due to various factors such as equipment malfunction or temporary environmental disturbances. Let's apply outlier detection to a simulated air quality dataset.
import pandas as pd
import numpy as np
from scipy import stats
# Generate simulated air quality data (PM2.5 concentrations)
np.random.seed(42)
dates = pd.date_range(start='2023-01-01', periods=1000, freq='H')
pm25 = np.random.lognormal(mean=3, sigma=0.5, size=1000)
outliers = np.random.uniform(100, 500, 20)
pm25[np.random.choice(1000, 20, replace=False)] = outliers
air_quality = pd.DataFrame({'timestamp': dates, 'pm25': pm25})
# Detect outliers using Z-score method
z_scores = np.abs(stats.zscore(air_quality['pm25']))
outliers = air_quality[z_scores > 3]
# Plot the data with outliers highlighted
plt.figure(figsize=(12, 6))
plt.scatter(air_quality['timestamp'], air_quality['pm25'], alpha=0.5, label='Normal')
plt.scatter(outliers['timestamp'], outliers['pm25'], color='red', label='Outliers')
plt.title('Air Quality Monitoring: PM2.5 Concentrations')
plt.xlabel('Timestamp')
plt.ylabel('PM2.5 (μg/m³)')
plt.legend()
plt.show()
print(f"Number of outliers detected: {len(outliers)}")
Real-Life Example: Network Traffic Analysis
Network traffic analysis often involves detecting anomalies that could indicate potential security threats or network issues. Let's apply outlier detection to a simulated network traffic dataset.
import pandas as pd
import numpy as np
from sklearn.ensemble import IsolationForest
# Generate simulated network traffic data
np.random.seed(42)
timestamps = pd.date_range(start='2023-01-01', periods=1000, freq='5T')
traffic = np.random.poisson(lam=100, size=1000)
anomalies = np.random.uniform(500, 1000, 10)
traffic[np.random.choice(1000, 10, replace=False)] = anomalies
network_data = pd.DataFrame({'timestamp': timestamps, 'packets': traffic})
# Detect outliers using Isolation Forest
clf = IsolationForest(contamination=0.01, random_state=42)
outliers = clf.fit_predict(network_data[['packets']])
network_data['is_outlier'] = outliers
# Plot the data with outliers highlighted
plt.figure(figsize=(12, 6))
plt.scatter(network_data[network_data['is_outlier'] == 1]['timestamp'],
network_data[network_data['is_outlier'] == 1]['packets'],
alpha=0.5, label='Normal')
plt.scatter(network_data[network_data['is_outlier'] == -1]['timestamp'],
network_data[network_data['is_outlier'] == -1]['packets'],
color='red', label='Anomalies')
plt.title('Network Traffic Analysis: Packet Count')
plt.xlabel('Timestamp')
plt.ylabel('Packets per 5 minutes')
plt.legend()
plt.show()
print(f"Number of anomalies detected: {sum(network_data['is_outlier'] == -1)}")
Comparing Outlier Detection Methods
Different outlier detection methods may yield different results. Let's compare the performance of Z-score, IQR, LOF, and Isolation Forest on a synthetic dataset.
import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from sklearn.neighbors import LocalOutlierFactor
from sklearn.ensemble import IsolationForest
# Generate synthetic data with outliers
np.random.seed(42)
X = np.random.normal(0, 1, (1000, 2))
X_outliers = np.random.uniform(-4, 4, (100, 2))
X = np.concatenate([X, X_outliers])
# Define outlier detection functions
def zscore_outliers(X, threshold=3):
z = np.abs(stats.zscore(X))
return np.any(z > threshold, axis=1)
def iqr_outliers(X):
Q1 = np.percentile(X, 25, axis=0)
Q3 = np.percentile(X, 75, axis=0)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return np.any((X < lower_bound) | (X > upper_bound), axis=1)
# Detect outliers using different methods
outliers_zscore = zscore_outliers(X)
outliers_iqr = iqr_outliers(X)
outliers_lof = LocalOutlierFactor().fit_predict(X) == -1
outliers_iforest = IsolationForest().fit_predict(X) == -1
# Plot results
fig, axs = plt.subplots(2, 2, figsize=(12, 12))
methods = [outliers_zscore, outliers_iqr, outliers_lof, outliers_iforest]
titles = ['Z-Score', 'IQR', 'LOF', 'Isolation Forest']
for ax, method, title in zip(axs.ravel(), methods, titles):
ax.scatter(X[~method, 0], X[~method, 1], c='blue', alpha=0.5, label='Normal')
ax.scatter(X[method, 0], X[method, 1], c='red', alpha=0.5, label='Outliers')
ax.set_title(f'{title} ({sum(method)} outliers)')
ax.legend()
plt.tight_layout()
plt.show()
Handling Outliers in Machine Learning
Outliers can significantly impact machine learning models. Let's compare the performance of a linear regression model with and without outlier removal.
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
# Generate synthetic data with outliers
np.random.seed(42)
X = np.linspace(0, 10, 100).reshape(-1, 1)
y = 2 * X + 1 + np.random.normal(0, 1, X.shape)
outliers_X = np.random.uniform(0, 10, (10, 1))
outliers_y = np.random.uniform(15, 25, (10, 1))
X_with_outliers = np.vstack([X, outliers_X])
y_with_outliers = np.vstack([y, outliers_y])
# Fit linear regression models
model_with_outliers = LinearRegression().fit(X_with_outliers, y_with_outliers)
model_without_outliers = LinearRegression().fit(X, y)
# Calculate MSE
mse_with_outliers = mean_squared_error(y_with_outliers, model_with_outliers.predict(X_with_outliers))
mse_without_outliers = mean_squared_error(y, model_without_outliers.predict(X))
# Plot results
plt.figure(figsize=(12, 6))
plt.scatter(X, y, c='blue', alpha=0.5, label='Normal data')
plt.scatter(outliers_X, outliers_y, c='red', alpha=0.5, label='Outliers')
plt.plot(X, model_with_outliers.predict(X), c='red', label='With outliers')
plt.plot(X, model_without_outliers.predict(X), c='green', label='Without outliers')
plt.legend()
plt.title('Linear Regression: With vs Without Outliers')
plt.xlabel('X')
plt.ylabel('y')
plt.show()
print(f"MSE with outliers: {mse_with_outliers:.4f}")
print(f"MSE without outliers: {mse_without_outliers:.4f}")
Robust Statistical Methods
When dealing with datasets containing outliers, robust statistical methods can be employed to minimize their impact on the analysis. These methods are less sensitive to outliers compared to their non-robust counterparts.
import numpy as np
from scipy import stats
# Generate sample data with outliers
np.random.seed(42)
data = np.random.normal(0, 1, 1000)
outliers = np.random.uniform(-10, 10, 50)
data_with_outliers = np.concatenate([data, outliers])
# Calculate mean and median
mean = np.mean(data_with_outliers)
median = np.median(data_with_outliers)
# Calculate standard deviation and median absolute deviation (MAD)
std = np.std(data_with_outliers)
mad = stats.median_abs_deviation(data_with_outliers)
# Calculate Pearson and Spearman correlation
x = np.linspace(0, 10, 1000)
y = 2 * x + 1 + np.random.normal(0, 1, 1000)
y_with_outliers = np.(y)
y_with_outliers[np.random.choice(1000, 50)] = np.random.uniform(-10, 30, 50)
pearson_corr, _ = stats.pearsonr(x, y_with_outliers)
spearman_corr, _ = stats.spearmanr(x, y_with_outliers)
print(f"Mean: {mean:.2f}, Median: {median:.2f}")
print(f"Standard Deviation: {std:.2f}, MAD: {mad:.2f}")
print(f"Pearson correlation: {pearson_corr:.2f}")
print(f"Spearman correlation: {spearman_corr:.2f}")
Outlier Detection in High-Dimensional Data
As the number of dimensions increases, traditional outlier detection methods may become less effective. Techniques like Principal Component Analysis (PCA) can be used to reduce dimensionality before applying outlier detection.
import numpy as np
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import IsolationForest
# Generate high-dimensional data with outliers
np.random.seed(42)
n_samples, n_features = 1000, 50
X = np.random.normal(0, 1, (n_samples, n_features))
outliers = np.random.uniform(-10, 10, (10, n_features))
X = np.vstack([X, outliers])
# Standardize the data
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# Apply PCA
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# Detect outliers using Isolation Forest
clf = IsolationForest(contamination=0.01, random_state=42)
outlier_labels = clf.fit_predict(X_pca)
# Plot results
plt.figure(figsize=(10, 6))
plt.scatter(X_pca[outlier_labels == 1, 0], X_pca[outlier_labels == 1, 1], c='blue', alpha=0.5, label='Normal')
plt.scatter(X_pca[outlier_labels == -1, 0], X_pca[outlier_labels == -1, 1], c='red', alpha=0.5, label='Outliers')
plt.title('Outlier Detection in High-Dimensional Data using PCA')
plt.xlabel('First Principal Component')
plt.ylabel('Second Principal Component')
plt.legend()
plt.show()
print(f"Number of outliers detected: {sum(outlier_labels == -1)}")
Handling Outliers in Time Series Forecasting
When dealing with time series data, outliers can significantly impact forecasting models. Here's an example of how to handle outliers in a simple time series forecasting scenario using moving averages.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import ExponentialSmoothing
# Generate sample time series data with outliers
np.random.seed(42)
date_rng = pd.date_range(start='2023-01-01', end='2023-12-31', freq='D')
y = np.sin(np.arange(len(date_rng)) * 2 * np.pi / 365) * 10 + np.random.normal(0, 1, len(date_rng))
outliers = np.random.choice(len(y), 20, replace=False)
y[outliers] += np.random.uniform(5, 15, 20) * np.random.choice([-1, 1], 20)
df = pd.DataFrame(data={'y': y}, index=date_rng)
# Apply moving average to smooth outliers
df['y_ma'] = df['y'].rolling(window=7, center=True).mean()
# Fit ExponentialSmoothing models
model_with_outliers = ExponentialSmoothing(df['y'], seasonal_periods=365, trend='add', seasonal='add').fit()
model_without_outliers = ExponentialSmoothing(df['y_ma'].dropna(), seasonal_periods=365, trend='add', seasonal='add').fit()
# Generate forecasts
forecast_with_outliers = model_with_outliers.forecast(steps=30)
forecast_without_outliers = model_without_outliers.forecast(steps=30)
# Plot results
plt.figure(figsize=(12, 6))
plt.plot(df.index, df['y'], label='Original Data', alpha=0.5)
plt.plot(df.index, df['y_ma'], label='Moving Average', linewidth=2)
plt.plot(forecast_with_outliers.index, forecast_with_outliers, label='Forecast with Outliers', linewidth=2)
plt.plot(forecast_without_outliers.index, forecast_without_outliers, label='Forecast without Outliers', linewidth=2)
plt.title('Time Series Forecasting: Handling Outliers')
plt.legend()
plt.show()
Additional Resources
For those interested in diving deeper into outlier detection and removal techniques, here are some valuable resources:
- Aggarwal, C. C. (2017). Outlier Analysis. Springer International Publishing. arXiv:1011.5921 [cs.LG]
- Chandola, V., Banerjee, A., & Kumar, V. (2009). Anomaly detection: A survey. ACM Computing Surveys, 41(3), 1-58. arXiv:0906.5507 [cs.LG]
- Liu, F. T., Ting, K. M., & Zhou, Z. H. (2008). Isolation Forest. In 2008 Eighth IEEE International Conference on Data Mining (pp. 413-422). IEEE. arXiv:1811.02141 [cs.LG]
- Breunig, M. M., Kriegel, H. P., Ng, R. T., & Sander, J. (2000). LOF: identifying density-based local outliers. In Proceedings of the 2000 ACM SIGMOD international conference on Management of data (pp. 93-104). Available at: https://www.dbs.ifi.lmu.de/Publikationen/Papers/LOF.pdf
These resources provide in-depth explanations of various outlier detection algorithms, their theoretical foundations, and practical applications in different domains.