Tableau是一个数据可视化工具,虽然其本身自带“分析”功能,但毕竟不如Python这一类工具强大。Tableau使用Python的方式主要有两种:一是,用tableau向tabpy_server发送python脚本;二是,将python脚本部署在tabpy_server上
1.相关包的安装
Tableau与Python连接,需要两个包的辅助,这里可以参考知乎这篇文章,这篇文章写的很清楚,实测有效。https://zhuanlan.zhihu.com/p/54766875
这里要补充说明一下,安装 tabpy_server和tabpy_client时,如果提示ReadTimeoutError,则需要在安装时设置延迟等待时间。具体如下:
pip --default-timeout=100 install tabpy_server
pip --default-timeout=100 install tabpy_client
2. 向tabpy_server发送python脚本
Tableau提供了四个函数SCRIPTINT、SCRIPTREAL、 SCRIPTSTR 、SCRIPT_BOOL,这四个函数负责向Python发送计算脚本,并要求Python返回特定类型的返回值。SCRIPTINT要求返回Int型,SCRIPTREAL要求返回实数类型,SCRIPTSTR要求返回字符串类型,而SCRIPT_BOOL要求返回布尔型变量。
仍然以Tableau中自带的【超市】数据为例,现在我们想要知道各个子类别的销售额总和,并且需要将销售额超过子类别平均销售额的子类别标记出来。如果不用python脚本实现的话,需要创建如下的计算字段【判断是否超过平均水平】:
最后的结果如下(中间拖拖拽拽的过程就省略不写了。)
上述的计算字段如果用python脚本实现,具体写法如下:
将这个计算字段替换视图中的【判断是否超过平均水平】,仍然可以达到同样的效果。
这里需要说明一下,Tableau的这四个函数所能接收的返回值形式。如果将上述代码中 return子句中的tolist()函数去掉,改成‘return arg1>nation_level’,Tableau会报如下错误(TypeError : Object of type ndarray is not JSON serializable):
从这个提示可以知道,python脚本中的return子句必须函数JSON序列化的数据格式,Tableau才可以正常接收。而ndarray不是可序列化的格式,所以会报错。看来从python返回tableau用的是序列化方式,目前支持的JSON序列化的python数据类型主要有以下几种:
Python类型 | JSON类型 |
---|---|
dict | object |
list,tuple | array |
str | string |
int,float | number |
True | true |
False | false |
None | null |
所以,向tabpy_server发送python脚本时,其return子句中的返回类型只能是:list,tuple,int,float,str以及bool型。对于上述需求,如果要用tuple,可以参考如下写法:
3.将python脚本部署在tabpy_server上
除了第2部分在创建计算字段的时候使用Python的脚本方式之外,还可以将Python脚本部署在tabpy_server上供Tableau使用。这里以Tableau使用Python机器学习为例进行说明。
(1)先在jupyter notebook中写好python代码并将函数部署到tabpy_server上。使用的Tableau版本为Tableau2018,以Iris数据集为例,具体代码如下:
from sklearn import datasets
from sklearn import svm
from sklearn.preprocessing import StandardScaler
from sklearn.grid_search import GridSearchCV
import numpy as np
import tabpy_client
iris=datasets.load_iris()
X=iris.data #原始训练数据中的特征向量
y=iris.target #原始训练数据中的类别数据
#对X进行归一化
scaler=StandardScaler().fit(X)
X=scaler.transform(X)
#使用GridSearchCV方法对SVC进行调参
svc=svm.SVC()
paras={'kernel':('linear','rbf'),'C':[1,10,100,1000]}
svcclf=GridSearchCV(svc,paras)
svcclf.fit(X,y)
#要部署到tabpy_server上的函数
def iris_classifier(sepal_length,sepal_width,petal_length,petal_width):
X=np.column_stack([sepal_length,sepal_width,petal_length,petal_width])
X=scaler.transform(X)
y=svcclf.predict(X)
return y.tolist()
client=tabpy_client.Client("http://localhost:9004")
#将iris_classifier函数部署到tabpy_server上
client.deploy(name='iris_classifier',obj=iris_classifier,override=True)
(2)在Tableau中创建计算字段(假设Iris数据已经导入到Tableau中),其具体写法如下:
(3)关于以上两个部分代码的写法有以下几点需要说明:
- iris_classifier函数的return语句的写法和我在网上找的资料中的写法不同。其他资料中的写法为:
return encoder.inverse_transform(y).tolist()
但我用这种写法时,函数可以正常部署到tabpy_server上,但是使用pre_label进行表计算的时候,会提示以下错误,原因暂不清楚。
The endpoint you're trying to query did not respond. Please make sure the endpoint exists and the correct set of arguments are provided.
- 计算字段pre_label的写法中,这里用的是SCRIPT_INT()函数,而不是SCRIPT_STR函数,因为机器学习中的svcclf.predict()方法返回的类型就是int型。只是,后续在Tableau中使用pre_label字段时,将它更改为了离散型变量。
- pre_label中四个参数的写法用了聚合函数,否则会报错。主要是结合后续使用,一定会按行对数据进行聚合,所以无论是MIN()、MAX(),抑或SUM(),AVG()都不会改变结果,所以就选用了MIN()。在使用pre_label的计算结果时,将四个维度都设为了“维度”。具体如下: