Polars 2.0 预发布:流式引擎成为默认
文章摘要
Polars 作者 Ritchie Vink 在 2026 年 9 月 2 日发布了 Polars 2.0 的第一个候选版本,正式版将在数周内落地。开篇就定了调子:这不是一次大特性发布,作者「希望它对你来说是一次无聊的体验」。之所以升大版本号,是为了摆脱过去那些如今已成为阻碍的设计决定,并把默认值改成对更广泛用户更合理的设置。
最大的变化:流式引擎成为默认。 现在对 LazyFrame 调用 collect() 会默认走流式引擎,官方预期流式引擎「轻松快 5 倍」,普通用户在内存占用和性能上都会看到巨大改善。这件事之所以需要升大版本,是因为流式引擎在某些操作上(join、group_by、unpivot 等)默认不保证行序。如果你需要可观察的行序,可以显式设置 maintain_order=True;如果你想继续用「内存」引擎,可以用 pl.Config.set_engine_affinity("in-memory") 全进程设置,或者在单次查询里传 collect(engine="in-memory")。
更严格的 Polars。 Polars 的目标是严格并快速失败——错误理想情况下应当在一开始就抛出,而不是在管道跑了 20 分钟之后。数据不匹配时的隐式行为应当是选择加入而非默认,因为这类不匹配会掩盖 bug。作者特意提到,这种严格性在 AI 驱动开发兴起后变得更有价值:智能体可以通过调用 collect_schema() 提前验证查询结构,解析类型并在不实体化任何数据的情况下捕获 schema 层面的不匹配,从而获得快速反馈、更快地迭代。
文章给了几个变严格的具体例子:
-
is_in的无损类型强制转换。 之前如果两边数据类型不同,Polars 会把它们都转成共同的超类型,即使这个转换是有损的。文章举的例子极具说服力:一个从 JSON 导出加载的「已标记账户 ID」列表里,大 ID 变成了浮点数9007199254740992.0;而待检查的user_id是 Int64 的9007199254740993——差 1 的另一个 ID。2.0 之前user_id会被强制转成 Float64,但 9007199254740993 超过了 2^53(Float64 能精确表示的最大整数),于是静默向下取整为 9007199254740992.0,产生一个假阳性。2.0 会直接抛错,要求用户显式处理有损类型转换。 -
严格的拼接。 水平方向的
concat现在会检查长度,而不是静默填 null。例子是把按日交易数与按日欺诈标记数拼在一起,而第 5 天的上游任务静默失败了、只有 4 行——旧行为会让第 5 天的 flagged 悄悄变成 null。2.0 抛出ShapeError;如果你要的就是填充,得显式用how="horizontal_extend",让意图对读者变得清晰。 -
移除歧义的 cast,改用专用方法/构造器,目标是让解析数据只有一种显而易见的方式。枚举/分类类型与整数之间的 cast 被移除,改用
.cat.to(dtype)(int → 分类)和.cat.physical()(分类 → int);字符串到时间类型的 cast 也被移除,改用.str.to_date()/.str.to_datetime()——好处是这些方法允许你指定解析格式,对数据如何被解析有更多控制。
更有信息量的报错。 团队为「用了不再支持的旧参数」的场景下了不少功夫,新增了两个类型化异常:AttributeRemovedError(处理被移除的属性和方法)和 ArgumentRemovedError(处理被移除的参数)。错误信息会直接指向新的 API,比如调用 melt 会告诉你改用 LazyFrame.unpivot,且 id_vars 改叫 index、value_vars 改叫 on。作者说大部分被移除的功能已经废弃很久了,如果你一直保持更新,理应不受影响。
最后的展望。 作者重申 2.0 关乎更好的默认值和更好的 API,「我们希望这次发布相当无聊;我们不会把新特性押到大版本号后面,特性一准备好就发」。但他强调不要误会——Polars 2.x 会远好于 1.x,并列出了一批还在路上、尚未充分公开谈论的东西:流式引擎正经的核外(out-of-core)支持、全新的 IO 插件设计、他们认为会是「业界最快的 S3 读取器」、SQL 覆盖度的大幅改进、基于代价的规划器、join 重排序,以及移除 mmap——后者将让整条管道端到端完全异步。
HN 评论精华
399 分、119 条评论。讨论集中在三处:对「无聊的大版本发布」这种克制的赞赏、默认不保证行序引发的正确性焦虑,以及一场关于 Polars 语法 vs SQL 的长跑题。难得的是,Polars 的核心开发者 orlp 亲自下场做了最有价值的技术澄清。
一、「无聊」是一种美德
benrutter 引了那段「我们不打算做大特性发布,事实上我们希望它对你来说很无聊」,然后说:「我知道这种看法暴露了我是个非常乏味的人,但我就喜欢看到项目这样认真对待 semver!版本号跃迁真的应该关乎移除废弃的陈渣,而不是闪亮的新特性。」他说自己用 Polars 有一阵了,团队对稳定性的重视正是当初说服他跳槽过来的一大原因。Bluestein 给这种做法起了个好名字:「平静开发(对比「炒作驱动的发布」)。」
但这条也引出了一小段争议。dist-epoch 指出一个反面事实:「话虽如此,Polars 是我用的几百个 Python 库里少数几个我需要读每一个小版本发布说明的(比如 1.44 → 1.45),因为他们倾向于频繁地废弃、移除或改变特性。」duskdozer 顺势调侃:「听起来他们的版本号应该远高于 2.x 才对。」sanderjd 做了个技术澄清:在 semver 下,不破坏的废弃在小版本里是可以的。
这引出了 tialaramex 一段关于「严格性曲线」的精彩发挥。他指出 Rust 有明确的规则,但你仍可能写出不违反这些规则、却破坏了某些做了非常特殊事情的人的东西。「某个点上你会撞到 Hyrum 定律 / 空格键加热那个边缘——当然,没有任何合理的技术定义能得出你的改动『破坏』了别人,但某人的方案以前能用现在不能用了,他们为此愤怒。」他举了个比空格键加热稍微不那么荒谬的例子:也许有人依赖于能解析你库的某个文件,你重新设计了库,没有 API 变更,但他们在解析你的源码,现在他们的做法失效了。「一般来说,用户更少更轻的库倾向于更宽松……Hyrum 意味着当你变得更大更重要时,总有一天空格键加热问题会发生,那就太糟了。一门语言(比如 Rust)能在某种程度上帮你推迟那一天、并在它发生时减小爆炸半径,但它是不可避免的——Hyrum 定律不是你能彻底防止发生的东西,它是工程师设计系统时要考虑的一个因素,就像误用抵抗性一样。」
esafak 提出了一个更结构性的批评:「一个库是一堆特性的集合,其中任何一个都可能有破坏性变更。这就是为什么 semver 是不充分的。如果有一种标准化的方式来标示各组件层面的破坏性变更就好了,比如 changesets。」
另有几个人(altern8、suddenlybananas、cassianoleal、winniewinnie)绕着「大版本不就该表示破坏性变更吗」讨论了一圈,结论是 Polars 确实遵循 semver、2.0 确实有破坏性变更,magicmicah85 给出了最准确的解读:「这次不会有新特性,但它让我们能够开发新特性。」nicce 抛了个有趣的问题:「能有没有新特性的废弃陈渣吗?」kvdveer 的回答很到位:理想情况下不能——所有闪亮的新特性都不该等到 (N+1).0 版本,它们应该已经在 (N).(M) 版本里了;实践中移除陈渣会为某些新特性扫清障碍,但那应当很罕见。illiac786 补了一个:有时候东西变得不必要是因为外部因素而非新特性。
二、行序不再保证:科学计算的正确性焦虑
这是讨论中最实质、最有分歧的一支,由 trombonechamp 提出:「除了性能,maintain_order=False 成为默认还有别的理由吗?我问这个是因为 Polars 被用在许多科学数据分析管道里,而非确定性行为是科学计算中有充分记录的 bug 来源(他附了一篇 PMC 论文)。新的默认值要求用户在判断代码是否正确时,脑子里还得装着 API 的实现细节。这在科学计算里很棘手,因为正确答案事先并不知道,所以 bug 可以溜过去并静默给出错误结果。」
反驳分几路。hopfenspergerj 给的是最简洁的一条:「这是标准的 SQL 行为,用户总是把想要的排序作为查询的一部分明确指定。」Xunjin 补了个教学经验:「刚接触数据库和 SQL 的人常常认为排序是隐式的,我教过几十个初级工程师,他们都相信这是默认的。我一直好奇为什么会有这种思维过程。」fwip 给出了本场最精辟的一句解释:「因为他们跑的头 100 个查询里,99 个是按顺序回来的,而他们没注意到第 100 个不是。」
nemothekid 从设计原则出发:「如果我程序的正确性依赖于数据的顺序(对于本来可交换的操作而言),那看起来这应该是显式的而非隐式的。我不确定我同意『一个隐藏的设置实际上在保证你数据正确』应该成为默认。」Permik 的分析最深入:真正的问题不是处理算法的非确定性,而是数据的隐式排序——他所说的隐式排序指的是「一条未充分指定的管道,对某些输入已被排序有着未声明的依赖」,而这正是被引论文里的代码在某些系统上能用、在另一些上不能用的原因(os.listdir() 调用不保证有序结果,于是被假定的不变量被打破了)。「要求代码默认对自己的不变量保持显式,就是纯粹的好设计,也让东西更健壮。而当你对不变量更精确时,你就能收获算法上的好处。」Lvl999Noob 回应「改默认值本身是隐式的,怎么能让建模更显式」这个质疑时给出了要害:它变得更显式,是因为在顺序重要的地方你现在会看到一个 maintain_order=True,而以前你根本无法判断它是否真的需要固定顺序。
sanderjd 提了个精确的术语质疑:「『非确定性』是这件事的正确描述吗?我读到的是一个不保留顺序、但确定性地不保留的实现。是我读错了吗?」dhampi 给出了权威答案:Polars 内部会对某些操作的行做哈希,方式会影响最终顺序,而他们不保证哈希算法或种子在版本和平台之间的稳定性——所以确实是非确定性的(他还补了一句「我没意见,我同意除非你显式要求,顺序就不该重要」)。jorelfermin 从实现层面补充了机制:group_by、join、unique 的顺序都跑在跨线程的并行哈希聚合上,所以输出顺序取决于数据如何被分区到各个核上;这就是为什么即便数据集和 Polars 版本完全相同,你也可能在不同机器上得到不同的行顺序(他本人遇到过);修复办法是设 maintain_order=True,或者更好的做法是在保存或比较/diff 输出的地方加一个显式的 sort。
站在 trombonechamp 一边的也有实战苦水。0cf8612b2e1e 说这个行为反复让他抓狂:「我在写某个新的变换,想看看结果,而我最开始那几行哨兵数据不见了,因为它们被打乱了。我不把 dataframe 想成一个集合,而是一个有序的行集合。我的源 CSV 里行是这个顺序的,除非我选择追求最高性能,否则我希望它被保持。」当 geysersam 建议在查询末尾加个 order_by 时,他的回应点出了分歧的真正所在:「当然我可以改变我得到的东西,我只是不同意我那张有序的表不被保证保持有序这件事。R 和 Pandas 都不会默认打乱结果。」dash2 提供了一个来自另一个生态的历史佐证:「我曾说服 dplyr 的维护者不要做一个可能在 filter() 之后重排行的更新。我认为『把数据库的行想成存在于一个固定的、给定的顺序中,只会被显式改变』这种人类倾向是很深的。」
winniewinnie 一度怀疑动机是「为了在基准测试上拿到更好的成绩」,Bootvis 的反驳很有说服力:「这是个 API 设计问题,看哪种是更常见的用例。你似乎暗示他们只是为了跑分——他们完全可以自己在跑分时传这个选项而不改默认值,所以那不太可能是动机。」
三、Polars 开发者亲自解释「流式」到底是什么意思
arn3n 提了个直觉性的疑问:默认走流式引擎很有意思,但流式引擎必然是顺序处理行的,直觉上它应该比更容易用批处理并行化的 dataframe 操作更慢——是我的直觉错了,还是我低估了 Polars 做多少自动并行化?
Polars 核心开发者 orlp 的回答是全场最有信息量的一条,也澄清了一个普遍的命名误解:「这里的『流式』和你可能熟悉的含义不同。它不是指那种在无尽数据流涌入时维护聚合/状态的在线处理。这个名字是早期为了与旧的执行模型形成对比而选的——旧模型本质上是全数据在内存、一次一列。那个引擎仍然存在,我们把它用作新引擎尚不支持的东西的回退机制(或者当你显式要求 engine="in-memory" 时)。新的执行模型首先构造一个计算图,节点之间以缓存内数据批次(morsel)的流进行通信,这意味着如果没有必要,完整数据集永远不会被持有在内存里。它在早期原型里因此被称为流式引擎,这个名字就留了下来。事后看,我承认这个命名选择有点令人困惑。」
sanderjd 追问「你说的『缓存内批次』,是指磁盘上的缓存吗?只有数据很大时才这样?」——orlp 的回答揭示了核心的性能原理,也顺便剧透了未来:「嗯……一旦我最近做的核外工作落地,当我们用光内存预算时批次可能会在磁盘上 ;) 但不,我不是那个意思。我指的是批次的大小被设计成能装进你的 CPU 缓存。这对吞吐可以是巨大的提升,因为每一小块数据在从数据源移动到数据汇的过程中都留在缓存里。对比一次一列的执行:当你开始对这一列做下一个操作时,列的开头已经又出了缓存,意味着你在以 RAM 速度(或更糟,磁盘速度)而非缓存速度运行。」他还附上了自己一年多前关于流式引擎的一场(他自称相当浅层的)演讲链接。sanderjd 的反应是:「啊——CPU 缓存这一层正是我漏掉的!这下完全说得通了!」
四、Polars vs Pandas vs SQL(本场最大的跑题)
bobson_dugnutt5 说自己很爱 Polars,在公司里做了大量布道让人们放弃 Pandas——这句话引爆了一场关于语法品味的长跑题。mgaunard 泼了盆冷水:「两个的语法都糟糕到让 SQL 看起来是有史以来最可读的东西。」fzumstein 附议并加了一个时代性的观察:「SQL 过去可能更难写(自动补全比 pandas/polars 差),但现在 AI 在写代码了,SQL 通常读起来容易得多,所以 DuckDB 是 pandas 的另一个有趣替代。」
refactor_master 给出了 Polars 一方最有力的辩护,并附了一段颇长的代码:「Polars 酷的地方在于你可以跨越许多层业务逻辑有条件地攒表达式,然后在最后计算结果。在 SQL 里干这个会变成一堆字符串和为了迎合语法而修剪的结尾。你还可以毫不费力地直接在 Polars 里写相当复杂的条件逻辑,并轻松桥接到周围的 Python。我发现 SQL 只在抽象最少时更易读,但项目一变大,SQL 就变成一座笨重的、格格不入的孤岛,在我们读写完数据之后它的使命就结束了。」他的例子是把 KPI 层、阈值层、交互层、分段层各写成一个返回 list[pl.Expr] 的函数,然后合并成单次图遍历执行。
_zoltan_ 贴出了对应的 SQL 版本并说「抱歉,但这个看起来好多了」,随即遭到三面夹击。bobson_dugnutt5:「真的吗?你把所有的比率和阈值都手写出来了。如果用户想改变阈值的集合,Polars 的方式远为优越。你到底认为这哪里更好?」sanderjd:「……真的吗?我不觉得,即便是这个形式。现在试着把所有条件和变换都(以某种方式)注入到那个字符串里而不是显式写出来。糟糕得多。」throwaway7783:「我更喜欢 Polars 版本。而且如果我想在别处复用这些 CTE,我就得求助于另一个工具比如 DBT,或者手搓点什么来做字符串操作。」
这直接引出了一场关于 dbt 的支线辩论。vovavili 主张认真做查询构建就该用 dbt 而不是字符串操作,理由是:至少你在 SQL 查询里得到了 Jinja2 模板,可以在模板里做带条件逻辑的纯 SQL 变换;加上能跑测试、定义自定义宏、有版本控制、有某种受约束的方式组织表——「你用一个库就把 SQL 变成了一门正经的编程语言。」sanderjd 一直没搞明白 dbt 相对于用 sqlalchemy/duckdb/polars 的 Python 程序有什么优势,追问到最后只剩一句灵魂发问:「但那……是好事吗?」vovavili 的回答很务实:「它绝对好过 500 多行原始 SQL 加 300 多行 Python 拼起来的、没有干净版本控制的某个定制业务变换——而那正是没有 dbt 这类东西时 SQL 变换倾向于收敛到的样子。」throwaway7783 提了个尖锐的批评:「我对 DBT 的问题是它混合了 SQL、YAML、Jinja2 流程控制(metrics 又是另一回事)。带 Jinja2 if/else 的 SQL 会很快变得极难维护。……Polars 是代码,也能版本控制。在我看来 dataframe 更优雅,配上正确的后端和一些血缘增强,能服务比 DBT 更宽泛的用例集合。」sanderjd 的收尾观察相当诚实:实践中他做过的项目最后总是同时混着 dbt、Python 里的 SQL 字符串、和 dataframe 操作三样,「我一直觉得我们应该能压缩掉其中一个而偏向另外两个,但实践中似乎人们发现这三样各自在不同的事情上有用」。
关于「SQL 更可读」这个命题,benrutter 给出了最中肯的划界:「在基础查询上 SQL 真的很好,但当东西变复杂、一堆 CTE、更别提需要循环的函数时,它就变得相当晦涩。」sanderjd 的表述类似但更精确:「我会说 Polars(以及所有 dataframe API)的查询比 SQL 更容易分解、更容易从零件搭起来。每当我发现自己在写超过五行左右的 SQL,尤其是在用逻辑分段构建 SQL 字符串时,我都希望自己手上是个 dataframe API。但反过来也有点成立,更简单、更显式的表达式用 SQL 更好。」geysersam 的总结值得记下:「我同意 SQL 更优雅。问题出在你必须在 SQL 之上添加逻辑的时候——我经常最后通过字符串操作来构造查询,那非常不符合人体工学。Polars 的 API 比 SQL 更啰嗦更复杂,但至少它不是元编程。」他还给出了对 DuckDB Python API 的具体评价:还行,但有点受限,没有 CTE、没有 as-of join,而且在循环里做多个 relation 的 union 之类操作时绑定/解释期可能很慢(他认为那会变成 O(N^2),但不确定)——大部分问题能绕过去,但 Polars 是从头就为从 Python 使用而设计的。
还有一支来自 R 生态的比较。aquafox 拿 dplyr 的 select(x, y = w/z) 对比 Polars 那段更长的 pl.col 写法。bobson_dugnutt5 和 orlp 分别给出了更简洁的 Polars 写法(df.select("x", y=pl.col.w/pl.col.z) 和 from polars import col as C 后 df.select(C.x, y = C.w / C.z)),__mharrison__ 说「这才是正道,优先用关键字参数来起别名」。但 dkga 坚持这仍是「对更符合人体工学、更有表现力的 tidyverse 语法的一个很好的近似,但终究是近似」。sanderjd 的回应值得引用:「一个人的『符合人体工学且有表现力』是另一个人的『等等,这里到底发生了什么』。」他还给出了一个具体的技术理由:看到 R 那个例子他立刻会想 w、x、y、z 到底是变量还是列名——「如果 Python 能更简洁地表示名字和字面字符串(或者更糟,像你 R 例子里那样的变量引用)之间的区别就好了,但可惜。不过我认为用一些啰嗦换取这个区别上的显式性是个相当好的交易,而且非常符合 Python 风格。」jcattle 感慨 R 曾经是更优越的传统数据科学语言(ggplot vs matplotlib、dplyr vs pandas),而且 RStudio 里一切都那么容易检视——有个巨大的 dataframe?直接在 IDE 里看它就行。vovavili 反驳说 Altair 和 Positron 已经够用,配上 Marimo notebook 和 VegaFusion,「Polars/Python 的体验开始以相当大的幅度超过 R」。countrymile 给了个平衡的说法:Polars 与 Pandas 有天壤之别,但 dplyr 对初学者仍是最简单易懂的数据分析入门。
至于为什么该从 Pandas 迁移,bobson_dugnutt5 给了一份清单:快得多、默认多线程;没有 index/MultiIndex(Pandas 对索引的特殊对待总让人觉得麻烦大于价值,不用到处 reset_index());表达式非常可移植——一开始到处写 pl.col 觉得有点多余,但你可以在任何地方定义它们然后在想用的时候应用到任何 dataframe;内化之后语法比 Pandas 合理得多也一致得多。0cf8612b2e1e 给出了更冷静的评估:「说到底两个都能把活干完。Pandas 更符合人体工学,某些想法能更简洁地表达,代价是更多的动态性——如果底层数据更新了,行为可能会变。Polars 更严格,不会悄悄给你翻转一个数据类型,但这种严格性的代价是打字慢一点、有些数据惯用法没有好的 Polars 等价物。人们喜欢强调速度提升,但那是这个库最不有趣的地方——我很少遇到被 Pandas 卡住吞吐的问题。Polars 很大程度上就是一个吸取了一堆教训的 Pandas 2.0,我不认为这是天翻地覆的变化,但有条件时值得迁移。」
五、最有说服力的生产实践证言
perrygeo 写了本场技术含量最高的一条使用心得,而且切入角度独特——他明确说自己对 API 的人体工学或语法毫无兴趣:「对我来说,Polars 的超能力是生产环境的稳定性。Pandas 倾向于把所有问题推到运行时,带着各种隐藏的启发式规则,尤其是围绕列类型和缺失值。你很难知道自己是否测遍了所有边缘情况。测试你代码的唯一方式是把各种数据变体都扔给它。如果你坐在 notebook 前、有耐心替它验证和『清洗』数据,那没问题。如果你因为数据管道期待一个 int 列却拿到 float 而在凌晨三点被叫醒,那就不太行了。Polars 默认更严格,并通过它的规划器把成本前置。由此产生的应用在生产中明显更稳定。你可以测试代码,并对它在野外数据上能工作有合理的把握。」他还加了个附加分:Polars 也有 Rust API,编译器能有效地证明你的程序处理了每一个边缘情况,「写出无人值守跑好几年的 Rust Polars 应用是常事」。
这引出了一段关于 Python 类型检查的争论。sigseg1v 惊讶地问 2026 年了还有人用不对 API 做任何编译期检查的语言吗。perrygeo 的回答很实际:「数据科学/工程领域迷恋 Python。做交互式工作时这很合理,在生产里则毫无道理。但到那时候,多数开发者把它当作沉没成本,宁可让 Python 应用一瘸一拐地活着也不重写。这就是为什么 Polars 是个很好的选项:先用 Python 做原型,等需要来了再相对容易地移植到 Rust 应用。」maleldil 反驳说用 linter 和静态类型检查器仍能有大量「编译期」检查,而且这让 Polars 在带类型的 Python 里更好用,因为它比 Pandas 那团动态混乱要好类型检查得多。perrygeo 的回应带着明显的伤痕:「在这方面 Polars 比 Pandas 好得多。但通过 Python 用它仍然带着运行时错误的风险。我过去三年做过的每一个 Python 应用都 (a) 100% 经过类型检查,然而 (b) 仍然经常撞上大量新奇的运行时错误。Python 贫血的类型系统绝不是先进编译器的替代品。」what 的回击很干脆:「如果它是 100% 类型检查过的而你还在撞上大量类型相关的运行时错误,那它大概不是 100% 检查过的。」paddy_m 提出了一个更细致的区分:「做数据科学时,我很少遇到 Python 类型检查器抓不到的程序类型问题。我遇到的是我读进来的 dataframe/csv/parquet 的类型和脏数据问题。」perrygeo 对这个区分的反应很有意思:「这两句话让我脑子要炸了。你很少有类型问题,除了处理数据的时候?处理数据不就是程序的全部意义吗?」
六、文档细节与零散吐槽
有两位读者不约而同地对同一处文档缺陷提出了意见。bbstats:「『改用 .cat.to(dtype) 做 int → categorical,.cat.physical() 做 categorical → int』这句没给出必需的代码示例,快把我逼疯了!」wodenokoto 说得更完整:「永远要展示正确的做法。我完全不知道这里正确的写法是什么,而且我也没真看出这个 API 变化带来了什么好处。」值得称道的是,Polars 团队的 thijsn 直接下场回复,给出了迁移指南中对应章节的链接,并说:「告诉我你还缺什么,如果你愿意可以开个 issue,我保证在 2.0 正式版之前处理掉。」
dtj1123 注意到原文里一个 their/they're 的笔误,感慨道:「现在看到一个错别字会想『哦酷,这是人写的,我可以认真对待』而不是『天哪,他们连拼写都不会,我不能认真对待』,这变成相对正常的事了,真是奇怪。」
Kydlaw 提到自己因为相比 Pandas 和 SQL 更好的人体工学而把 Polars 当作首选库,但「他们最近有点安静,我开始越来越多地看 DuckDB……直到最近 DuckLab 被 AWS 收购」。这引出了一小段替代方案讨论:f311a 说自己用 clickhouse-local 而非 DuckDB 已经有段时间了(还有 chDB),理由很实在——他们生产环境用的就是常规 ClickHouse,他熟悉它,而且总能在有 1TB 内存的生产服务器上跑大查询,常规 CH 还支持外部数据源,所以他能从 S3 读 500GB 的 JSON 并在生产服务器上极快地在内存里做 group by。他还提了一句很多人会共鸣的话:「用了 pandas 十年之后,不知为何我现在偏爱 SQL。」Kydlaw 自己的分工是:需要互操作性的数据管道和处理用 SQL,数据探索则通常偏好 Polars(他认为处理文本和半结构化数据时更容易)。
lmeyerov 分享了一个具体的落地案例:他们最近给 GFQL(在 dataframe 上做 Cypher 图查询,不需要数据库)加了 Polars 后端,CPU 和 GPU 模式都有,「印象非常深刻,相对 pandas/cudf 有明显改进,并让 GFQL 在更多类别上击败流行系统,不只是大数据集,还包括低延迟」。
最后是本场最好笑的一条。latexr 对 bobson_dugnutt5 那句「做了大量布道让人们放弃 pandas」的解读:「脱离上下文看,你这帖子读起来像是一个自然保护主义者受够了大熊猫当旗舰物种,把毕生使命定为用北极熊取代它们。这不是批评。作为一个不用 Python 的人,我只是觉得很好笑。」blitzar 顺势补刀:「你该学蟒蛇(Boa constrictor)而不是 Python。」bobson_dugnutt5 的回应:「谢谢,你是唯一正确理解我评论的人 :)」