本次演示概述了 JMP 18 对 Python 支持的全新重大更新,这些更新实现了与 Python 更紧密的集成,并显著提升了以 Python 为中心的用户体验。在 JMP 18 中,您将能够:
这些面向 Python 开放的 JMP 功能将随着时间的推移而不断扩展,但目前的重点是关键功能和内存数据支持。本次演讲将详细介绍这些变化及其背后的原因,并展示 JMP 用户可体验到的新功能。演讲还将探讨如何通过使用外部 Python 包来实现扩展性和数据导入功能,并演示如何使用外部包通过 Python 接口访问 GPU。
大家好,我是保罗·尼尔森。今天我想和大家聊聊JMP、Python以及我们的下一步计划。去年的一次演讲之后,我们觉得可以更上一层楼。JMP和Python还有改进的空间,而这正是我今天要讲的内容。
客户遇到的最大挑战是 JMP 14 到 17 版本中 Python 支持的问题。如果能正常运行,那当然很好。但实际情况往往并非如此。由于 Python 发行版众多,环境各异,这给我们的测试工作带来了很大的困难。
Anaconda 有时可以在 Windows 上运行,Python 虚拟环境有时也可以运行。但由于每个人使用的 Python 版本都不同,脚本甚至无法依赖于用户环境中通用的 Python 版本。
在 JSON 编辑器中编辑 Python 代码既困难又不方便。Python 代码块之间用空格分隔,而当你需要将代码块作为字符串粘贴到 JSL 中时,这无疑增加了操作难度。此外,旧版本 JMP 中 Python 的错误报告机制也存在缺陷,使得调试 Python 脚本变得异常困难。
最后,数据表和矩阵的传输是通过文件系统完成的,其中数据表以 CSV 文件格式传输,如果数据量很大,这种方式速度会很慢。处理小类数据没问题,但处理更大的数据量时速度就会变慢。
我们的目标是确保 Python 集成开箱即用,无需任何配置。为此,我们内置了 Python 3.115 和 JMP 18.0,并将其作为 JMP 的一部分进行安装。这是一个通用环境,所有用户都将拥有相同版本的 Python 和 JMP。
我们更进一步,确保 JSL 和 Python 之间的数据流完全在内存中进行,并且对内存的访问完全零拷贝。我们为 Python 程序员打造了一个友好高效的工作环境。现在,我们还提供了一个 Python 脚本编辑器,支持代码着色和语法高亮显示。
您可以直接从脚本编辑器运行纯 Python 脚本,也可以在 Python 环境中直接访问 JMP 数据和日志,还可以调用 JSL 并从 Python 访问数据。我们大幅增强了 JMP 日志和嵌入式日志的错误报告功能。
由于现在这是一个通用环境,我们可以对其进行测试和验证。正如您所看到的,我们的环境与您相同。为了与之匹配,我们在脚本索引中添加了一个完整的 Python 类别……以便解释并展示 JMP 导入包和其他一些实用包的可用功能示例。
另一方面,这使我们能够利用 Python 庞大的包库来扩展 JMP 的功能。我们并非试图打造一个完全替代 JSL 的方案,而是力求在 JMP 中为 Python 编程提供一个熟悉且高效的环境。
这是一次现代化改造,一次彻底的革新。JMP Python 集成的一些原始设计决策存在局限性。我们已尽一切努力将变更带来的影响降至最低,同时为未来奠定坚实稳定的基础。
预计此次故障仅限于向 JMP 过渡期间。关键在于,我们现在仅支持随 JMP 一起发布的 Python 版本。您可以将其视为一个私有的 JMP 虚拟环境。
脚本中另一项改动是,我们改变了 Python 发送数据表的方式。现在它会创建一个 JMP 数据表对象。这样就可以实时访问 JMP 数据表。内存中的副本可以让你编辑和修改现有表,它不再是副本。
JMP 14 到 17 版本通过将数据复制到文件系统中的临时 CSV 文件来创建 pandas DataFrame 对象。但对于大型数据集,导出为 CSV 文件会变得非常庞大且速度很慢。此外,由于需要在二进制格式和文本格式之间进行转换,因此还可能存在精度损失的问题。
如果您确实需要相同的功能,我们在示例文件中提供了一个指向 pandas Python 的 DT(数据树),其中包含两个 pandas 函数。它的工作方式与之前内部实现的方式相同:创建一个临时 CSV 文件,并指示 pandas 导入该 CSV 文件。
此外,还有一个 JMP 到 pandas 的示例,展示了如何从 JMP 数据表对象在内存中创建 pandas DataFrame,该对象一次使用 JMP 列中的一列来构建 pandas 列。
Python 的 SendMatrix 函数以前也是通过文件系统写入数据的。它会写入一个包含双精度浮点数的文件,然后让 NumPy 读取该文件。现在,这个过程在内部内存中进行,并且是直接引用矩阵,而不是复制。
JSL Python API 的大部分功能保持不变。此次更改主要涉及移除已弃用的参数和函数,这些参数和函数用于配置原始 Python 支持以选择 Python 版本。
唯一移除的 API 是 Python 的 get graphics 函数,它用于从 Matplotlib 获取图像。在我们的脚本索引中,我们提供了一个示例,展示了如何仅使用 JSL 和 Python 代码实现 JMP 内部的相同功能。赶紧演示一下吧。我们来看看。退出演示。
这里是 JMP。我想展示的一点是,您会看到工具栏中的 Python 图标,在“文件”菜单的“新建”选项中,我们原本是“JSON 脚本”,但现在是“新建 Python 脚本”。我们打开 Python 脚本编辑器。
我们可以使用“显示嵌入式日志”功能。我们有一个 JMP 导入包。导入 JMP。我们已尽力确保添加了 Python 文档字符串,以便您可以运行“help JMP Python”命令。我们可以运行该脚本。您会看到,我们已经记录了此顶级 JMP 导入包中的对象和函数。
这里有很多常量。大部分特殊目录都已列出。所以我们可以这样做。现在我用 Python 执行 JMP 打开。JMP,嗯,我今天打字不太方便,JMP。示例数据。此外。Big Boss。就像在 JSL 中一样,我们可以选择代码行并在脚本中运行它。我们有一个大类。
现在,这不是副本。我们可以获取该对象。我们可以执行 `TT = JMP open` 来获得引用。但我们也可以执行 `TT = JMP.current`。这将给我们一个指向当前数据表的引用。
我们可以打印 TT,这表示这是一个很大的班级,有 5 列和 40 列。但更有趣的是,你还可以打印 TT,表示“姓名”。我们可以使用 Python 的切片操作符。我这里要打印的是“姓名”列中从零开始的每隔一个名字。
在 Python 中,我们使用索引,索引从零开始。我们的数据表索引是按列和行排列的。由于我们采用的是列优先的格式,如果我们打印每隔一个名字,我们会得到 Katie、Jane、Lilly、James、Barbara、Susan、Joe 和 David。
这是实时访问,所以我们可以做一些事情,比如……我们可以做一些事情,比如 TT,我们可以使用名称或列索引。我们打印列。这样我们就得到了,选择。这是你的……我选了一个,对吧,Lewis。抱歉。零零。是 Katie。因为数据表是实时的。你可以对 Katie 做
稍等。小写。大小写混合。现在我们在嵌入式日志中看到输出。你会注意到,我们已经修改了数据表。这使你能够修改数据表,以 Python 的方式访问数据表,并且能够从 Python 创建数据表。我们已经尽力了。
现在我们有了脚本索引。我会在这里展示……就像我之前说的,get graphics 函数已经弃用了。它实际上不再是 JMP 中的一个有效函数,但脚本索引中的示例会向您展示如何从以前的方法过渡到 JMP 内部实际实现的方法,即保存图形、打开图像并创建一个包含该图像的新窗口。
Python 环境中一个很棒的新特性是,Python 本身就是一个独立的类别。JMP 导入包包含数据表对象。每个对象都有示例,我们可以创建新列、添加行并保存表格。它还包含各种属性,例如获取表名等等。
这条命令会创建一个由 Python 驱动的新数据表。我们还有一个 JMP 工具集。你可以在这里运行它,或者我们可以用它来运行 pip JMP,因为它创建了自己的隔离环境。当我们安装软件包时,它们需要安装在 JMP 的隔离环境中。
希望您在使用 JMP 时不会遇到软件包与其他程序或 Python 安装冲突的问题。我们已经确保它们彼此隔离。所以,如果您确实需要……我们提供了 `create jpip` 命令,它会创建一个脚本来包装 Python 的 pip 脚本,方便您进行安装;或者您也可以直接从 gem 包中的 JMP 工具类运行它。
这里我们将展示如何使用 pip 命令列出已安装的软件包。调用 pip 并返回 Python 环境中已安装的软件包列表。另一个例子是,您可以在这里实际安装软件包。这段代码可以直接在 JMP 中运行。
这里有一些注意事项,这也是为什么需要创建 jpip 包的原因。如果像 NumPy 这样的包加载到内存中,因为它使用了共享库,那么 Windows 系统可能会阻止你更新该包。有些包需要编译,这些编译可能需要通过命令行完成。
有些方法可能有效,但可能需要通过命令行运行。因此,你需要创建一个 jpip 包装脚本。运行该脚本后,会弹出一个窗口,让你选择一个目录来写入文件,然后选择该文件夹,脚本就会生成一个可以从命令行运行的 jpip 包装脚本。
运行该脚本时,安装完成后需要使用 `---user` 标志。从这里调用时,我们会自动执行此操作。此外,还有一个 JSL 函数可以运行安装程序。Python 安装包就更简单了。您只需运行 JSL 脚本,它就会在后台调用 pip,并正确设置所有必要的环境变量,将包放入 JMP 的隔离环境中。
开始吧。接下来我们可以继续演示一些其他功能。这些都是简单的基础内容。但我们此举的另一个目的是让 JMP 对 Python 更加友好,并更容易通过额外的 Python 包来扩展 JMP 的功能。
用户经常提出的一个支持需求是导入 Parquet 文件。这个演示会从我的硬盘中提取一个表格。这个表格是我从网上下载的。没错。然后它会打印出 Parquet 文件的结构,并根据 Parquet 文件中的数据逐列构建一个 JMP 数据表。我们会清除日志。请看我实际操作。
我没运行脚本。我肯定是高亮显示了某些内容。好了。这是一个 Parquet 文件。我们用 Pyrr 制作了 Parquet 文件,因为 PQ 导入了它。这是 Parquet 文件的属性。它有一千行。形状是 13 列乘一千行。架构显示了列名的架构。
在最底部,我们可以展示另一件事,那就是我们离完成 Python 方面想要改进的事情还差得很远,但在我们实现目标之前,你可以从 Python 运行 JSL。
尽管 Python 代码目前还不支持设置显示宽度甚至更改列类型等功能,但您仍然可以创建新列,只是无法通过 Python 更改其类型。这些都是我们未来想要实现的功能,但目前您可以通过一种变通方法,即像之前在 JSL 中运行 Python 脚本一样,在 Python 中运行 JSL 脚本。
现在有了这个增强的环境,你可以在两者之间进行双向调用。Python 可以运行 JSL 脚本。Python 中的调用随后可以在 JSL 环境中运行,因此你可以嵌套调用来实现一些原本无法实现的功能。例如,将 JMP 本身不支持的数据类型(例如 Parquet 文件)导入到 JMP 中。但我们通过导入一个 Python 包来实现这一点。
还有一件事我很喜欢。我不太懂统计学,但我精通图形学和其他数学。这里我们用的是开放计算语言(OpenCL)。我们已经导入了 OpenCL 包。我们要创建一个曼德勃罗集分形。先来看看 CPU 的运行情况。用 CPU 运行大约需要 16 秒。
这很麻烦,你会注意到 JMP 本身运行缓慢。由于此时 Python 控制着程序,所以它响应迟钝。如果你在紧密循环内部深处放置一个运行中的单元格等待语句,就能重新获得控制权。这里使用的是 CPU 来生成这个分形。
现在我们改成用GPU运行脚本。搞定!生成图像、保存图像、在脚本中打开图像,看看结果如何,只用了不到一秒。用GPU花了16秒……但是,如果我们追溯到更早的时候,就会发现用CPU运行这个脚本实际上花了32秒。
如果你想在 JMP 中使用 GPU,现在可以通过 Python 实现。我们已经测试过 PyOpenCL 和 PyCuda,所以如果你有 Nvidia 显卡并且正在使用 CUDA,你可以选择 PyCuda 或更通用的 OpenCL。这将进一步增强 JMP 的计算能力,避免浪费你桌面系统的性能。
我们还可以通过 Python 连接到 SaaS。SaaS 有一个 SaaS y 包,我们已经把它安装到……这部分工作是我一位同事做的。我们清除日志。这样我们就能看到我得到的结果,然后可以运行脚本了。
它正在按预期收集这些信息。好了。这个脚本实际上使用了 JSL 来确保运行脚本可能需要的所有 Python 包都已安装,您可以自行设置。您在日志中看到的部分内容就是这些包已经安装完毕。以下是运行连接到 SaaS 的脚本返回的所有结果。使用的是 SaaS Py 包,我们稍后会查看日志。
脚本会回显到日志中。就像我刚才说的,我们有连接到 SaaS 平台、运行产品以及我们物流方面的信息。然后返回输出值,并结束 SaaS 会话。
我们听说大家可能对 TensorFlow 感兴趣。这里有一个非常简单的例子。不对,我又错了,我已经安装了 TensorFlow,这段代码是用 JSL 运行的,但我们完全可以直接用 Python 运行这段代码。
它运行 TensorFlow,生成这个总和。我相信你会想到比我演示的更复杂的用法。Py Digest 是另一个例子。它就像一个屏幕。这里有一个我创建的示例,展示了它的几个功能,包括运行 JSL,它允许我使用 JMP 的 pick directory 函数选择一个目录,然后使用 JSL pick directory 函数在 Python 脚本中使用它。这完全是纯 Python 脚本。它允许我选择一个目录。
它会计算目录中每个文件的校验和,并将校验和存入 SQLite 数据库,然后根据数据库创建 JMP 数据表。这能让你接触到一些数据库编程知识,以及一些 JMP 编程技巧,例如如何使用 Python 创建 JMP 数据表和运行 JSL 代码。
选择目录。这里有很多数据,所以我们先选择要展示的 Parquet 文件夹或 Select 文件夹。现在我们有了一个校验和表。他们创建了……它会检查目录中的每个文件,并提供它们的路径名和文件内容的 MD5 校验和。构建了数据库。使用 Python 构建了 JMP 数据表,并使用 JSL 设置了列宽。
再次强调,我们尚未实现此功能,但已将其列入考虑范围。不过,如果您愿意,您可以通过 Python 代码执行 JSL 操作。
有些人可能想用 R 语言。好吧,在这种情况下,我已经在我的机器和我们的环境中,使用我们的服务器建立了连接。在 Windows 系统上运行我们的服务器来运行 Linux WSL 环境要容易得多。
我这里运行着一个 R 服务器守护进程,并使用预留的客户端。我正在做一个简单的操作:在服务器上创建一个 R 向量,计算结果并求和,然后运行这个脚本。我们已经连接到服务器,获取了向量,并将所有元素相加得到 6。我们的 py2 版本也有一些示例。如果您想要一个非常简单的、可以从命令行运行的 R 示例,这里有一个如何使用进程从命令行调用并运行 R 的示例。
最后是统计部分,Jon Saul 帮了他不少忙。他创建了一个使用 HDB scan Python 包的示例。关于 Matplotlib 需要注意一点:Windows 上的 Matplotlib 没有图形后端,所以如果想要查看输出,我们发现 PyQt5 的效果最好。
作为后端,在 Mac 上,如果您使用 Matplotlib,我们会强制它使用 AGG(非图形后端),如果您尚未安装 PyQt5 或 PySide。这是因为 Mac 上的 Matplotlib 会自动选择 Cocoa 后端,而 Cocoa 后端存在一个 bug:当您在 Mac 上关闭 Matplotlib 的最后一个窗口时,会导致 JMP 退出。
为了防止出现强制使用的情况,如果我们在您的系统上找到 Matplotlib,并且它有后端,我们会加载 Matplotlib 并强制它使用 Qt 后端。否则,我们会确保它是一个非图形后端。我们已经将其设置为后端。我的机器上安装了 PyQt5。在这个示例中,我们对 HDB 扫描进行了一些聚类分析。
现在我们来展示一下嵌入的日志。我们将运行脚本。结果会得到一个用 Matplotlib 生成的数据表格。这里可以看到我们使用 JMP 打开了文件,并获取了 DT 参考。这些就是我拥有的样本。相信我。
我将重新介绍一下演示文稿。我们近期工作的重点是:持续改进和完善;提升 JMP 和 Python 之间的数据互操作性。目前,列修改编辑的限制在于,虽然可以创建任何类型的列,但只能修改和获取数值列、字符列、表达式列或状态列的值。创建链接表的功能将在未来实现。
虽然 Python 脚本编辑器支持代码着色和语法高亮,但目前还不支持自动缩进。我们也会研究这个问题。
现代化的目标是让 JMP 与 Python 的集成开箱即用。用户无需任何配置即可高效地使用 JMP 和 Python。如果集成失败,将会非常棘手。为什么不缩短 Python 程序员在 JMP 中投入工作的时间,并提升 JMP 的功能呢?使其能够轻松扩展,利用外部 Python 包,从而成为一个强大的分析中心。
提升我们加载不同数据格式的能力,例如我们已经测试过的 Parquet 格式和 HDF5 格式。现有的数据格式种类繁多,任何一个开发团队都无法全部编写,但通过这种方式,您可以引入 Python 包,并将其转换为 JMP 数据表,从而在 JMP 中实现强大的数据处理能力。
感谢您抽出时间。希望您喜欢我们的作品,非常期待您的反馈。谢谢。