當前位置:首頁 » 編程語言 » python關系圖

python關系圖

發布時間: 2022-11-07 07:22:58

1. python非同步有哪些方式

yield相當於return,他將相應的值返回給調用next()或者send()的調用者,從而交出了CPU使用權,而當調用者再次調用next()或者send()的時候,又會返回到yield中斷的地方,如果send有參數,還會將參數返回給yield賦值的變數,如果沒有就和next()一樣賦值為None。但是這里會遇到一個問題,就是嵌套使用generator時外層的generator需要寫大量代碼,看如下示例:
注意以下代碼均在Python3.6上運行調試

#!/usr/bin/env python# encoding:utf-8def inner_generator():
i = 0
while True:
i = yield i if i > 10: raise StopIterationdef outer_generator():
print("do something before yield")
from_inner = 0
from_outer = 1
g = inner_generator()
g.send(None) while 1: try:
from_inner = g.send(from_outer)
from_outer = yield from_inner except StopIteration: breakdef main():
g = outer_generator()
g.send(None)
i = 0
while 1: try:
i = g.send(i + 1)
print(i) except StopIteration: breakif __name__ == '__main__':
main()041

為了簡化,在Python3.3中引入了yield from

yield from

使用yield from有兩個好處,

1、可以將main中send的參數一直返回給最里層的generator,
2、同時我們也不需要再使用while循環和send (), next()來進行迭代。

我們可以將上邊的代碼修改如下:

def inner_generator():
i = 0
while True:
i = yield i if i > 10: raise StopIterationdef outer_generator():
print("do something before coroutine start") yield from inner_generator()def main():
g = outer_generator()
g.send(None)
i = 0
while 1: try:
i = g.send(i + 1)
print(i) except StopIteration: breakif __name__ == '__main__':
main()

執行結果如下:

do something before coroutine start123456789101234567891011

這里inner_generator()中執行的代碼片段我們實際就可以認為是協程,所以總的來說邏輯圖如下:

我們都知道Python由於GIL(Global Interpreter Lock)原因,其線程效率並不高,並且在*nix系統中,創建線程的開銷並不比進程小,因此在並發操作時,多線程的效率還是受到了很大制約的。所以後來人們發現通過yield來中斷代碼片段的執行,同時交出了cpu的使用權,於是協程的概念產生了。在Python3.4正式引入了協程的概念,代碼示例如下:

import asyncio# Borrowed from http://curio.readthedocs.org/en/latest/[email protected] countdown(number, n):
while n > 0:
print('T-minus', n, '({})'.format(number)) yield from asyncio.sleep(1)
n -= 1loop = asyncio.get_event_loop()
tasks = [
asyncio.ensure_future(countdown("A", 2)),
asyncio.ensure_future(countdown("B", 3))]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()12345678910111213141516

示例顯示了在Python3.4引入兩個重要概念協程和事件循環,
通過修飾符@asyncio.coroutine定義了一個協程,而通過event loop來執行tasks中所有的協程任務。之後在Python3.5引入了新的async & await語法,從而有了原生協程的概念。

async & await

在Python3.5中,引入了aync&await 語法結構,通過」aync def」可以定義一個協程代碼片段,作用類似於Python3.4中的@asyncio.coroutine修飾符,而await則相當於」yield from」。

先來看一段代碼,這個是我剛開始使用async&await語法時,寫的一段小程序。

#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time


async def wait_download(url):
response = await requets.get(url)
print("get {} response complete.".format(url))


