昨天晚上Tableau发布了2022年的3季度更新,其中有一个功能让我非常兴奋,那就是Table Extension。

按照官方的介绍,该功能可将Tableau数据源中的表,作为输入传入Table Extension中,使用Python、R等各种分析扩展程序进行重塑、扩展或其他高级分析后,生成的结果作为输出再返回到Tableau数据源。

按这个描述,这个功能就非常有用了,借助该功能,我们可以在Tabeau数据源页面中使用其他程序脚本来对数据源做很多Tableau本身无法完成的数据处理加工工作。

例如:我之前在做一个文本分析任务时,想要通过Tableau来做词云,我不得不使用Python对要分析的文本进行分词后,将分词结果存到数据库一个表中,再用Tableau连接分词结果表进行可视化,原因就是当时在Tableau中,无法完成将文本段落转换成能直接用于绘制词云图的 一个词一行的数据结构,而且用这种方法,词云图数据想要动态更新,我还不得不维护一个定时任务程序,去实现将文本分词并存入数据库表这个过程的自动化处理。

然而今天,得益于Table Extension功能的出现,对于以上场景,我们的处理过程将得到大幅简化。

作为Tableau的资深用户,看到新版本释出的通知后,我第一时间就进行了下载安装,想以最快的速度体验到新增的功能,以下是Table Extension功能页面截图:

tableau里使用python进行计算 tableau连接python_Python

经过我一番摸索,得知使用该功能的步骤如下:

  1. 安装Tableau提供的Python扩展程序:TabPy
  2. 建立数据源连接
  3. 将左侧数据源窗格中“表”栏底部的“新表扩展程序”拖到右侧区域
  4. 将要作为输入的数据源表拖动到右侧的“新扩展程序”窗格中,作为输入
  5. 在脚本编辑框中输入 要对输入表进行处理的程序脚本
  6. 点击“应用”按钮,即可在“输出表”TAB下看到经过脚本处理后的数据

下面我们就来一步步操作,通过一个具体案例来探索这个功能。

一、安装、配置TabPy分析扩展

TabPy是Tableau官方提供的基于Python的分析扩展服务,Tableau可以借助TabPy将Python的强大能力引入Tableau中,实现一些高级的分析。

1、TabPy安装

和安装其他Python包一样,直接pip安装:

pip install tabpy

tableau里使用python进行计算 tableau连接python_数据源_02

2、TabPy启动

直接在终端中运行如下命令:

tabpy

tableau里使用python进行计算 tableau连接python_python_03

看到最后一句“ Web service listening on port 9004”则表明TabPy服务已成功启动,服务端口默认为9004。

3、Tableau连接TabPy分析服务

Tableau Desktop中连接TabPy等分析扩展服务的入口为:

tableau里使用python进行计算 tableau连接python_Python_04

填入TabPy服务连接信息:

tableau里使用python进行计算 tableau连接python_数据源_05

如果大家的TabPy安装在Tableau Desktop当前机器上,主机名可以填写 : localhost,端口号就是刚刚TabPy服务启动时显示的端口号,默认情况下,TabPy是不启用鉴权的,不需要进行登录,所以填好主机名和端口后,可以直接点击“测试连接”,看是否连通,显示成功则可点击保存按钮关闭,此时回到数据源页面,就可以使用刚刚连接的TabPy扩展服务了。

二、探索Table Extension

我将从网易云音乐爬取的周杰伦歌词作为输入表拖到了“表分析扩展”窗格中,想要尝试在这里用脚本进行分词并将数据处理成一个词一行的结构进行输出

tableau里使用python进行计算 tableau连接python_python_06

现在问题来了,这个脚本应该怎么写,有什么语法规则或格式约定?

在Tableau Desktop界面中没有看到任何提示,于是我去找官方文档,看有没有介绍指引,找了很久只找到了这一篇,是关于Table Extensions的功能介绍页,但是写的非常简略,完全没有关于这里的脚本写法的语法介绍,只有如下截图一个简单得不能再简单的示例:

tableau里使用python进行计算 tableau连接python_Python_07

看到这里,我脑海中当时就出现了以下画面:

tableau里使用python进行计算 tableau连接python_数据源_08

就这???官方示例你就只给一句代码,写文档那位哥们你还能再懒一点吗???这样看完我还是不会啊!!!

return _arg1

但是,当我平静下来仔细思考,发现这一句代码其实已经提供了很多信息,我们可以化身柯南尝试挖掘一下:

tableau里使用python进行计算 tableau连接python_python_09

我把以上代码复制到我的脚本输入框中,看看能否正常输出,输出是什么:

tableau里使用python进行计算 tableau连接python_Python_10

上图可以看出,这句脚本是将输入表原样输出(字段顺序有颠倒,但不影响)。

同时,在TabPy的运行窗口中,可以看到当我点击“应用”按钮执行脚本时,窗口输出了请求记录,而且把脚本代码也打印了出来:

