在开始时,我们将简要介绍在教导内部产品A1以编程方式收集数据源并将其发布到Tableau Server之前和之后的过程。然后,我们将仔细研究BI命令的问题和找到的解决方案,并深入了解(这里是有关创建.hyper文件,在tableau服务器上发布文件以及更新hyper)的信息。欢迎下切!
DAN广告小组中的我们经常处理Mediascope(媒体市场上的工业仪表)的广告监控数据。有不同的方案:一些员工上载原始数据,其他人使用现成的预处理数据库,而另一个人则根据此数据命令开发自动化仪表板。让我们更详细地讨论最后一个场景-我们的BI开发人员在Tableau中收集仪表板,但是在开始“绘制”之前,他们还需要将数据转换为便于开发的所需格式。
从原材料到精美的自动图形的数据生命周期大致可分为4个步骤:
- 获取原始数据
- 数据清理和修订
- 为Tableau创建数据源
- 可视化的发展
它是
在学习如何以编程方式为Tableau生成数据源之前,该过程如下所示:
1.获取原始数据
用户通过内部工具A1生成表格报告。我们将在下面更详细地讨论它。
2.数据清理和修改
A1工具还包含数据转换功能,之后可以将清理后的数据上传到xslx / csv,并在工具之外继续使用它们。在此值得注意的是,某些用户将自己限制在第一点,并且在上载报告后自行修改数据。
3.为Tableau创建数据源
以前,仪表板客户附带了一组Excel,它们是在上几段中生成的。 BI开发人员将这些出厂单据单独带入单个数据源(小报语)中。并非总是只能将自己局限于Tableau工具;它们经常使用Python编写脚本。
4.可视化的开发
最后,冰山一角是创建一个仪表板并将其发布在Tableau Server上,供客户查看。实际上,渲染通常比收集数据和设置更新花费的时间更少。
随着定制解决方案数量的增加,在第三步中积累的痛苦越来越大,维护和实施成本很高。此外,第二步数据中的错误经常泄漏出去-两个系统(A1和Tableau)之间的中间Excel正在推动用户:“让我们用笔纠正一些东西,没人会注意到”。
已经成为
主要任务是消除2到3步之间的多余费用。结果,我们教会了A1收集数据源并将其发布到Tableau Server。这是发生了什么:
现在,第1步到第3步在A1中进行,BI团队将在输出中收到在Tableau Server上发布的用于开发可视化效果的数据源。Hyper API成为连接链接,我们将对此进行进一步讨论。
结果
当使用不同的工具时,减少了节点数。现在,在过程中的某个地方犯错误变得更加困难,并且更容易发现错误发生的地方,对故障的调查花费的时间更少。系统警告用户常见错误。
为BI团队腾出时间。以前,模板解决方案很少,定制很多。大多数情况下,为每个项目添加Python处理。在极少数情况下,不需要处理,我们直接在Tableau Desktop(主要开发工具)中工作。
现在,数据源的准备工作是:在A1界面中单击必填字段,标记我们扩展为行的那些字段(如有必要),并可以选择预先定义字段的类型。
我们不加载Tableau Server更新庞大的数据源-更新由A1完成,并将现成的Hyper下载到服务器。
*奖金-我们鼓励用户在A1内部工作。如果以前有一些用户在卸载原始报告后在工具外手动修改了它们,现在,由于步骤1到3的整个过程都在A1中进行,因此用户可以更轻松地在此处设置清理过程。
问题与解决方案
关于A1的一些知识
在开始谈论我们的解决方案之前,我们需要谈论我们的内部产品A1,并将其附加到数据源的生成中。
A1是公司的内部产品,旨在简化主要工作如下的员工的工作流程:
- 从MediaScope软件产品检索数据
- 将这些数据(干净)整理成便于主题分析人员使用的形式
- 如有必要,准备用于创建仪表板的数据(我们将在今天进行讨论)
用户完成清理数据后,它们将存储在A1系统中。在我们的术语中,这称为“容器”。容器是MongoDB中的常规文档,我们需要将其转移到Tableau服务器。
BI团队问题
我们的BI开发团队需要以某种方式从存储在MongoDB中的A1中获取数据,并根据接收到的数据构建仪表板。首先,我们尝试使用标准记分板工具从MongoDB中获取数据,但这不能解决问题:
- 由于数据存储在MongoDB中,因此在记分板的入口处会接收具有任意结构的数据,这意味着您将不得不不断维护此逻辑。
- 要从MongoDB聚合数据,必须从集合中拖动某些记录,而不是整个集合,Tableau驱动程序无法做到这一点。
- 除其他事项外,获取数据还不够:有时必须对其进行“扩展”,才能将某些列“取消透视”为行。从字面上看,这也不是那么容易做到的。
我们想出了什么
决定尝试使用Tableau Hyper API库解决我的自行车问题。该库使您可以创建.hyper格式的文件,可以轻松地向其中添加数据,然后将其用作数据源以在服务器板上创建仪表板。
正如计分板的开发人员自己描述超级脚本:
Hyper是一种高性能的内存数据引擎,可通过有效评估数据库查询来帮助客户快速分析大型或复杂数据集。Hyper基于Tableau平台,使用专有的动态代码生成技术和高级并发技术来实现提取和查询的高性能。我们程序中的大致工作过程如下:
- 用户选择容器和所需的列
- 系统将数据从容器中拉出
- 根据收到的数据,系统确定列的类型
- 超级的创建和向其中的数据插入被初始化
- 超级加载到记分板服务器上
- BI开发人员可以在服务器上看到超级文件,并基于它创建仪表板
当将新数据倒入容器时,系统将收到一个信号,通知超级需要更新:
- 系统将从记分板服务器下载超级
- 将从MongoDB中获取新数据并更新超级
- 之后,系统将新的超级链接上传到服务器,覆盖现有的超级链接
- 用户只需要单击“刷新”按钮即可在仪表板上显示最新信息。
用户看到的
如前所述,A1是一个Web应用程序。我们使用Vue.js和Vuetify创建了前端超生成服务。
应用程序界面分为三个屏幕。
在第一个屏幕上,用户选择所需的容器和列。
如果启用了“取消透视”(Unpivot)选项,则将在超级文件中创建另外两个列:变量-“度量”列选择的列的名称和值-这些列中的值。
“维度”列会将具有相同名称的选定列的列添加到超级。在所有容器中,选定列的维数及其名称的数目必须相同,以便不违反超级数据库中表的完整性,因此,存在一列“超级名称”,如果它们在容器中的名称不同,则可以指定选定列的名称。
这样就完成了设置hyperer的过程。用户只需要转到第二个屏幕,单击“创建超级”,然后在日志中查看事件的进度。
第三个屏幕包含其他设置:
- 如果我们不需要系统自动更新超级服务器,则可以打开忽略更新
- 您可以指定一封电子邮件以发送更新报告
- 您可以手动指定值列的数据类型(仅在非透视模式下使用):浮点数,字符串或由系统自动确定(我们将进一步讨论类型)
- 您还可以为容器中的选定列指定数据类型。
内幕是什么
A1用Python编写。为了处理数据,我们使用Pandas,然后将来自Pandas的数据序列化以进行腌制并将其存储在MongoDB GridFS中。
收到创建超级的命令时,系统执行以下操作:
- 从MongoDB卸载所有必要的容器,并将数据反序列化为pandas datafremes
- 准备数据:仅在数据框中保留所需的列,为其指定新名称,并在必要时通过pandas.melt扩展表
- 如果用户已设置列的数据类型,则将数据转换为float32或字符串
- 在对数据进行所有准备工作之后,系统会通过超级API创建文件,并通过tabcmd将文件发送到记分板服务器。
值得讨论一下列的数据类型。在A1容器中存储数据的特征之一是用户不必理会要分配给列的类型,大熊猫为它们完美地做到了:系统可以轻松应对列中存在数字和字符串值的情况。但是,hyper并不喜欢这样:如果您告诉他该列必须为int类型,则在尝试插入除整数以外的任何内容时,系统都会发誓。因此,决定在hypers中仅使用两种数据类型:string和float。
因此,我们了解了工作的一般原理,让我们谈谈使用hyper本身的问题。
创建一个.hyper文件
要使用Hyper API,您需要安装该库,您可以从此处的官方网站下载它。还有一些很好的例子说明如何使用此工具。我们将简要概述要点。
超级文件本身是一种数据库,有点让人想起SQLite。通过api,您可以使用类似SQL语法的方式访问数据:
f"SELECT {escape_name('Customer ID')} FROM {escape_name('Customer')}"
由于我们的系统是用Python编写的,因此我们还将使用相应语言的库。创建文件时,必须指定架构名称,表名称和类型的列。模式和表的名称应称为“提取”,因为Tableau Server会在该模式中与表一起爬升以提取书籍数据。
with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
with Connection(
hyper.endpoint, self.fullpath_hyper, CreateMode.CREATE_AND_REPLACE
) as connection:
connection.catalog.create_schema("Extract")
main_table = TableName("Extract", "Extract")
example_table = TableDefinition(main_table)
创建表后,我们需要创建列和设置类型。如前所述,我们的数据只有两种类型(浮点型或字符串型),因此根据数据框中列的类型,我们为列设置此类型:
for column in dataframe.columns:
if dataframe[column].dtype.name in ("category", "object"):
example_table.add_column(TableDefinition.Column(column, SqlType.text()))
elif dataframe[column].dtype.name in ("float32"):
example_table.add_column(
TableDefinition.Column(column, SqlType.double())
)
connection.catalog.create_table(example_table)
创建表后,可以插入数据:
with Inserter(connection, example_table) as inserter:
for val in dataframe.values:
inserter.add_row(val.tolist())
inserter.execute()
这里我们逐行遍历数据帧,并通过inserter.add_row()将值累加到列表中。实际上,api hyper中有一个函数add_rows(),该函数获取列表列表并已插入值。为什么不这样做呢?为了节省RAM:为了从数据框中提供值列表的列表,您需要让熊猫来做values.tolist()。而且当您拥有1.5亿行数据时,对于RAM来说这是非常昂贵的操作,尽管这丝毫不影响性能(无论如何,由于行上的迭代迭代,创建Hyper的速度有所下降的原因并未引起注意)。另外,add_rows()就像语法糖一样工作:它实际上需要一个列表列表并迭代地添加数据。
这样就结束了我们的hyper的创建。接下来,我们需要将其发布在服务器上。
将文件发布到Tableau Server
要访问tableau服务器,我们将使用tabcmd实用程序-这是一个控制台实用程序,它使您可以连接到服务器并执行管理功能-创建用户,组,书籍等。
我们将通过Python子进程运行tabcmd命令。
popen = subprocess.Popen(
f'/opt/tableau/tabcmd/bin/tabcmd publish "{fullpath_hyper}" -n "{filename}" -o -r "A1_test" '
'-s http://tableau.domain.com -u "username" -p "password" --no-certcheck',
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
return_code = popen.wait()
if return_code:
error = str(popen.communicate()[1])
return f" . {error}"
我们将以下命令和键传递给tabcmd:
- 发布:将文件上传到服务器
- -n(-- name ):服务器上的文件名
- -o(--overwrite):如果存在具有该名称的文件,则覆盖
- -r “A1_test” (--project): ( )
- -s (--server): tableau-
- -u -p:
- --no-certcheck: SSL-
我们想出了如何创建一个新的Hyper,但是当Hyper由十个容器组成并且其中一个容器接收到新数据时该怎么办?我们将更新超级。
当新数据到达容器时,系统将查看是否有使用该容器的超级用户。如果存在,则任务是更新超级。
为了了解超级容器中哪个容器中的数据,系统在创建超级容器时还会创建一个附加的container_id列。使用这种方法,更新变得非常简单:
- 我们从服务器获取文件
- 我们删除hyper中的所有行,其中container_id等于更新后的容器
- 插入新行
- 将覆盖的文件上传回服务器。
检索文件的过程与下载文件的过程略有不同。首先,我们不会从服务器中获取.hyper文件,而是从.tdsx存档中获取文件,然后将其解压缩并打开.hyper本身。
为了获取文件,我们使用tabcmd:
popen = subprocess.Popen(
f'/opt/tableau/tabcmd/bin/tabcmd get "datasources/{filename_tdsx}" '
f'-s http://tableau.domain.com -u "username" -p "password" '
f'--no-certcheck -f "{fullpath_tdsx}"',
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
)
return_code = popen.wait()
if return_code:
error = str(popen.communicate()[1])
return f". {error}"
在这里,我们使用以下命令和键:
- get:从服务器获取文件。如果test.hyper文件在服务器上,那么您需要引用test.tdsx文件,并且它们都在数据源目录中(如果您知道记分板的这种功能,我无法在Google上进行谷歌搜索)
- -f(--filename):完整路径,包括文件名和扩展名,文件保存位置
下载文件后,必须通过zipfile解压缩文件:
with zipfile.ZipFile(fullpath_tdsx, "r") as zip_ref:
zip_ref.extractall(path)
解压缩后,超级目录将位于./Data/Extracts目录中。
现在我们有了文件的当前版本,我们可以从文件中删除不必要的行:
table_name = TableName("Extract", "Extract")
with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
with Connection(hyper.endpoint, self.fullpath_hyper) as connection:
connection.execute_query(
f"DELETE FROM {table_name} WHERE "
f'{escape_name("container_id")}={container_id}'
).close()
好了,上面已经描述了插入和发布文件。
结论
底线是什么?完成了实现超文件的生成并将其自动交付到Tableau Server的工作之后,我们大大降低了BI团队的负担,使仪表板中的数据更新变得更容易,并且更重要的是,它变得更快。熟悉hyper api并不痛苦,文档编写得很好,而且将技术非常轻松地集成到我们的系统中也很容易。
感谢您的关注!如果您有任何问题或意见,请保留在评论中。
该文章与Vasily Lavrov(VasilyFromOpenSpace) — -