数据预处理方法大全+实战代码(三)

前言

继续详细介绍缺失值处理异常值检测数据一致性处理。这是数据清洗的第一步,关键步骤。
上一篇因为篇幅的原因,只介绍了缺失值处理,本文介绍异常值检测

1、异常值与离群点检测

1.1 基于统计学方法(Z-Score、IQR)

  • a) Z-Score:公式:Z = (X - μ) / σ
    其中,X是原始值,μ是平均值,σ是标准差
    原理:假设数据呈正态分布,将原始数据转换为标准正态分布。通常|Z| > 3被视为异常值

  • b) IQR(四分位距)方法:公式:IQR = Q3 - Q1
    下界 = Q1 - 1.5 * IQR
    上界 = Q3 + 1.5 * IQR
    原理:利用数据的四分位数来定义异常值范围,对非正态分布数据也适用。

依旧用到了scipy.stats,上篇已经介绍过,就不做过多的赘述了。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats

# 创建包含异常值的示例数据
np.random.seed(42)
data = np.concatenate([np.random.normal(0, 1, 980), np.random.normal(5, 1, 20)])
df = pd.DataFrame({'value': data})

# Z-Score方法 
z_scores = np.abs(stats.zscore(df['value']))
z_score_outliers = df[z_scores > 3]

# IQR方法
Q1 = df['value'].quantile(0.25)
Q3 = df['value'].quantile(0.75)
IQR = Q3 - Q1
iqr_outliers = df[(df['value'] < (Q1 - 1.5 * IQR)) | (df['value'] > (Q3 + 1.5 * IQR))]

# 可视化
plt.figure(figsize=(12, 6))
plt.subplot(121)
plt.hist(df['value'], bins=50, edgecolor='black')  # 修正了bins的值
plt.title('Data Distribution')
plt.xlabel('Value')
plt.ylabel('Frequency')  # 修正了ylabel的拼写

plt.subplot(122)
plt.boxplot(df['value'])
plt.title('Box Plot')
plt.ylabel('Value')

plt.tight_layout()
plt.show()

print(f"Z-Score method detected {len(z_score_outliers)} outliers")
print(f"IQR method detected {len(iqr_outliers)} outliers")

# 显示检测到的异常值
print("\nZ-Score Outliers:")
print(z_score_outliers)
print("\nIQR Outliers:")
print(iqr_outliers)

结果如下,


左边为分布图,右边为箱形图

其实检测异常值的代码就6行,其余是拟合数据,画图的代码,所以说可视化是非常直观的,大家在做任何数据处理前要学会看数据,如散点图、热力图、线图等等。

2.2 基于Isolation Forest、LOF的检测

  • a) Isolation Forest孤立森林

    • 原理:通过随机选择特征和分割点来构建多棵隔离树,是一种用于异常检测的无监督学习算法。

    • 核心思想:异常值在数据集中通常较少且与正常数据有显著差异,更容易在隔离树中被快速隔离。

    • 算法步骤

      (1)随机选择一个特征
      (2)在该特征的取值范围内随机选择一个分割点
      (3)根据选定的特征和分割点,将数据集分割成两部分
      (4)重复1-3,直到每个样本被隔离或达到指定深度,即每个点都位于一个叶节点中
      (5)计算平均路径长度,路径较短的被视为异常点

用一句大白话说,越偏离大部队的样本,越容易被找出来(平均路径长度)

  • b) LOF局部离群因子

    • 原理:比较一个点的密度与其邻居的密度,来判定是否是异常点。

    • 核心思想:如果一个点的密度明显低于其邻居,则可能是异常点。

    • 算法步骤

      (1)选择邻居数K,计算每个点的k-距离(到第k个最近邻居的距离)
      (2)计算每个点的可达距离,对于每个数据点p和其邻居o,定义可达距离reach-dist_k(p, o),表示从p到o的距离,考虑到o的k-距离
      (3)计算每个点的局部可达密度,基于其可达距离的倒数
      (4)计算LOF值(邻居的平均局部可达密度与点自身局部可达密度的比值)