tableau里使用python进行计算 tableau连接python_数据源_11

从以上信息,我们可以获得如下推论:

  • 脚本的输出使用 return 关键字。
  • 上述脚本代码中,变量 _arg1 的值就是输入的表格,return _arg1就是将输入表直接输出。
  • 脚本 "return _arg1" 将输入直接输出,而从Tableau界面中看,输入和输出都是表,输入和输出大概率是同一种数据类型

基于以上推论,我们可以大胆进行假设:Table Extension 中接收输入表的参数名就是 _arg1

只要我把 _arg1 改为其他名称如 :_arg /_arg2 / param 等等其他名称,就可以进行验证:

tableau里使用python进行计算 tableau连接python_数据类型_12

果不其然,换了任何别的名称都不行,会报名称未定义的错误,至此我们可以确定,在脚本中,变量名 _arg1 专门用于指代 输入表。接下来就是要弄清楚 _arg1 的数据类型和数据结构,只要知道了 _arg1 的数据类型和结构,我们就知道了在脚本中应该如何操作 _arg1 这个变量。

我们可以尝试在脚本输入框中加一些print的代码,以在运行窗口中输出我们想看到的信息,来逐步解开 Table Extension 这个黑箱子的秘密。

尝试使用如下代码在脚本运行时输出 _arg1 的数据类型:

print('_arg1的数据类型:',type(_arg1)) 
return _arg1

运行窗口中可以看到如下输出:

tableau里使用python进行计算 tableau连接python_python_13

是不是很惊喜?原来就是我们熟悉的 dict 数据类型,接下来就是弄清楚这个输入表格被组织成了什么结构的dict就好了,为了更清晰地观察数据结构,我们用自定义SQL限定一下输入表的数据量,只保留3行数据,然后在脚本中输入如下代码:

for key,value in _arg1.items():
    print(key) 
    print(value)
return _arg1

输出如下:

tableau里使用python进行计算 tableau连接python_python_14

从上图,我们可以一目了然:_arg1 是以输入表的字段名作为 key ,每个字段的行值构成的 list 作为 value,而形成的字典。

也就是说,Table Extension 的输入输出都是这样的结构!

这下就好办了,我们在只需要脚本中对输入表的字典进行各种加工处理之后,再将数据组织成这种结构的字典,再进行输出就可以了。

根据官方文档那一行示例代码,我们把 Table Extension 这个黑箱子变透明了。

那一行代码,就够了!

三、使用Table Extension改变数据源结构实操

我们使用对周杰伦歌词进行分词的任务进行演示,有了上文的探索,我们已经知道了 Table Extension 的使用方法,接下来直接上脚本代码,代码每一步有详细的注释,有Python基础的同学可以参考:

import pandas as pd  # 使用pandas来进行数据处理
import jieba.posseg as psg  # 使用jieba进行文本分词


# 将输入表转为DataFrame后边进行后续处理
input_table = pd.DataFrame.from_dict(_arg1)


# 定义一个对歌词进行分词的函数
def lrc_cut(lrc):
    # 对歌词进行分词,只保留 名词、动词、形容词 这三种词性的词汇
    result = [x.word for x in psg.cut(lrc) if x.flag in ['n','v','a']]
    return result


input_table['LRC_CUT'] = input_table['LRC'].map(lambda x : lrc_cut(x))  # 调用分词函数对歌词字段进行分词
# 为了避免数据冗余,舍弃掉歌曲名称和歌词文本字段,取出歌曲ID和分词后的歌词字段到一个新 DataFrame
df = input_table[['SONG_ID', 'LRC_CUT']]  
output_table = df.explode('LRC_CUT')[['SONG_ID', 'LRC_CUT']]  # 对分词后的歌词列表进行行扩展,每个词作为一行数据

# 将 DataFrame 转为 dict 形式,给到 Tableau 作为输出表,注意orient参数要指定为list,才是Tableau需要的格式
output = output_table.to_dict(orient='list')  
return output

我们看下输出的表格数据:

tableau里使用python进行计算 tableau连接python_python_15

输出表只保留了两个字段,一个歌曲ID,另一个是每首歌中的词汇,一个词一行,我们成功改变了数据源结构!

令人惊喜的是,表分析扩展输出的表依然可以和其他现有数据源建立关系连接,来进一步构造后续分析使用的数据模型,这一点是非常有必要的,确保了这个功能的实用性,如下图所示:

tableau里使用python进行计算 tableau连接python_Python_16

四、结语

Tableau是我最喜欢的工具之一,也是日常工作最常用的工具,Tableau基本保持每个季度发一版更新,不断地带来新的改进,其中不乏一些能极大提高我们数据处理分析可视化效率的功能,例如本文所分享的表分析扩展,以上示例只是抛砖引玉,这个功能给我们带来的想象远不止于此,例如,我们还可以编写调用API的脚本,将输入表作为参数,从外部接口获取数据,并返回到Tableau中等等,欢迎读者们一起进行交流探讨。