async def main():
start = time.time()
await asyncio.wait([
wait_download("http://www.163.com"),
wait_download("http://www.mi.com"),
wait_download("http://www.google.com")])
end = time.time()
print("Complete in {} seconds".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(main())

這里會收到這樣的報錯:

Task exception was never retrieved
future: <Task finished coro=<wait_download() done, defined at asynctest.py:9> exception=TypeError("object Response can't be used in 'await' expression",)>
Traceback (most recent call last):
File "asynctest.py", line 10, in wait_download
data = await requests.get(url)
TypeError: object Response can't be used in 'await' expression123456

這是由於requests.get()函數返回的Response對象不能用於await表達式,可是如果不能用於await,還怎麼樣來實現非同步呢?
原來Python的await表達式是類似於」yield from」的東西,但是await會去做參數檢查,它要求await表達式中的對象必須是awaitable的,那啥是awaitable呢? awaitable對象必須滿足如下條件中其中之一:

1、A native coroutine object returned from a native coroutine function .

原生協程對象

2、A generator-based coroutine object returned from a function decorated with types.coroutine() .

types.coroutine()修飾的基於生成器的協程對象,注意不是Python3.4中asyncio.coroutine

3、An object with an await method returning an iterator.

實現了await method,並在其中返回了iterator的對象

根據這些條件定義,我們可以修改代碼如下:

#!/usr/bin/env python# encoding:utf-8import asyncioimport requestsimport time


async def download(url): # 通過async def定義的函數是原生的協程對象
response = requests.get(url)
print(response.text)


async def wait_download(url):
await download(url) # 這里download(url)就是一個原生的協程對象
print("get {} data complete.".format(url))


async def main():
start = time.time()
await asyncio.wait([
wait_download("http://www.163.com"),
wait_download("http://www.mi.com"),
wait_download("http://www.google.com")])
end = time.time()
print("Complete in {} seconds".format(end - start))


loop = asyncio.get_event_loop()
loop.run_until_complete(main())27282930

好了現在一個真正的實現了非同步編程的小程序終於誕生了。
而目前更牛逼的非同步是使用uvloop或者pyuv,這兩個最新的Python庫都是libuv實現的,可以提供更加高效的event loop。

uvloop和pyuv

pyuv實現了Python2.x和3.x,但是該項目在github上已經許久沒有更新了,不知道是否還有人在維護。
uvloop只實現了3.x, 但是該項目在github上始終活躍。

它們的使用也非常簡單,以uvloop為例,只需要添加以下代碼就可以了

import asyncioimport uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())123

2. python fastapi 返回echart

可以。
做一些數據可視化,想做成web,並可以動態展示之類的,於是想到了Echart,上面有許多炫酷的可視化案例,看中一個關系圖,並將部署到Flask上。
FastAPI是一個使用Python編寫的Web框架,還應用了Pythonasyncio庫中最新的優化。

3. 人工智慧和python有什麼聯系嗎

Python和人工智慧的關系,先來上兩張圖人工智慧和Python的圖。

從上圖可以看出,人工智慧包含常用機器學習和深度學習兩個很重要的模塊,而下圖中Python擁有matplotlib、Numpy、sklearn、keras等大量的庫,像pandas、sklearn、matplotlib這些庫都是做數據處理、數據分析、數據建模和繪圖的庫,基本上機器學習中對數據的爬取(scrapy)、對數據的處理和分析(pandas)、對數據的繪圖(matplotlib)和對數據的建模(sklearn)在Python中全都能找到對應的庫來進行處理。

所以,要想學習AI而不懂Python,那就相當於想學英語而不認識單詞,所以,Python學起來吧。

4. 如何用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=[]

&#8205;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()

從而得到這張圖。

5. python可視化神器——pyecharts庫

無意中從今日頭條中看到的一篇文章,可以生成簡單的圖表。據說一些大數據開發們也是經常用類似的圖表庫,畢竟有現成的,改造下就行,誰會去自己造輪子呢。

pyecharts是什麼?

pyecharts 是一個用於生成 Echarts 圖表的類庫。Echarts 是網路開源的一個數據可視化 JS 庫。用 Echarts 生成的圖可視化效果非常棒, pyecharts 是為了與 Python 進行對接,方便在 Python 中直接使用數據生成圖 。使用pyecharts可以生成獨立的網頁,也可以在flask、django中集成使用。

安裝很簡單:pip install pyecharts

如需使用 Jupyter Notebook 來展示圖表,只需要調用自身實例即可,同時兼容 Python2 和 Python3 的 Jupyter Notebook 環境。所有圖表均可正常顯示,與瀏覽器一致的交互體驗,簡直不要太強大。

參考自pyecharts官方文檔: http://pyecharts.org

首先開始來繪制你的第一個圖表

使用 Jupyter Notebook 來展示圖表,只需要調用自身實例即可

add() 主要方法,用於添加圖表的數據和設置各種配置項

render() 默認將會在根目錄下生成一個 render.html 的文件,文件用瀏覽器打開。

使用主題

自 0.5.2+ 起,pyecharts 支持更換主體色系

使用 pyecharts-snapshot 插件

