直接將所有 Python 程式寫在工作簿內的第一種 TabPy 使用方法我們已經學會了,但這種方法的最大缺點是難以管控程式碼,無法將程式碼提供給多個工作簿共用。這裡要分享的是第二種 TabPy 使用方式,以佈署函式的方式讓我們可以將程式碼集中管理。
第二種使用方式就是向 TabPy Server 先註冊 Python 函式來提前佈署,佈署好的函式在 TabPy 中就會被視為一個 Model,Tableau Desktop 只需要指定要用來處理資料的 Model 名稱,即可等待運算結果回傳。
建立一個名稱為 TabPyTest.py
的 Python 檔案,內容如下
from tabpy.tabpy_tools.client import Client
client = Client('http://localhost:9004/')
def foo(data1, data2):
return True
client.deploy('foo', foo, 'This is the test function.')
Client(url)
:建立一個 TabPy Client 物件,並指定對 url
連線,TabPy 目前不接受遠端佈署,只能對在 localhost
的 TabPy Server 佈署函式,所以這裡的 url
網域必為 localhost
。foo(data1, data2)
:自行建立可接受兩個輸入參數的函式,與第一種 TabPy 使用方法不同的是,參數名稱可以自定義,這裡分別命名為 data1
與 data2
。client.deploy(model_name, function, model_description)
:model_name
為佈署後的 Model 名稱,可以選擇與函式不同的名稱,但建議為有意義並且容易懂的詞彙;function
為要佈署的函式;model_description
為 Model 的補充敘述。執行佈署 (需在有安裝 TabPy 的虛擬環境中執行)
(Tableau-Python-Server) C:\Users\wrxue>python TabPyTest.py
若佈署成功,在 http://localhost:9004/ 的 Deployed Models 區域應該就會看到新增一個名為 foo
的 Model,也就是我們在 TabPyTest.py
內的 foo
函式
"foo": {
"description": "This is the test function.",
"type": "model",
"version": 1,
"dependencies": [],
"target": null,
"creation_time": 1626685276,
"last_modified_time": 1626685276,
"schema": null,
"docstring": "-- no docstring found in query function --"
}
理解佈署的概念與流程之後,便能將我們在方法一使用到的 Python 包裝成四個不同的函式來佈署,將 TabPyTest.py
修改為如下內容後執行佈署
from tabpy.tabpy_tools.client import Client
client = Client('http://localhost:9004/')
def testBool(data):
return [x > 10000 for x in data]
def testInt(data):
return [int(x * 2) for x in data]
def testReal(data):
import math
return [math.sqrt(x) for x in data]
def testStr(data1, data2):
return [f'{x[1]} 的銷售額為 {int(x[0])}' for x in zip(data1, data2)]
client.deploy('test_SCRIPT_BOOL', testBool, 'Test SCRIPT_BOOL by deployment')
client.deploy('test_SCRIPT_INT', testInt, 'Test SCRIPT_INT by deployment')
client.deploy('test_SCRIPT_REAL', testReal, 'Test SCRIPT_REAL by deployment')
client.deploy('test_SCRIPT_STR', testStr, 'Test SCRIPT_STR by deployment')
TabPy Server 的 Deployed Models 會跟著新增 4 個 Models,分別為 test_SCRIPT_BOOL
、test_SCRIPT_INT
、test_SCRIPT_REAL
與 test_SCRIPT_STR
"test_SCRIPT_BOOL": {
"description": "Test SCRIPT_BOOL by deploy",
"type": "model",
"version": 1,
"dependencies": [],
"target": null,
"creation_time": 1626686885,
"last_modified_time": 1626686885,
"schema": null,
"docstring": "-- no docstring found in query function --"
},
"test_SCRIPT_INT": {
"description": "",
"type": "model",
"version": 1,
"dependencies": [],
"target": null,
"creation_time": 1626686886,
"last_modified_time": 1626686886,
"schema": null,
"docstring": "-- no docstring found in query function --"
},
"test_SCRIPT_REAL": {
"description": "",
"type": "model",
"version": 1,
"dependencies": [],
"target": null,
"creation_time": 1626686886,
"last_modified_time": 1626686886,
"schema": null,
"docstring": "-- no docstring found in query function --"
},
"test_SCRIPT_STR": {
"description": "",
"type": "model",
"version": 1,
"dependencies": [],
"target": null,
"creation_time": 1626686886,
"last_modified_time": 1626686886,
"schema": null,
"docstring": "-- no docstring found in query function --"
}
修改工作簿中 4 個與 SCRIPT 函式有關的 Calculated Field
SCRIPT_BOOL("return tabpy.query('test_SCRIPT_BOOL', _arg1)['response']", SUM([Sales]))
SCRIPT_INT("return tabpy.query('test_SCRIPT_INT', _arg1)['response']", SUM([Sales]))
SCRIPT_REAL("return tabpy.query('test_SCRIPT_REAL', _arg1)['response']", SUM([Sales]))
SCRIPT_STR("return tabpy.query('test_SCRIPT_STR', _arg1, _arg2)['response']"
, SUM([Sales]), ATTR([State]))
此時的效果就與 [Day24] Tableau 輕鬆學 - TabPy 使用方法 1 的效果是一樣的,只是使用 Python 的方式不同而已。
當我們想要直接重新佈署已經存在的 Model,會出現錯誤訊息如下,大意是說已經有相同名稱的 Model 存在
RuntimeError: An endpoint with that name (test_SCRIPT_BOOL) already exists. Use "override = True" to force update an existing endpoint.
這時候我們有兩個方法讓佈署能夠成功,一個方法是直接覆蓋掉現有的 Model,另一個方法則是先移除現存的 Model 再行佈署。
只需在 client.deploy
加上 override
參數,允許它可以覆蓋現有的 Model
client.deploy('test_SCRIPT_BOOL', testBool, 'Test SCRIPT_BOOL by deployment', override=True)
覆蓋後,若仔細觀察 Deployed Models 中的 test_SCRIPT_BOOL
,會看到它的 version
變為 2,這是因為每次覆蓋會造成版次自動加 1
"test_SCRIPT_BOOL": {
"description": "Test SCRIPT_BOOL by deployment",
"type": "model",
"version": 2,
"dependencies": [],
"target": null,
"creation_time": 1626686885,
"last_modified_time": 1626696002,
"schema": null,
"docstring": "-- no docstring found in query function --"
},
在 client.deploy
之前先呼叫移除 Model 的函式便能將 Model 名稱空出來,避免 Model 撞名導致無法佈署
client.remove('test_SCRIPT_BOOL')
這裡介紹的 TabPy 使用方法讓我們可以集中管理 Python 程式碼,使工作簿可以共用相同的函式。但這種方法不容易得知有哪些工作簿使用到對應的 Model,無法快速知道若將 Model 進行更新對應需要修改的工作簿有哪些。我個人認為兩種 TabPy 方法可以並行採用,若程式碼不會被重複使用,可以考慮直接寫在工作簿內,而會被重複使用的程式碼還是以佈署的方式為主,維護上會比較方便。
在實作中遇到困難是難免的,這裡提供原始檔作為參考,若仍然無法解決歡迎至下方討論區留言。