导航:首页 > 编程语言 > 关系图谱模型python

关系图谱模型python

发布时间:2023-12-25 20:28:27

‘壹’ 如何用python绘制Circos图

用Python实现Circos图的绘制在线绘制的Circos有一定局限性,如对数据的要求、个性化的局限和处理速度等的问题,但如果你是一个Pythoneer或者喜欢用更加Pythonic的方式来个性化地绘制Circos图,那么今天就跟随我一起用代码实现这一目标吧!

安装Circos包

首先,登录Python的包索引网站PythonPackageIndex(PyPI,正确读音是:PiePeeAi),找到Circos包的下载页:

https://pypi.python.org/pypi/Circos/1.3.5

该包/模块的作者是我的好友EricMa。你可以选择下载wheeler文件,然后本地安装。也可以在shell下直接通过pip进行安装:

pipinstallcircos

注意,所支持的Python版本必须是3.x,对2不支持。

选择数据

当安装了circos包后,我们就可以直接应用这个包来写代码了。为了演示方便,我需要应用一些数据。作为内科医师,就让我来展示一下老本行:处理药物与肝酶细胞色素P450的相互关系的可视化。由于是为了抛砖引玉,所以绘制出的Circos图相对简单。

我们先从美国FDA官网下载不同细胞色素相关的各种口服药物表。共202种常用的口服药物,涵盖内科学、肿瘤学、神经科和心理学等学科。数据文件如下:

可以看到这个数据的结构:是按肝细胞色素酶进行分类,共分8个列。这8个细胞色素酶分别是:CYP1A2,CYP2B6,CYP2C8,CYP2C9,CYP2C19,CYP2D6,CYP2E1和CYP3A4。我们将要建立各个口服药与这些肝酶之间关系的Circos图,从而了解通过相同肝酶代谢或转化的药物之间是否存在相互作用。

导入各个模块和读入数据

导入各个模块:

fromcircosimportCircosPlot

importxlrd

importpandasaspd

importnumpyasnp

读入文件:

filename='.\MedicationInteraction.xlsx'

book=xlrd.open_workbook(filename)

print('Fileloaded!')

提取数据:

nrows=book.sheet_by_name('Sheet1').nrows

header=book.sheet_by_name('Sheet1').row_values(0)

data=[book.sheet_by_name('Sheet1').row_values(i)foriinrange(1,nrows)]

df=pd.DataFrame(data,columns=header)

df[df=='']=np.nan

读取后,药物和酶的数据为pandas的DataFrame数据结构,细胞色素P450酶的名字为columns的名字。我们可以检查一下数据:

修数据,尤其是处理NA数据

df_dict={}

foriinrange(len(df.columns)):

df_dict[df.icol(i).name]=list(df.icol(i).dropna())

节点和连线

创建节点(nodes)数据,在我这个例子里就是各个药物和肝酶:

nodes=[]

forkeyindf_dict.keys():

nodes.extend(df_dict[key])

nodes=list(nodes)

headers=list(df.columns)

enzymes=['0']*5

forheaderinheaders:

enzymes.append(header)

enzymes.extend(['0']*5)

nodes.extend(enzymes)

创建连线(edges)数据,我们应用tuple(元组)这个数据结构来表示药物与特定肝酶之间的关系:

edges_origin=[]

forkeyindf_dict.keys():

forvalueindf_dict[key]:

edges_origin.append((key,value))

绘图

绘制Circos图:

c=CircosPlot(nodes,edges_origin,radius=10,

nodecolor="blue",

edgecolor="red",

)

c.draw()

得到了下面这张所有药物与肝酶之间的图:

左上方是8个肝脏细胞色素P450酶(CYP1A2、CYP2B6、CYP2C8、CYP2C9、CYP2C19、CYP2D6、CYP2E1和CYP3A4)。其它点即为202种口服药物。每种药物都与参与代谢和转化它的P450酶相连。与相同酶连接的不同药物,理论上应该都存在相互作用,但具体如何还要看与酶的作用机理。

个性化绘图

如果我们打算分别可视化出不同肝酶的关系图形,我们只需改变连线信息,即edges信息:

edges=[]

‍forvalueindf_dict['CYP2B6']:

edges.append(('CYP2B6',value))

c=CircosPlot(nodes,edges,radius=10,

nodecolor="orange",

edgecolor="orange",

)

c.draw()

从而我们得到了各种肝酶所代谢和转化药物的图形

用PS将它们合并:

相同肝酶所代谢和转化的药物用相同颜色的edges表示。

显示特定药物

最后,我们可以挑选其中一些感兴趣的药物来进行观察,例如,我从这202个药物中指定几个我感兴趣的药物:

