DensityPeaks是一种基于密度的聚类方法,其也可以用于检测异常值。其基本思想就是认为那些距离样本群落较远且自身周围又没有多少样本的样本点很有可能就是异常值。要说明DensityPeaks,首先说明密度的概念,什么叫密度呢,实际上就是给定某个半径,则某个样本点的密度定义为以此样本点为圆心,以半径
做圆,落入圆内的样本点个数叫做这个样本点的密度,其数学定义如下所示:
则为第i个样本点的密度。接下来,需要介绍每个样本点的另一个测量值
,其反应了当前样本点与所有密度大于它的样本点的距离的最小值,其数学表示如下所示:
但是,对于密度最大点的值定义为所有样本点与其距离的最大值,如下所示:
那么,通过以及
这两个指标就可以知道某个样本点是否距离其他样本群较远,其周围样本数目的多少。实际上那些距离较远,自身密度较大的样本点就可以作为聚类中心来进行聚类。例如找出密度
及距离
都很大的样本点作为聚类中心,可以采取DBSCAN找到其密度可达样本点集合从而将数据进行聚类。同理,我们可以将那些密度
较小,单距离
较大的样本点识别为异常值。
通过DensityPeaks识别异常值的python代码如下:
# coding: utf-8
# In[1]:
import numpy as np
from numpy import random as rd
import matplotlib.pyplot as plt
# In[2]:
def generate_samples():
samples_1 = rd.normal(1, 0.05, (12, 2))
samples_2 = rd.normal(1.4, 0.05, (12, 2))
samples = np.concatenate([samples_1, samples_2], axis=0)
fig = plt.figure(figsize=(8, 6))
ax = plt.subplot(1, 1, 1)
ax.scatter(samples[:, 0], samples[:, 1])
for i in range(samples.shape[0]):
plt.text(samples[i, 0], samples[i, 1], i)
plt.show()
return samples
# In[3]:
samples = generate_samples()
# In[4]:
def get_density(samples, r):
densities = []
for sample in samples:
densities.append(list(np.sqrt(np.sum(np.square(sample - samples), axis=1)) < r).count(True))
return densities
def get_delta(densities, samples):
max_index = np.argmax(densities)
deltas = []
for i in range(len(densities)):
if densities[i] == densities[max_index]:
deltas.append(np.max(np.sqrt(np.sum(np.square(samples[i] - samples), axis=1))))
continue
deltas.append(np.min(np.sqrt(np.sum(np.square(samples[i] - samples[densities[i] < np.array(densities)]), axis=1))))
return deltas
# In[5]:
densities = get_density(samples, 0.1)
deltas = get_delta(densities, samples)
# In[6]:
fig = plt.figure(figsize=(10, 6))
ax = plt.subplot(1, 1, 1)
ax.scatter(densities, deltas, alpha=0.5)
ax.set_xlabel("densities")
ax.set_ylabel("deltas")
for i in range(len(deltas)):
plt.text(densities[i] * 1.01, deltas[i] * 1.01, i)
plt.show()
# In[7]:
def find_outlier_index(densities, deltas):
"""
找异常值点索引,如果密度过小,且距离比自己密度大的所有样本点的距离的最小值过大则可能为异常值
"""
min_density = np.min(densities)
max_delta = np.max(deltas)
density_thresh, delta_thresh = 1.5 * min_density, 0.08 * max_delta
outlier_bool_index = list(np.logical_and(np.array(densities) < density_thresh, np.array(deltas) > delta_thresh))
outlier_index = []
count = 0
while True:
try:
i = outlier_bool_index.index(True)
except Exception:
break
else:
outlier_bool_index.remove(True)
outlier_index.append(i + len(outlier_index))
return outlier_index
# In[8]:
outlier_index = find_outlier_index(densities, deltas)
print("可能的异常值点为:", outlier_index)