记一次异常数据过滤算法的优化


这段时间公司给了个新的需求:对现有的小区均价异常数据过滤算法进行优化,因为现在的过滤算法过滤了大量正常数据,导致均价变化幅度很大,引起客户的怀疑。

算法目标

过滤算法的目标是过滤小区房子的错误数据。由于我们的数据来源是中介网站,可以认为错误数据不会很多。另外,如果有错误数据,也认为这是很容易辨识出来的数据。

过滤算法 V1.0

现有的过滤算法的基本思想是计算幅度。先将小区中房子的成交价按照时间排序,并选取一个月的价格序列,计算相邻价格之间的变化幅度,按照一定的规则确定异常数据。规则如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 对所有的房子价格求幅度
for n in range(1, len(price_list)-1):
# 计算前项的幅度
fn1 = price_list[n] / price_list[n-1] - 1
# 计算后项的幅度
fn2 = 1 - price_list[n] / price_list[n+1]
# 计算平均的幅度
fn3 = (price_list[n-1] + price_list[n+1]) / 2 / price_list[n]
# 如果满足下式,则n必为噪点
if abs(fn1) > a1 and abs(fn2) > b1 and abs(fn3) > c1 and fn1*fn2 < 0:
noise_index_list.append(n)
continue
# 如果满足下式,则n可能为噪点
if abs(fn1) > a2 and abs(fn2) < b2 and abs(fn3) < c2:
# 计算n+1的相关指标
x = n + 1
if x < len(price_list)-1:
fx1 = price_list[x] / price_list[x-1] - 1
fx2 = 1 - price_list[x] / price_list[x+1]
fx3 = (price_list[x-1] + price_list[x+1]) / 2 / price_list[x]
# 如果满足下式,则n为噪点
if abs(fx1) < a2 and abs(fx2) > b2 and abs(fx3) > c2 and fn1*fx2 < 0:
noise_index_list.append(n)

上述算法是有一定说服力的,如果某个价格B较前一个价格A上升很多,后一个价格C较B又下降很多,那么可以确定B是异常数据。但是这个算法有一个前提,就是数据整体呈上升趋势。当然,假定房价整体呈上升趋势,这也是说得过去的。
通过分析数据,发现同一个小区的房价数据虽然大体处于同一水平,但有些有明显的分化迹象。比如,某个小区一些房子单价在6万左右,另一些在4万左右。这样的小区,如果单纯地按照上述算法过滤价格,那么很可能将大量正常数据过滤掉。

过滤算法 V2.0

基于上面的发现,我考虑能否先将价格差异明显的小区均价分类,然后每个类内部再使用过滤算法进行过滤。于是,我想到了聚类。
对于这样的聚类场景,首先想到的,当然是AP算法,因为不知道聚类的个数。但是,在简单尝试对几个小区聚类之后,我放弃了,因为聚类结果不理想,类太多。在这个场景下,只有非常明显的价格差异,我们才进行分类,类过多毫无用处。我还是回到K-Means算法,不过,做了一些优化。先计算出各个聚类个数下(从5递减)的聚类中心,如果某次聚类下聚类中心相距较远,那么可以认为该聚类个数比较合理;相反,如果聚类中心相距很近,那么减少聚类个数。
在加入分类步骤后再过滤数据,使得很多正常数据得以保留,算法也更合理。但是,仍会有一些正常数据被删掉。继续分析被删掉数据,发现它们大多是与相邻聚类中心都很远的数据。比如,一个聚类中心为5万,这个类中有相邻的三个数据4万、6万、4万,它们虽然属于同一类,但是都处于边缘,导致计算它们的幅度时,仍然超出了限制,6万被删除。
这不禁让我回过头来思考,使用相邻价格的变化幅度来过滤数据的正确性。首先,在这个场景下,相邻数据的上升趋势不明显;另外,我们考虑的是房子的成交单价,一般来说,一套房子只能成交一次,相邻的价格就是两套房子的成交单价,它们其实没有那么多变化幅度上的关联。

过滤算法 V3.0

根据上面的思考,再结合我们这个场景下的过滤目标,我决定改变原有的计算变化幅度的算法,转而寻找新的算法。通过查阅网上资料,我发现了用于去除噪声的拉依达准则:假设一个集合,均值为μ,按贝塞尔公式计算出标准偏差为σ,那么,我们可以认为大部分数据都会在(μ-3σ, μ+3σ)之间,如果一个数据超出这个范围,我们就认为它是异常数据。拉依达准则主要考虑数据与均值的距离,而不是相邻点的幅度,这比较符合我们的使用场景。

结果

至此,我们得到了改进的算法:

  1. 通过价格聚类,将小区价格分类
  2. 对每个类,计算数据与类中心的距离(拉依达准则),决定是否过滤

目前,这个算法取得了最好的效果,符合过滤的预期。当然,还是有改进的余地,如果大家有什么好的建议,请不吝赐教!


坚持原创技术分享,您的支持将鼓励我继续创作!