xbe.at

Machine learning / Feature engineering and preprocessing

Outlier Detection and Removal in Python

12 min read, 15 sections

Contents
  1. Introduction to Outlier Detection and Removal
  2. Z-Score Method for Outlier Detection
  3. Interquartile Range (IQR) Method
  4. Local Outlier Factor (LOF)
  5. Isolation Forest
  6. Removing Outliers
  7. Handling Outliers in Time Series Data
  8. Real-Life Example: Air Quality Monitoring
  9. Real-Life Example: Network Traffic Analysis
  10. Comparing Outlier Detection Methods
  11. Handling Outliers in Machine Learning
  12. Robust Statistical Methods
  13. Outlier Detection in High-Dimensional Data
  14. Handling Outliers in Time Series Forecasting
  15. 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:

  1. Aggarwal, C. C. (2017). Outlier Analysis. Springer International Publishing. arXiv:1011.5921 [cs.LG]
  2. Chandola, V., Banerjee, A., & Kumar, V. (2009). Anomaly detection: A survey. ACM Computing Surveys, 41(3), 1-58. arXiv:0906.5507 [cs.LG]
  3. 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]
  4. 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.

More in Feature engineering and preprocessing

  1. Solve imbalanced datasets in Python4 min

    Imbalanced datasets occur when the distribution of classes in a dataset is heavily skewed, with one or more classes being significantly underrepresented...

  2. Normalizing and Feature Extraction of 3D Point Clouds10 min

    Point clouds are 3D representations of objects or environments, consisting of numerous points in space. Normalizing these point clouds and extracting...

  3. Sparse Matrices for Machine Learning in Python6 min

    Sparse matrices are data structures that efficiently store and operate on matrices with mostly zero elements. They are crucial in machine learning for...

  4. Non-Uniform Negative Sampling for Imbalanced Data in Python12 min

    Non-uniform negative sampling is a technique used to address imbalanced datasets in machine learning. It involves selectively choosing negative examples to...

  5. Splitting Arrays for Machine Learning Model Training12 min

    Array splitting is a crucial technique in machine learning, particularly for preparing data for model training. It involves dividing a large dataset into...

  6. Measuring Feature Importance with Shuffle Technique9 min

    Shuffle Feature Importance is a model-agnostic technique that measures feature importance by randomly permuting individual features and observing the impact...