如果想直接將圖片保存為 png, pdf, gif 格式的文件,可以使用 pyecharts-snapshot。使用該插件請確保你的系統上已經安裝了 Nodejs 環境。

安裝 phantomjs $ npm install -g phantomjs-prebuilt

安裝 pyecharts-snapshot $ pip install pyecharts-snapshot

調用 render 方法 bar.render(path='snapshot.png') 文件結尾可以為 svg/jpeg/png/pdf/gif。請注意,svg 文件需要你在初始化 bar 的時候設置 renderer='svg'。

圖形繪制過程

基本上所有的圖表類型都是這樣繪制的:

chart_name = Type() 初始化具體類型圖表。

add() 添加數據及配置項。

render() 生成本地文件(html/svg/jpeg/png/pdf/gif)。

add() 數據一般為兩個列表(長度一致)。如果你的數據是字典或者是帶元組的字典。可利用 cast() 方法轉換。

多次顯示圖表

從 v0.4.0+ 開始,pyecharts 重構了渲染的內部邏輯,改善效率。推薦使用以下方式顯示多個圖表。如果使是 Numpy 或者 Pandas,可以參考這個示例

當然你也可以採用更加酷炫的方式,使用 Jupyter Notebook 來展示圖表,matplotlib 有的,pyecharts 也會有的

Note: 從 v0.1.9.2 版本開始,廢棄 render_notebook() 方法,現已採用更加  pythonic  的做法。直接調用本身實例就可以了。

比如這樣

還有這樣

如果使用的是自定義類,直接調用自定義類示例即可

圖表配置

圖形初始化

通用配置項

xyAxis:平面直角坐標系中的 x、y 軸。(Line、Bar、Scatter、EffectScatter、Kline)

dataZoom:dataZoom 組件 用於區域縮放,從而能自由關注細節的數據信息,或者概覽數據整體,或者去除離群點的影響。(Line、Bar、Scatter、EffectScatter、Kline、Boxplot)

legend:圖例組件。圖例組件展現了不同系列的標記(symbol),顏色和名字。可以通過點擊圖例控制哪些系列不顯示。

label:圖形上的文本標簽,可用於說明圖形的一些數據信息,比如值,名稱等。

lineStyle:帶線圖形的線的風格選項(Line、Polar、Radar、Graph、Parallel)

grid3D:3D笛卡爾坐標系組配置項,適用於 3D 圖形。(Bar3D, Line3D, Scatter3D)

axis3D:3D 笛卡爾坐標系 X,Y,Z 軸配置項,適用於 3D 圖形。(Bar3D, Line3D, Scatter3D)

visualMap:是視覺映射組件,用於進行『視覺編碼』,也就是將數據映射到視覺元素(視覺通道)

markLine&markPoint:圖形標記組件,用於標記指定的特殊數據,有標記線和標記點兩種。(Bar、Line、Kline)

tooltip:提示框組件,用於移動或點擊滑鼠時彈出數據內容

toolbox:右側實用工具箱

圖表詳細

Bar(柱狀圖/條形圖)

Bar3D(3D 柱狀圖)

Boxplot(箱形圖)

EffectScatter(帶有漣漪特效動畫的散點圖)

Funnel(漏斗圖)

Gauge(儀表盤)

Geo(地理坐標系)

GeoLines(地理坐標系線圖)

Graph(關系圖)

HeatMap(熱力圖)

Kline/Candlestick(K線圖)

Line(折線/面積圖)

Line3D(3D 折線圖)

Liquid(水球圖)

Map(地圖)

Parallel(平行坐標系)

Pie(餅圖)

Polar(極坐標系)

Radar(雷達圖)

Sankey(桑基圖)

Scatter(散點圖)

Scatter3D(3D 散點圖)

ThemeRiver(主題河流圖)

TreeMap(矩形樹圖)

WordCloud(詞雲圖)

用戶自定義

Grid 類:並行顯示多張圖

Overlap 類:結合不同類型圖表疊加畫在同張圖上

Page 類:同一網頁按順序展示多圖

Timeline 類:提供時間線輪播多張圖

統一風格

註:pyecharts v0.3.2以後,pyecharts 將不再自帶地圖 js 文件。如用戶需要用到地圖圖表,可自行安裝對應的地圖文件包。

地圖文件被分成了三個 Python 包,分別為:

全球國家地圖:

echarts-countries-pypkg

中國省級地圖:

echarts-china-provinces-pypkg

