背景
上学的时候没有好好学高数,没有学图论,程序开发没有学算法,真不好意思说自己是学计算机的,不过我猜你也一样,哈哈。有必要还是应该好好学学数学和算法,好吧又一个Flag已立。
吹水结束,进入正题,最近在做一个数据分析的研究过程中,遇到一个大约有3万个节点和11万条变的关系网,而我手中的数据简化一下大概是如下的格式:
节点1 | 节点2 |
---|---|
a | b |
a | d |
b | a |
b | d |
b | c |
b | e |
c | b |
c | d |
d | a |
d | b |
d | c |
d | f |
关系网对应的图形如下:
需求
我所需要做的就是从给定的节点出发,从11万条关系数据中挑出选定节点处于同一关系网的不重复的两两对应关系,这样就可以使用工具画出上面的关系网图形了。
也就是对于上面的表格,得到如下的表格形式:
节点1 | 节点2 |
---|---|
a | b |
a | d |
b | d |
b | c |
b | e |
c | d |
d | f |
解决
数据的处理我使用Python来完成,通过函数递归调用来完成所有11万条关系的遍历,由于对图论相关算法不了解,所以自己设计算法,最终用一个看上去比较笨的办法成功解决的问题。具体如下:
1、描述
算法的核心是从数据库中查出节点1这一列中所有等于a的行,然后再将每行中节点b中的值逐个作为参数带入函数进行递归查询,直到查询结束。为了因为存在a–>b,b–>a这样的数据会造成递归无限循环,因此,每次查询的时候需要有两个参数,第一个是本次查询的节点1值,另一个则是节点1的值作为上层节点2的值时所对应的节点1的值。同时为了防止跳过好几层后出现之前查询过值再次作为节点1的值进行查询,需要单独创建一个空的列表,将已经查询过的值插入列表,每次查询前检查,已经查询过的就不再进行查询,这里描述的比较绕口,可以直接跳过看代码。
2、代码
global relationship
relationship = [] #设置全局变量relationship用来保存最终导出的关系
global notList
notList = [] #设置全局变量notList用来保存已经作为节点1查询过的节点值,防止出现递归造成的死循环
def getTree(node1,node2):
returnout = [] #为了防止递归层数太深导致的mysql连接数过大,使用returnout变量先将每次查询的关系存入列表
db= pymysql.connect(host="localhost",user="root", password="root",db="ant",port=3306)
cur = db.cursor()
sqlstring = "SELECT * FROM test WHERE node1='"+node1+"' and node2!= '"+node2+"';"
cur.execute(sqlstring)
for eachiline in cur:
returnout.append([eachiline[1],eachiline[2]])
cur.close()
db.close()
for each in returnout: #遍历数据库查询结果,并对符合条件的数据进行深层次递归
relationship.append([each[0],each[1]]) #将查询结果保存在导出列表中
print("[*]已统计"+str(len(relationship))+"对关系")
notList.append(each[0]) #将已经查询过的节点1值保存进notList列表
if each[1] not in notList: #检查节点2的值是否在notList列表中,如果不在就继续进行递归调用
getTree(each[1],each[0]) #递归调用时,将节点一和节点二的值对调带入函数进行下一层的查询,同时除去a-->b,b-->a这样的关系对,保证只出现一次。
getTree('a','')
使用测试数据执行成功后,最终从给定节点出发,从11万关系数据中挑选出与其相关的关系共4万多条,画出了一个十分庞大的关系网。
总结
问题时成功解决了,但是感觉有些啰嗦,如果你看到这篇文章且正好了解相关的算法,或者我写的方法就是其中某种算法值是存在更简洁的写法请一定留言告诉我。如果以后学习过程中有了新的认识,我也会及时在这里更大家更新分享的。