propafenone(心律平),acetaminophen(对乙酰氨基酚),paclitaxel(紫杉醇),ibuprofen(布洛芬),losartan(洛沙坦),omeprazole(奥美拉唑),carvediolo(卡维地洛),codeine(可待因),theophylline(茶碱),quinidine(奎尼丁),verapamil(异搏定),lovastatin(洛伐他汀),nitrendipine(尼群地平)

然后重新建立edges:

medications=['propafenone','acetaminophen','paclitaxel','ibuprofen','losartan','omeprazole','carvedilol','codeine','theophylline','quinidine','verapamil','lovastatin','nitrendipine']

edges_candidate=set()

formedicationinmedications:

foredgeinedges_origin:

ifmedication==edge[1]:

edges_candidate.add(edge)

edges_candidate=list(edges_candidate)

然后再绘图:

c=CircosPlot(nodes,edges_candidate,radius=10,

nodecolor="black",

edgecolor="black",

)

c.draw()

从而得到这张图。

‘贰’ python可视化数据分析常用图大集合(收藏)

python数据分析常用图大集合:包含折线图、直方图、垂直条形图、水平条形图、饼图、箱线图、热力图、散点图、蜘蛛图、二元变量分布、面积图、六边形图等12种常用可视化数据分析图,后期还会不断的收集整理,请关注更新!

以下默认所有的操作都先导入了numpy、pandas、matplotlib、seaborn

一、折线图

折线图可以用来表示数据随着时间变化的趋势

Matplotlib

plt.plot(x, y)

plt.show()

Seaborn

df = pd.DataFrame({'x': x, 'y': y})

sns.lineplot(x="x", y="y", data=df)

plt.show()

二、直方图

直方图是比较常见的视图,它是把横坐标等分成了一定数量的小区间,然后在每个小区间内用矩形条(bars)展示该区间的数值

Matplotlib

Seaborn

三、垂直条形图

条形图可以帮我们查看类别的特征。在条形图中,长条形的长度表示类别的频数,宽度表示类别。

Matplotlib

Seaborn

1plt.show()

四、水平条形图

五、饼图

六、箱线图

箱线图由五个数值点组成:最大值 (max)、最小值 (min)、中位数 (median) 和上下四分位数 (Q3, Q1)。

可以帮我们分析出数据的差异性、离散程度和异常值等。

Matplotlib

Seaborn

七、热力图

力图,英文叫 heat map,是一种矩阵表示方法,其中矩阵中的元素值用颜色来代表,不同的颜色代表不同大小的值。通过颜色就能直观地知道某个位置上数值的大小。

通过 seaborn 的 heatmap 函数,我们可以观察到不同年份,不同月份的乘客数量变化情况,其中颜色越浅的代表乘客数量越多

八、散点图

散点图的英文叫做 scatter plot,它将两个变量的值显示在二维坐标中,非常适合展示两个变量之间的关系。

Matplotlib

Seaborn

九、蜘蛛图

蜘蛛图是一种显示一对多关系的方法,使一个变量相对于另一个变量的显着性是清晰可见

十、二元变量分布

二元变量分布可以看两个变量之间的关系

十一、面积图

面积图又称区域图,强调数量随时间而变化的程度,也可用于引起人们对总值趋势的注意。

堆积面积图还可以显示部分与整体的关系。折线图和面积图都可以用来帮助我们对趋势进行分析,当数据集有合计关系或者你想要展示局部与整体关系的时候,使用面积图为更好的选择。

十二、六边形图

六边形图将空间中的点聚合成六边形,然后根据六边形内部的值为这些六边形上色。

原文至:https://www.py.cn/toutiao/16894.html

‘叁’ 用Python画图

今天开始琢磨用Python画图,没使用之前是一脸懵的,我使用的开发环境是Pycharm,这个输出的是一行行命令,这个图画在哪里呢?

搜索之后发现,它会弹出一个对话框,然后就开始画了,比如下图

第一个常用的库是Turtle,它是Python语言中一个很流行的绘制图像的函数库,这个词的意思就是乌龟,你可以想象下一个小乌龟在一个x和y轴的平面坐标系里,从原点开始根据指令控制,爬行出来就是绘制的图形了。

  它最常用的指令就是旋转和移动,比如画个圆,就是绕着圆心移动;再比如上图这个怎么画呢,其实主要就两个命令:

turtle.forward(200)

turtle.left(170)

第一个命令是移动200个单位并画出来轨迹

第二个命令是画笔顺时针转170度,注意此时并没有移动,只是转角度

然后呢? 循环重复就画出来这个图了

好玩吧。

有需要仔细研究的可以看下这篇文章 https://blog.csdn.net/zengxiantao1994/article/details/76588580 ,这个牛人最后用这个库画个移动的钟表,太赞了。

Turtle虽好玩,但是我想要的是我给定数据,然后让它画图,这里就找到另一个常用的画图的库了。

