0%

[Day25] Tableau 輕鬆學 - TabPy 使用方法 2

前言

直接將所有 Python 程式寫在工作簿內的第一種 TabPy 使用方法我們已經學會了,但這種方法的最大缺點是難以管控程式碼,無法將程式碼提供給多個工作簿共用。這裡要分享的是第二種 TabPy 使用方式,以佈署函式的方式讓我們可以將程式碼集中管理。

佈署函式

說明

第二種使用方式就是向 TabPy Server 先註冊 Python 函式來提前佈署,佈署好的函式在 TabPy 中就會被視為一個 Model,Tableau Desktop 只需要指定要用來處理資料的 Model 名稱,即可等待運算結果回傳。

流程

建立一個名稱為 TabPyTest.py 的 Python 檔案,內容如下

1
2
3
4
5
6
7
8
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 使用方法不同的是,參數名稱可以自定義,這裡分別命名為 data1data2
  • client.deploy(model_name, function, model_description)model_name 為佈署後的 Model 名稱,可以選擇與函式不同的名稱,但建議為有意義並且容易懂的詞彙;function 為要佈署的函式;model_description 為 Model 的補充敘述。

執行佈署 (需在有安裝 TabPy 的虛擬環境中執行)

1
(Tableau-Python-Server) C:\Users\wrxue>python TabPyTest.py

若佈署成功,在 http://localhost:9004/ 的 Deployed Models 區域應該就會看到新增一個名為 foo 的 Model,也就是我們在 TabPyTest.py 內的 foo 函式

1
2
3
4
5
6
7
8
9
10
11
"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 --"
}

使用方法

TabPy 佈署

理解佈署的概念與流程之後,便能將我們在方法一使用到的 Python 包裝成四個不同的函式來佈署,將 TabPyTest.py 修改為如下內容後執行佈署

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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_BOOLtest_SCRIPT_INTtest_SCRIPT_REALtest_SCRIPT_STR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
"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 --"
}

Tableau Desktop 呼叫

修改工作簿中 4 個與 SCRIPT 函式有關的 Calculated Field

銷售額大於10000

1
SCRIPT_BOOL("return tabpy.query('test_SCRIPT_BOOL', _arg1)['response']", SUM([Sales]))

2倍銷售額

1
SCRIPT_INT("return tabpy.query('test_SCRIPT_INT', _arg1)['response']", SUM([Sales]))

銷售額平方根

1
SCRIPT_REAL("return tabpy.query('test_SCRIPT_REAL', _arg1)['response']", SUM([Sales]))

銷售額說明

1
2
SCRIPT_STR("return tabpy.query('test_SCRIPT_STR', _arg1, _arg2)['response']"
, SUM([Sales]), ATTR([State]))

此時的效果就與 [Day24] Tableau 輕鬆學 - TabPy 使用方法 1 的效果是一樣的,只是使用 Python 的方式不同而已。

覆蓋與移除 Model

當我們想要直接重新佈署已經存在的 Model,會出現錯誤訊息如下,大意是說已經有相同名稱的 Model 存在

1
RuntimeError: An endpoint with that name (test_SCRIPT_BOOL) already exists. Use "override = True" to force update an existing endpoint.

這時候我們有兩個方法讓佈署能夠成功,一個方法是直接覆蓋掉現有的 Model,另一個方法則是先移除現存的 Model 再行佈署。

覆蓋 Model

只需在 client.deploy 加上 override 參數,允許它可以覆蓋現有的 Model

1
client.deploy('test_SCRIPT_BOOL', testBool, 'Test SCRIPT_BOOL by deployment', override=True)

覆蓋後,若仔細觀察 Deployed Models 中的 test_SCRIPT_BOOL,會看到它的 version 變為 2,這是因為每次覆蓋會造成版次自動加 1

1
2
3
4
5
6
7
8
9
10
11
"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 --"
},

移除 Model

client.deploy 之前先呼叫移除 Model 的函式便能將 Model 名稱空出來,避免 Model 撞名導致無法佈署

1
client.remove('test_SCRIPT_BOOL')

結語

這裡介紹的 TabPy 使用方法讓我們可以集中管理 Python 程式碼,使工作簿可以共用相同的函式。但這種方法不容易得知有哪些工作簿使用到對應的 Model,無法快速知道若將 Model 進行更新對應需要修改的工作簿有哪些。我個人認為兩種 TabPy 方法可以並行採用,若程式碼不會被重複使用,可以考慮直接寫在工作簿內,而會被重複使用的程式碼還是以佈署的方式為主,維護上會比較方便。

工作簿原始檔案

Workbook

在實作中遇到困難是難免的,這裡提供原始檔作為參考,若仍然無法解決歡迎至下方討論區留言。

很高興能在這裡幫助到您,歡迎登入 Liker 為我鼓掌 5 次,或者成為我的讚賞公民,鼓勵我繼續創造優質文章。
以最優質的內容回應您的鼓勵