DBSCAN算法学习与运用(小白初试)

DBSCAN算法学习与运用(小白初试),第1张

DBSCAN算法学习与运用(小白初试)

背景:有个需求是把几个行为发生的时间在一定范围的,分到一个团体,比如两个人做这件事的时间差是在15分钟以内,那就算是一个团体。另外一个人和这两个时间比较,如果是在15分钟以内,那他也是这个团体的。难点是,要确定来的每一个人都要和团体里已有的人的时间内比较到,万幸是时间是有序的。一开始想的太简单,本来想用sql语句直接搞出来,结果就跑远了。用java吧,一堆东西下去也差不多。所以就想着加个python用算法搞,网上找了一遍聚类的算法。发现DBSCAN挺符合需求的,虽然我这个都用不到多少它的优点…

DBSCAN介绍(原处)

一开始在网上看到一个挺不错的聚类算法介绍博客:https://blog.csdn.net/u011511601/article/details/81951939
很易懂。本来没注意那个笑脸的,后来找一圈回来,发现维度控制成一维的就很符合我要的东西了。

DBSCAN(Density-based Spatial Clustering of Applications with Noise,具有噪声的基于密度的聚类方法)是一种基于密度的空间聚类算法。 该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据库中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。

有个算法体验的网址,可以去玩玩。https://www.naftaliharris.com/blog/visualizing-dbscan-clustering/

1.算法的含义,与其他聚类算法的差别

文字看不懂看下面这个图。下面这些点是分布在样本空间的众多样本,现在我们的目标是把这些在样本空间中距离相近的聚成一类。我们发现A点附近的点密度较大,红色的圆圈根据一定的规则在这里滚啊滚,最终收纳了A附近的5个点,标记为红色也就是定为同一个簇。其它没有被收纳的根据一样的规则成簇。(形象来说,我们可以认为这是系统在众多样本点中随机选中一个,围绕这个被选中的样本点画一个圆,规定这个圆的半径以及圆内最少包含的样本点,如果在指定半径内有足够多的样本点在内,那么这个圆圈的圆心就转移到这个内部样本点,继续去圈附近其它的样本点,类似传销一样,继续去发展下线。等到这个滚来滚去的圈发现所圈住的样本点数量少于预先指定的值,就停止了。那么我们称最开始那个点为核心点,如A,停下来的那个点为边界点,如B、C,没得滚的那个点为离群点,如N)。

基于密度这点有什么好处呢,我们知道kmeans聚类算法只能处理球形的簇,也就是一个聚成实心的团(这是因为算法本身计算平均距离的局限)。但往往现实中还会有各种形状,比如下面两张图,环形和不规则形,这个时候,那些传统的聚类算法显然就悲剧了。于是就思考,样本密度大的成一类呗。这就是DBSCAN聚类算法。

2.参数的选择

上面提到了红色圆圈滚啊滚的过程,这个过程就包括了DBSCAN算法的两个参数,这两个参数比较难指定,公认的指定方法简单说一下:

半径:半径是最难指定的 ,大了,圈住的就多了,簇的个数就少了;反之,簇的个数就多了,这对我们最后的结果是有影响的。我们这个时候K距离可以帮助我们来设定半径r,也就是要找到突变点,比如:

以上虽然是一个可取的方式,但是有时候比较麻烦 ,大部分还是都试一试进行观察,用k距离需要做大量实验来观察,很难一次性把这些值都选准。

MinPts:这个参数就是圈住的点的个数,也相当于是一个密度,一般这个值都是偏小一些,然后进行多次尝试

DBSCAN运用

我也只是个小白,代码什么的将就看一下吧

from urllib import parse
import pymysql

# 打开数据库,参数依次为:主机名/IP,用户名,密码,数据库名,字符集
db = pymysql.connect(host="", user="", password="", charset='utf8')
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()

# 数据准备
# 使用 execute() 方法执行 SQL 查询
cursor.execute(
    """
    这里是直接查出你要分析的数据
	""")
	
import pandas as pd

result = cursor.fetchall()
frame = pd.Dataframe(list(result))
frame.columns = ['columns1', 'columns2', 'columns3', 'create_date', 'update_date', 'rn', 'time_type', 'rownumber']

# 算法分析数据
F = pd.Dataframe()
i = 1
# 循环将不同分组('rownumber')的数据放入计算
while i <= frame['rownumber'].max():
   _frame = frame[frame['rownumber'] == i].copy()
   X = _frame[['time_type', 'bid_id', 'create_date', 'update_date', 'rownumber']]
	# 半径是最难分析出来的,只有一个维度的话,很好理解,多维度就很难确定,我这里本来是只有一个差异维度的。后来又加了一个,这时直接用时间戳当距离就已经有点不规范了,算是小白试个水
   # 设置半径为900(把时间改成时间戳,在数轴上有一定顺序,也符合距离计算),最小样本量为1(半径内另外有一个算是一个聚类),建模 
   dsc = DBSCAN(eps=900, min_samples=1).fit(X)
   labels = dsc.labels_
   _frame['group_no'] = labels  # 在数据集最后一列加上经过DBSCAN聚类后的结果
   _frame.sort_values('group_no')
   F = F.append(_frame)
   i += 1
# print(F) # 展示数据

# 删除表中数据,为插入做准备,现在是每日全量
sql = "DELETE FROM db.table " 
try:
   # 执行SQL语句
   cursor.execute(sql)
   # 提交修改
   db.commit()
except:
   # 发生错误时回滚
   db.rollback()
# 关闭连接
cursor.close()
db.close()


# 将结果集导入数据库
from sqlalchemy import create_engine

# Dataframe插入数据,踩了不少坑
engine = create_engine('mysql+pymysql://user:%s@post/db ?charset=utf8'%parse.quote_plus('psw'))
# 可用于to_sql和read_sql
frame.to_sql(name='table',con = engine,if_exists = 'append',index = False,index_label = False)
print("更新成功")

代码不重要,只是提供一个参考,理解最重要,怎么运用和得到自己需要的结果还要好好学习。我也只是初步阶段,欢迎指正

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/3971892.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-10-21
下一篇 2022-10-21

发表评论

登录后才能评论

评论列表(0条)

保存