Matplotlib是python最着名的绘图库,它提供了一整套和matlab相似的命令API,十分适合交互式地行制图。其中,matplotlib的pyplot模块一般是最常用的,可以方便用户快速绘制二维图表。

使用起来也挺简单,

首先import matplotlib.pyplot as plt 导入画图的图。

然后给定x和y,用这个命令plt.plot(x, y)就能画图了,接着用plt.show()就可以把图形展示出来。

接着就是各种完善,比如加标题,设定x轴和y轴标签,范围,颜色,网格等等,在 https://blog.csdn.net/guoziqing506/article/details/78975150 这篇文章里介绍的很详细。

现在互联网的好处就是你需要什么内容,基本上都能搜索出来,而且还是免费的。

我为什么要研究这个呢?当然是为了用,比如我把比特币的曲线自己画出来可好?

假设现在有个数据csv文件,一列是日期,另一列是比特币的价格,那用这个命令画下:

这两列数据读到pandas中,日期为df['time']列,比特币价格为df['ini'],那我只要使用如下命令

plt.plot(df['time'], df['ini'])

plt.show()

就能得到如下图:

自己画的是不是很香,哈哈!

然后呢,我在上篇文章 https://www.jianshu.com/p/d4013d8a73de 中介绍过求Ahr999指数,那可不可以也放到这张图中呢?不就是加一条命令嘛

plt.plot(df['time'], df['Ahr999'])

图形如下:

但是,Ahr999指数怎么就一条线不动啊, 原来两个Y轴不一致,显示出来太怪了,需要用多Y轴,问题来了。

继续谷歌一下,把第二个Y轴放右边就行了,不过呢得使用多图,重新绘制

fig = plt.figure() # 多图

ax1 = fig.add_subplot(111)

ax1.plot(df['time'], df['ini'], label="BTC price")  # 绘制第一个图比特币价格

ax1.set_ylabel('BTC price') # 加上标签

# 第二个直接对称就行了

ax2 = ax1.twinx()# 在右边增加一个Y轴

ax2.plot(df['time'], df['Ahr999'], 'r', label="ahr999")  # 绘制第二个图Ahr999指数,红色

ax2.set_ylim([0, 50])# 设定第二个Y轴范围

ax2.set_ylabel('ahr999')

plt.grid(color="k", linestyle=":")# 网格

fig.legend(loc="center")#图例

plt.show()

跑起来看看效果,虽然丑了点,但终于跑通了。

这样就可以把所有指数都绘制到一张图中,等等,三个甚至多个Y轴怎么加?这又是一个问题,留给爱思考爱学习的你。

有了自己的数据,建立自己的各个指数,然后再放到图形界面中,同时针对异常情况再自动进行提醒,比如要抄底了,要卖出了,用程序做出自己的晴雨表。

‘肆’ 如何用 Python 实现一个图数据库(Graph Database)

本文章是 重写 500 Lines or Less 系列的其中一篇,目标是重写 500 Lines or Less 系列的原有项目:Dagoba: an in-memory graph database。

Dagoba 是作者设计用来展示如何从零开始自己实现一个图数据库( Graph Database )。该名字似乎来源于作者喜欢的一个乐队,另一个原因是它的前缀 DAG 也正好是有向无环图 ( Directed Acyclic Graph ) 的缩写。本文也沿用了该名称。

图是一种常见的数据结构,它将信息描述为若干独立的节点( vertex ,为了和下文的边更加对称,本文中称为 node ),以及把节点关联起来的边( edge )。我们熟悉的链表以及多种树结构可以看作是符合特定规则的图。图在路径选择、推荐算法以及神经网络等方面都是重要的核心数据结构。

既然图的用途如此广泛,一个重要的问题就是如何存储它。如果在传统的关系数据库中存储图,很自然的做法就是为节点和边各自创建一张表,并用外键把它们关联起来。这样的话,要查找某人所有的子女,就可以写下类似下面的查询:

还好,不算太复杂。但是如果要查找孙辈呢?那恐怕就要使用子查询或者 CTE(Common Table Expression) 等特殊构造了。再往下想,曾孙辈又该怎么查询?孙媳妇呢?

这样我们会意识到,SQL 作为查询语言,它只是对二维数据表这种结构而设计的,用它去查询图的话非常笨拙,很快会变得极其复杂,也难以扩展。针对图而言,我们希望有一种更为自然和直观的查询语法,类似这样:

为了高效地存储和查询图这种数据结构,图数据库( Graph Database )应运而生。因为和传统的关系型数据库存在极大的差异,所以它属于新型数据库也就是 NoSql 的一个分支(其他分支包括文档数据库、列数据库等)。图数据库的主要代表包括 Neo4J 等。本文介绍的 Dagoba 则是具备图数据库核心功能、主要用于教学和演示的一个简单的图数据库。