用一句大白话说,越靠近大部队的样本,样本之间的密达越大(局部可达密度)

  • c) LOF局部离群因子和Isolation Forest孤立森林区别

    • 原理不同
      LOF考虑数据点的局部密度;孤立森林异常点通常会有较短的路径

    • 计算复杂度
      LOF的计算复杂度较高,因为它需要计算每个数据点与其邻近点的距离,并计算密度比值。孤立森林的计算复杂度相对较低,因为它只需要构建一棵二叉树,并计算路径长度。

    • 检测效果
      LOF对于局部异常点的检测效果较好,孤立森林对于全局异常点的检测效果较好。

    • 适用性:LOF适用于那些异常点在局部区域内密度显著不同的数据集。孤立森林适用于那些异常点在整个数据集中分布较为稀疏的数据集。

依旧用到了sklearn,上篇已经介绍过,就不做过多的赘述了。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
from sklearn.datasets import make_blobs

# 创建包含异常值的示例数据
X, _ = make_blobs(n_samples=300, centers=1, cluster_std=0.5, random_state=0)
X = np.concatenate([X, np.random.uniform(low=-4, high=4, size=(15, 2))])

# Isolation Forest
iso_forest = IsolationForest(contamination=0.1, random_state=42)
iso_forest_labels = iso_forest.fit_predict(X)

# LOF
lof = LocalOutlierFactor(n_neighbors=20, contamination=0.1)
lof_labels = lof.fit_predict(X)

# 可视化
plt.figure(figsize=(12, 5))

plt.subplot(121)
plt.scatter(X[:, 0], X[:, 1], c=iso_forest_labels, cmap='viridis')
plt.title('Isolation Forest')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')

plt.subplot(122)
plt.scatter(X[:, 0], X[:, 1], c=lof_labels, cmap='viridis')
plt.title('Local Outlier Factor')
plt.xlabel('Feature 1')
plt.ylabel('Feature 2')

plt.tight_layout()
plt.show()

print(f"Isolation Forest detected {sum(iso_forest_labels == -1)} outliers")
print(f"LOF detected {sum(lof_labels == -1)} outliers")

输出结果
Isolation Forest detected 32 outliers
LOF detected 32 outliers

左为孤独森林,右为局部离群因子,紫色的点为异常点

sklearn.ensemble.IsolationForest孤独森林和sklearn.neighbors.LocalOutlierFactor局部离群因子。选用哪种方法,取决于数据的特性,所以大家可以提前画图看看,这个例子适合IsolationForest

2、 异常值修正

以下是处理异常值的几种常用策略的核心代码示例:

删除异常值

# 假设 df 是一个包含异常值的 DataFrame
# 删除某一列中所有异常值,这里以 'A' 列为例
df = df[df['A'] < some_threshold]  # 将 some_threshold 替换为适当的阈值

替换异常值

# 使用均值替换异常值
mean_value = df['A'].mean()
df['A'] = df['A'].apply(lambda x: mean_value if x > some_threshold else x)

# 使用中位数替换异常值
median_value = df['A'].median()
df['A'] = df['A'].apply(lambda x: median_value if x > some_threshold else x)

变换数据

# 对数变换,适用于正数数据
df['A'] = np.log1p(df['A'])  # 使用 np.log1p 以避免 log(0) 的情况

# 对数变换后,可能需要再次处理异常值

分箱(离散化)

# 使用 pandas 的 cut 函数进行分箱
bins = [-float('inf'), some_lower_threshold, some_upper_threshold, float('inf')]
labels = ['Low', 'Medium', 'High']
df['A_binned'] = pd.cut(df['A'], bins=bins, labels=labels, right=False)

代码示例提供了处理异常值的基本方法,但在实际应用中,选择策略时需考虑数据特性、异常值产生的原因和对模型的潜在影响。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354

推荐阅读更多精彩内容