中國市級地圖:

echarts-china-cities-pypkg

直接使用python的pip安裝

但是這里大家一定要注意,安裝完地圖包以後一定要重啟jupyter notebook,不然是無法顯示地圖的。

顯示如下:

總得來說,這是一個非常強大的可視化庫,既可以集成在flask、Django開發中,也可以在做數據分析的時候單獨使用,實在是居家旅行的必備神器啊

6. 如何用 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 元編程,那樣會更接近於作者的實現,但也會讓程序的復雜性大為增加。如果讀者有興趣,不妨對照著去讀讀原作者的版本。

7. 知識圖譜可以用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自學網!!

8. 這種漂亮的網路關系圖怎麼畫的用什麼軟體畫出來的

推薦比較常用的幾個工具,

一個是 python 的 NetworkX 庫

另一個是 Gephi 這個軟體。

NetworkX

這是一款Python的軟體包,用於創造、操作復雜網路,以及學習復雜網路的結構、動力學及其功能。

有了NetworkX你就可以用標准或者不標準的數據格式載入或者存儲網路,它可以產生許多種類的隨機網路或經典網路,也可以分析網路結構,建立網路模型,設計新的網路演算法,繪制網路等等。可以查看官方文檔


望採納,謝謝~

9. python中的庫是什麼意思

初學python的小夥伴一定遇到這樣一個問題,python模塊,python包,python庫...感覺被繞暈了,今天說一說python中的模塊,庫,包有什麼區別。
1.python模塊是:
python模塊:包含並且有組織的代碼片段為模塊。
表現形式為:寫的代碼保存為文件。這個文件就是一個模塊。sample.py 其中文件名smaple為模塊名字。
關系圖:
2.python包是:
包是一個有層次的文件目錄結構,它定義了由n個模塊或n個子包組成的python應用程序執行環境。通俗一點:包是一個包含__init__.py 文件的目錄,該目錄下一定得有這個__init__.py文件和其它模塊或子包。
常見問題:
引入某一特定路徑下的模塊
使用sys.path.append(yourmolepath)
將一個路徑加入到python系統路徑下,避免每次通過代碼指定路徑
利用系統環境變數 export PYTHONPATH=$PYTHONPATH:yourmolepath,
直接將這個路徑鏈接到類似/Library/Python/2.7/site-packages目錄下
好的建議:
經常使用if __name__ == '__main__',保證寫包既可以import又可以獨立運行,用於test。
多次import不會多次執行模塊,只會執行一次。可以使用reload來強制運行模塊,但不提倡。
常見的包結構如下:
package_a├── __init__.py├── mole_a1.py└── mole_a2.pypackage_b├── __init__.py├── mole_b1.py└── mole_b2.py
main.py
如果main.py想要引用packagea中的模塊molea1,可以使用:
from package_a import mole_a1
import package_a.mole_a1
如果packagea中的molea1需要引用packageb,那麼默認情況下,python是找不到packageb。我們可以使用sys.path.append('../'),可以在packagea中的__init__.py添加這句話,然後該包下得所有mole都添加* import __init_即可。
關系圖:
3、庫(pbrary)
庫的概念是具有相關功能模塊的集合。這也是Python的一大特色之一,即具有強大的標准庫、第三方庫以及自定義模塊。以上就是小編分享的關於python中的庫是什麼意思的詳細內容希望對大家有所幫助,更多有關python教程請關注環球青藤其它相關文章!

熱點內容
壓縮氣翻譯 發布:2025-01-11 19:42:51 瀏覽:743
安卓如何正確卡槍 發布:2025-01-11 19:29:57 瀏覽:749
米家小相機存儲卡 發布:2025-01-11 19:22:30 瀏覽:698
我的世界如何輸地圖密碼 發布:2025-01-11 19:13:21 瀏覽:225
php表單注冊 發布:2025-01-11 18:43:02 瀏覽:161
虛擬存儲功能 發布:2025-01-11 18:43:01 瀏覽:888
ninjaandroid 發布:2025-01-11 18:26:10 瀏覽:527
華為的編譯器可以用幾個軟體 發布:2025-01-11 18:18:18 瀏覽:620
python中的turtle 發布:2025-01-11 18:06:08 瀏覽:399
羅布樂思賬號密碼手機號多少 發布:2025-01-11 18:00:55 瀏覽:403