原文代码是使用 JavaScript 编写的,在定义调用接口时大量使用了原型( prototype )这种特有的语言构造。对于其他主流语言的用户来说,原型的用法多少显得有些别扭和不自然。

考虑到本系列其他数据库示例大多是用 Python 实现的,本文也按照传统,用 Python 重写了原文的代码。同样延续之前的惯例,为了让读者更好地理解程序是如何逐步完善的,我们用迭代式的方法完成程序的各个组成部分。

原文在 500lines 系列的 Github 仓库中只包含了实现代码,并未包含测试。按照代码注释说明,测试程序位于作者的另一个代码库中,不过和 500lines 版本的实现似乎略有不同。

本文实现的代码参考了原作者的测试内容,但跳过了北欧神话这个例子——我承认确实不熟悉这些神祇之间的亲缘关系,相信中文背景的读者们多数也未必了解,虽然作者很喜欢这个例子,想了想还是不要徒增困惑吧。因此本文在编写测试用例时只参考了原文关于家族亲属的例子,放弃了神话相关的部分,尽管会减少一些趣味性,相信对于入门级的代码来说这样也够用了。

本文实现程序位于代码库的 dagoba 目录下。按照本系列程序的同意规则,要想直接执行各个已完成的步骤,读者可以在根目录下的 main.py 找到相应的代码位置,取消注释并运行即可。

本程序的所有步骤只需要 Python3 ,测试则使用内置的 unittest , 不需要额外的第三方库。原则上 Python3.6 以上版本应该都可运行,但我只在 Python3.8.3 环境下完整测试过。

本文实现的程序从最简单的案例开始,通过每个步骤逐步扩展,最终形成一个完整的程序。这些步骤包括:

接下来依次介绍各个步骤。

回想一下,图数据库就是一些点( node )和边( edge )的集合。现在我们要做出的一个重大决策是如何对节点/边进行建模。对于边来说,必须指定它的关联关系,也就是从哪个节点指向哪个节点。大多数情况下边是有方向的——父子关系不指明方向可是要乱套的!

考虑到扩展性及通用性问题,我们可以把数据保存为字典( dict ),这样可以方便地添加用户需要的任何数据。某些数据是为数据库内部管理而保留的,为了明确区分,可以这样约定:以下划线开头的特殊字段由数据库内部维护,类似于私有成员,用户不应该自己去修改它们。这也是 Python 社区普遍遵循的约定。

此外,节点和边存在互相引用的关系。目前我们知道边会引用到两端的节点,后面还会看到,为了提高效率,节点也会引用到边。如果仅仅在内存中维护它们的关系,那么使用指针访问是很直观的,但数据库必须考虑到序列化到磁盘的问题,这时指针就不再好用了。

为此,最好按照数据库的一般要求,为每个节点维护一个主键( _id ),用主键来描述它们之间的关联关系。

我们第一步要把数据库的模型建立起来。为了测试目的,我们使用一个最简单的数据库模型,它只包含两个节点和一条边,如下所示:

按照 TDD 的原则,首先编写测试:

与原文一样,我们把数据库管理接口命名为 Dagoba 。目前,能够想到的最简单的测试是确认节点和边是否已经添加到数据库中:

assert_item 是一个辅助方法,用于检查字典是否包含预期的字段。相信大家都能想到该如何实现,这里就不再列出了,读者可参考 Github 上的完整源码

现在,测试是失败的。用最简单的办法实现数据库:

需要注意的是,不管添加节点还是查询,程序都使用了拷贝后的数据副本,而不是直接使用原始数据。为什么要这样做?因为字典是可变的,用户可以在任何时候修改其中的内容,如果数据库不知道数据已经变化,就很容易发生难以追踪的一致性问题,最糟糕的情况下会使得数据内容彻底混乱。

拷贝数据可以避免上述问题,代价则是需要占用更多内存和处理时间。对于数据库来说,通常查询次数要远远多于修改,所以这个代价是可以接受的。

现在测试应该正常通过了。为了让它更加完善,我们可以再测试一些边缘情况,看看数据库能否正确处理异常数据,比如:

例如,如果用户尝试添加重复主键,我们预期应抛出 ValueError 异常。因此编写测试如下:

为了满足以上测试,代码需要稍作修改。特别是按照 id 查找主键是个常用操作,通过遍历的方法效率太低了,最好是能够通过主键直接访问。因此在数据库中再增加一个字典:

完整代码请参考 Github 仓库。

在上个步骤,我们在初始化数据库时为节点明确指定了主键。按照数据库设计的一般原则,主键最好是不具有业务含义的代理主键( Surrogate key ),用户不应该关心它具体的值是什么,因此让数据库去管理主键通常是更为合理的。当然,在部分场景下——比如导入外部数据——明确指定主键仍然是有用的。

为了同时支持这些要求,我们这样约定:字段 _id 表示节点的主键,如果用户指定了该字段,则使用用户设置的值(当然,用户有责任保证它们不会重复);否则,由数据库自动为它分配一个主键。

如果主键是数据库生成的,事先无法预知它的值是什么,而边( edge )必须指定它所指向的节点,因此必须在主键生成后才能添加。由于这个原因,在动态生成主键的情况下,数据库的初始化会略微复杂一些。还是先写一个测试:

为支持此功能,我们在数据库中添加一个内部字段 _next_id 用于生成主键,并让 add_node 方法返回新生成的主键:

接下来,再确认一下边是否可以正常访问:

运行测试,一切正常。这个步骤很轻松地完成了,不过两个测试( DbModelTest 和 PrimaryKeyTest )出现了一些重复代码,比如 get_item 。我们可以把这些公用代码提取出来。由于 get_item 内部调用了 TestCase.assertXXX 等方法,看起来应该使用继承,但从 TestCase 派生基类容易引起一些潜在的问题,所以我转而使用另一个技巧 Mixin :

实现数据库模型之后,接下来就要考虑如何查询它了。

在设计查询时要考虑几个问题。对于图的访问来说,几乎总是由某个节点(或符合条件的某一类节点)开始,从与它相邻的边跳转到其他节点,依次类推。所以链式调用对查询来说是一种很自然的风格。举例来说,要知道 Tom 的孙子养了几只猫,可以使用类似这样的查询:

可以想象,以上每个方法都应该返回符合条件的节点集合。这种实现是很直观的,不过存在一个潜在的问题:很多时候用户只需要一小部分结果,如果它总是不计代价地给我们一个巨大的集合,会造成极大的浪费。比如以下查询:

为了避免不必要的浪费,我们需要另外一种机制,也就是通常所称的“懒式查询”或“延迟查询”。它的基本思想是,当我们调用查询方法时,它只是把查询条件记录下来,而并不立即返回结果,直到明确调用某些方法时才真正去查询数据库。

如果读者比较熟悉流行的 Python ORM,比如 SqlAlchemy 或者 Django ORM 的话,会知道它们几乎都是懒式查询的,要调用 list(result) 或者 result[0:10] 这样的方法才能得到具体的查询结果。

在 Dagoba 中把触发查询的方法定义为 run 。也就是说,以下查询执行到 run 时才真正去查找数据:

和懒式查询( Lazy Query )相对应的,直接返回结果的方法一般称作主动查询( Eager Query )。主动查询和懒式查询的内在查找逻辑基本上是相同的,区别只在于触发机制不同。由于主动查询实现起来更加简单,出错也更容易排查,因此我们先从主动查询开始实现。

还是从测试开始。前面测试所用的简单数据库数据太少,难以满足查询要求,所以这一步先来创建一个更复杂的数据模型:

此关系的复杂之处之一在于反向关联:如果 A 是 B 的哥哥,那么 B 就是 A 的弟弟/妹妹,为了查询到他们彼此之间的关系,正向关联和反向关联都需要存在,因此在初始化数据库时需要定义的边数量会很多。

当然,父子之间也存在反向关联的问题,为了让问题稍微简化一些,我们目前只需要向下(子孙辈)查找,可以稍微减少一些关联数量。

因此,我们定义数据模型如下。为了减少重复工作,我们通过 _backward 字段定义反向关联,而数据库内部为了查询方便,需要把它维护成两条边:

然后,测试一个最简单的查询,比如查找某人的所有孙辈:

这里 outcome/income 分别表示从某个节点出发、或到达它的节点集合。在原作者的代码中把上述方法称为 out/in 。当然这样看起来更加简洁,可惜的是 in 在 Python 中是个关键字,无法作为函数名。我也考虑过加个下划线比如 out_.in_ 这种形式,但看起来也有点怪异,权衡之后还是使用了稍微啰嗦一点的名称。

现在我们可以开始定义查询接口了。在前面已经说过,我们计划分别实现两种查询,包括主动查询( Eager Query )以及延迟查询( Lazy Query )。

它们的内在查询逻辑是相通的,看起来似乎可以使用继承。不过遵循 YAGNI 原则,目前先不这样做,而是只定义两个新类,在满足测试的基础上不断扩展。以后我们会看到,与继承相比,把共同的逻辑放到数据库本身其实是更为合理的。

接下来实现访问节点的方法。由于 EagerQuery 调用查询方法会立即返回结果,我们把结果记录在 _result 内部字段中。虽然 node 方法只返回单个结果,但考虑到其他查询方法几乎都是返回集合,为统一起见,让它也返回集合,这样可以避免同时支持集合与单结果的分支处理,让代码更加简洁、不容易出错。此外,如果查询对象不存在的话,我们只返回空集合,并不视为一个错误。

查询输入/输出节点的方法实现类似这样:

查找节点的核心逻辑在数据库本身定义:

以上使用了内部定义的一些辅助查询方法。用类似的逻辑再定义 income ,它们的实现都很简单,读者可以直接参考源码,此处不再赘述。

在此步骤的最后,我们再实现一个优化。当多次调用查询方法后,结果可能会返回重复的数据,很多时候这是不必要的。就像关系数据库通常支持 unique/distinct 一样,我们也希望 Dagoba 能够过滤重复的数据。

假设我们要查询某人所有孩子的祖父,显然不管有多少孩子,他们的祖父应该是同一个人。因此编写测试如下:

现在来实现 unique 。我们只要按照主键把重复数据去掉即可:

在上个步骤,初始化数据库指定了双向关联,但并未测试它们。因为我们还没有编写代码去支持它们,现在增加一个测试,它应该是失败的:

运行测试,的确失败了。我们看看要如何支持它。回想一下,当从边查找节点时,使用的是以下方法:

这里也有一个潜在的问题:调用 self.edges 意味着遍历所有边,当数据库内容较多时,这是巨大的浪费。为了提高性能,我们可以把与节点相关的边记录在节点本身,这样要查找边只要看节点本身即可。在初始化时定义出入边的集合:

在添加边时,我们要同时把它们对应的关系同时更新到节点,此外还要维护反向关联。这涉及对字典内容的部分复制,先编写一个辅助方法:

然后,将添加边的实现修改如下:

这里的代码同时添加正向关联和反向关联。有的朋友可能会注意到代码略有重复,是的,但是重复仅出现在该函数内部,本着“三则重构”的原则,暂时不去提取代码。

实现之后,前面的测试就可以正常通过了。

在这个步骤中,我们来实现延迟查询( Lazy Query )。

延迟查询的要求是,当调用查询方法时并不立即执行,而是推迟到调用特定方法,比如 run 时才执行整个查询,返回结果。

延迟查询的实现要比主动查询复杂一些。为了实现延迟查询,查询方法的实现不能直接返回结果,而是记录要执行的动作以及传入的参数,到调用 run 时再依次执行前面记录下来的内容。

如果你去看作者的实现,会发现他是用一个数据结构记录执行操作和参数,此外还有一部分逻辑用来分派对每种结构要执行的动作。这样当然是可行的,但数据处理和分派部分的实现会比较复杂,也容易出错。

本文的实现则选择了另外一种不同的方法:使用 Python 的内部函数机制,把一连串查询变换成一组函数,每个函数取上个函数的执行结果作为输入,最后一个函数的输出就是整个查询的结果。由于内部函数同时也是闭包,尽管每个查询的参数形式各不相同,但是它们都可以被闭包“捕获”而成为内部变量,所以这些内部函数可以采用统一的形式,无需再针对每种查询设计额外的数据结构,因而执行过程得到了很大程度的简化。

首先还是来编写测试。 LazyQueryTest 和 EagerQueryTest 测试用例几乎是完全相同的(是的,两种查询只在于内部实现机制不同,它们的调用接口几乎是完全一致的)。

因此我们可以把 EagerQueryTest 的测试原样不变拷贝到 LazyQueryTest 中。当然拷贝粘贴不是个好注意,对于比较冗长而固定的初始化部分,我们可以把它提取出来作为两个测试共享的公共函数。读者可参考代码中的 step04_lazy_query/tests/test_lazy_query.py 部分。

程序把查询函数的串行执行称为管道( pipeline ),用一个变量来记录它:

然后依次实现各个调用接口。每种接口的实现都是类似的:用内部函数执行真正的查询逻辑,再把这个函数添加到 pipeline 调用链中。比如 node 的实现类似下面:

其他接口的实现也与此类似。最后, run 函数负责执行所有查询,返回最终结果;

完成上述实现后执行测试,确保我们的实现是正确的。

在前面我们说过,延迟查询与主动查询相比,最大的优势是对于许多查询可以按需要访问,不需要每个步骤都返回完整结果,从而提高性能,节约查询时间。比如说,对于下面的查询:

以上查询的意思是从孙辈中找到一个符合条件的节点即可。对该查询而言,主动查询会在调用 outcome('son') 时就遍历所有节点,哪怕最后一步只需要第一个结果。而延迟查询为了提高效率,应在找到符合条件的结果后立即停止。

目前我们尚未实现 take 方法。老规矩,先添加测试:

主动查询的 take 实现比较简单,我们只要从结果中返回前 n 条记录:

延迟查询的实现要复杂一些。为了避免不必要的查找,返回结果不应该是完整的列表( list ),而应该是个按需返回的可迭代对象,我们用内置函数 next 来依次返回前 n 个结果:

写完后运行测试,确保它们是正确的。

从外部接口看,主动查询和延迟查询几乎是完全相同的,所以用单纯的数据测试很难确认后者的效率一定比前者高,用访问时间来测试也并不可靠。为了测试效率,我们引入一个节点访问次数的概念,如果延迟查询效率更高的话,那么它应该比主动查询访问节点的次数更少。

为此,编写如下测试:

我们为 Dagoba 类添加一个成员来记录总的节点访问次数,以及两个辅助方法,分别用于获取和重置访问次数:

然后浏览代码,查找修改点。增加计数主要在从边查找节点的时候,因此修改部分如下:

此外还有 income/outcome 方法,修改都很简单,这里就不再列出。

实现后再次运行测试。测试通过,表明延迟查询确实在效率上优于主动查询。

不像关系数据库的结构那样固定,图的形式可以千变万化,查询机制也必须足够灵活。从原理上讲,所有查询无非是从某个节点出发按照特定方向搜索,因此用 node/income/outcome 这三个方法几乎可以组合出任意所需的查询。

但对于复杂查询,写出的代码有时会显得较为琐碎和冗长,对于特定领域来说,往往存在更为简洁的名称,例如:母亲的兄弟可简称为舅舅。对于这些场景,如果能够类似 DSL (领域特定语言)那样允许用户根据专业要求自行扩展,从而简化查询,方便阅读,无疑会更为友好。

如果读者去看原作者的实现,会发现他是用一种特殊语法 addAlias 来定义自己想要的查询,调用方法时再进行查询以确定要执行的内容,其接口和内部实现都是相当复杂的。

而我希望有更简单的方法来实现这一点。所幸 Python 是一种高度动态的语言,允许在运行时向类中增加新的成员,因此做到这一点可能比预想的还要简单。

为了验证这一点,编写测试如下:

无需 Dagoba 的实现做任何改动,测试就可以通过了!其实我们要做的就是动态添加一个自定义的成员函数,按照 Python 对象机制的要求,成员函数的第一个成员应该是名为 self 的参数,但这里已经是在 UnitTest 的内部,为了和测试类本身的 self 相区分,新函数的参数增加了一个下划线。

此外,函数应返回其所属的对象,这是为了链式调用所要求的。我们看到,动态语言的灵活性使得添加新语法变得非常简单。

到此,一个初具规模的图数据库就形成了。

和原文相比,本文还缺少一些内容,比如如何将数据库序列化到磁盘。不过相信读者都看到了,我们的数据库内部结构基本上是简单的原生数据结构(列表+字典),因此序列化无论用 pickle 或是 JSON 之类方法都应该是相当简单的。有兴趣的读者可以自行完成它们。

我们的图数据库实现为了提高查询性能,在节点内部存储了边的指针(或者说引用)。这样做的好处是,无论数据库有多大,从一个节点到相邻节点的访问是常数时间,因此数据访问的效率非常高。

但一个潜在的问题是,如果数据库规模非常大,已经无法整个放在内存中,或者出于安全性等原因要实现分布式访问的话,那么指针就无法使用了,必须要考虑其他机制来解决这个问题。分布式数据库无论采用何种数据模型都是一个棘手的问题,在本文中我们没有涉及。有兴趣的读者也可以考虑 500lines 系列中关于分布式和集群算法的其他一些文章。

本文的实现和系列中其他数据库类似,采用 Python 作为实现语言,而原作者使用的是 JavaScript ,这应该和作者的背景有关。我相信对于大多数开发者来说, Python 的对象机制比 JavaScript 基于原型的语法应该是更容易阅读和理解的。

当然,原作者的版本比本文版本在实现上其实是更为完善的,灵活性也更好。如果想要更为优雅的实现,我们可以考虑使用 Python 元编程,那样会更接近于作者的实现,但也会让程序的复杂性大为增加。如果读者有兴趣,不妨对照着去读读原作者的版本。

‘伍’ Python 数据可视化:数据分布统计图和热图

本课将继续介绍 Seaborn 中的统计图。一定要牢记,Seaborn 是对 Matplotlib 的高级封装,它优化了很多古老的做图过程,因此才会看到一个函数解决问题的局面。

在统计学中,研究数据的分布情况,也是一个重要的工作,比如某些数据是否为正态分布——某些机器学习模型很在意数据的分布情况。

在 Matplotlib 中,可以通过绘制直方图将数据的分布情况可视化。在 Seaborn 中,也提供了绘制直方图的函数。

输出结果:

sns.distplot 函数即实现了直方图,还顺带把曲线画出来了——曲线其实代表了 KDE。

除了 sns.distplot 之外,在 Seaborn 中还有另外一个常用的绘制数据分布的函数 sns.kdeplot,它们的使用方法类似。

首先看这样一个示例。

输出结果:

① 的作用是设置所得图示的背景颜色,这样做的目的是让下面的 ② 绘制的图像显示更清晰,如果不设置 ①,在显示的图示中看到的就是白底图像,有的部分看不出来。

② 最终得到的是坐标网格,而且在图中分为三部分,如下图所示。

相对于以往的坐标网格,多出了 B 和 C 两个部分。也就是说,不仅可以在 A 部分绘制某种统计图,在 B 和 C 部分也可以绘制。

继续操作:

输出结果:

语句 ③ 实现了在坐标网格中绘制统计图的效果,jp.plot 方法以两个绘图函数为参数,分别在 A 部分绘制了回归统计图,在 B 和 C 部分绘制了直方图,而且直方图分别表示了对应坐标轴数据的分布,即:

我们把有语句 ② 和 ③ 共同实现的统计图,称为联合统计图。除了用 ② ③ 两句可以绘制这种图之外,还有一个函数也能够“两步并作一步”,具体如下:

输出结果:

‘陆’ 知识图谱可以用python构建吗

知识图谱可以用python构建吗?

答案当然是可以的!!!

那么如何使用python构建

什么是知识图谱

从Google搜索,到聊天机器人、金融风控、物联网场景、智能医疗、自适应教育、推荐系统,无一不跟知识图谱相关。它在技术领域的热度也在逐年上升。
互联网的终极形态是万物的互联,而搜索的终极目标是对万物的直接搜索。传统搜索引擎依靠网页之间的超链接实现网页的搜索,而语义搜索是直接对事物进行搜索,如人物、机构、地点等。这些事物可能来自文本、图片、视频、音频、IoT设备等各种信息资源。而知识图谱和语义技术提供了关于这些事物的分类、属性和关系的描述,使得搜索引擎可以直接对事物进行索引和搜索。
知识图谱是由Google公司在2012年提出来的一个新的概念。从学术的角度,我们可以对知识图谱给一个这样的定义:“知识图谱本质上是语义网络(Semantic Network)的知识库”。但这有点抽象,所以换个角度,从实际应用的角度出发其实可以简单地把知识图谱理解成多关系图(Multi-relational Graph)。
那什么叫多关系图呢? 学过数据结构的都应该知道什么是图(Graph)。图是由节点(Vertex)和边(Edge)来构成,但这些图通常只包含一种类型的节点和边。但相反,多关系图一般包含多种类型的节点和多种类型的边。
本项目利用pandas将excel中数据抽取,以三元组形式加载到neo4j数据库中构建相关知识图谱。

运行环境

基于Neo4j能够很容易构建知识图谱,除了用neo4j自带的cypher,也支持Python包py2neo创建节点和关系从而构建知识图谱。本项目是基于发票信息,将发票数据中结构化数据抽象成三元组,分别创建节点和关系从而构建成知识图谱。
具体包依赖可以参考文件requirements.txt

neo4j-driver==1.6.2numpy==1.15.3pandas==0.23.4parso==0.3.1pickleshare==0.7.5pluggy==0.8.0prompt-toolkit==1.0.15py==1.7.0py2neo==3Pygments==2.2.0pytest==3.9.3python-dateutil==2.7.5wcwidth==0.1.7wincertstore==0.2xlrd==1.1.0

将所需依赖安装到pyton中:pip install -r requirements.txt

Pandas抽取excel数据

python中pandas非常适用于数据分析与处理,可以将excel文件转换成dataframe格式,这种格式类似于Spark中的Dataframe结构,可以用类sql的形式对数据进行处理。
Excel数据结构如下

通过函数data_extraction和函数relation_extrantion分别抽取构建知识图谱所需要的节点数据以及联系数据,构建三元组。
数据提取主要采用pandas将excel数据转换成dataframe类型
invoice_neo4j.py

建立知识图谱所需节点和关系数据

DataToNeo4jClass.py

具体代码请移步到GitHub上下载

详细内容请到github下载,项目名neo4j-python-pandas-py2neo-v3

更多Python知识,请关注:Python自学网!!

阅读全文

与关系图谱模型python相关的资料

热点内容
电脑电销加密电话号码破解 浏览:503
世界史纲pdf 浏览:133
湖北社保年审app叫什么名字 浏览:852
迈达克云服务器 浏览:597
mfc深入浅出从mfc设计到mfc编程 浏览:81
萤石云服务器连接设置 浏览:325
中国名着pdf 浏览:592
华为服务器设备序列号怎么看 浏览:319
跑永辉生活配送用什么app 浏览:149
ug识别符号命令在哪里 浏览:719
pdf文件改文字 浏览:732
查询qq号剑灵服务器地址 浏览:552
国家反诈中心app为什么要刷脸 浏览:303
iphone怎么修改dns服务器地址 浏览:85
bandizip解压位置 浏览:168
服务器的防火墙如何访问 浏览:306
javagoto关键字 浏览:847
广州少儿编程加盟排名榜 浏览:122
51单片机th0 浏览:294
冠军交易pdf 浏览:208