LLM 能否将 Polars 代码转换为 SQL
本文探讨了将Polars代码转换为SQL的三种方法。研究发现,虽然LLM(包括GPT-5.1、DeepSeek V3.1和Qwen3 Coder)能够进行转换,但在处理空值等细节时容易出现错误。通过优化提示词可以提高准确性,但更可靠的方案是使用Narwhals工具,它能自动生成正确的SQL查询。建议优先使用Narwhals,仅在其功能不足时才考虑LLM方案,并需详细说明预期行为以确保转换准确。研究
结构化查询语言 (SQL)可能是工程师与数据交互最常用的方式。几乎所有数据服务都提供 SQL 接口。它通常被认为是数据科学家必备的技能,对数据工程师而言更是如此。SQL 具有可移植性、广泛应用、高度标准化和强大的功能。
尽管 SQL 优势显著,数据科学家通常更喜欢使用称为“数据框(dataframes)”(例如 pandas 和 Polars )的工具来进行数据分析。这不仅是因为dataframes工具的生态系统非常丰富,还因为dataframes允许他们以一种通常比 SQL 更自然的方式表达数据分析任务。
鉴于 Polars 和 SQL 的流行程度,我们将探讨它们之间的转换效果。给定一段 Polars 代码,是否可以将其转换为 SQL?转换后的输出是否相同?本文并非旨在进行全面的基准测试,而是着重探讨以下几个方面:
- LLM的回答是否存在某种模式,这种模式是否具有一定的可预测性?
- 我们可以采取哪些措施来提高 LLM 的回答质量?
- 我们可以使用开源的 LLM 来完成这项任务吗?
- 是否有非 LLM 解决方案?
提示词(Prompt)
我们将提出一个提示词,该提示词涉及将 Polars 语法转换为 SQL 的几个方面:聚合、空值处理和广播。提示词如下:
给定一个包含值的表 df
{'price': [1, 4, None, 4]}
你能把这段 Polar 代码翻译成 SQL 吗?
print(df.select(pl.col('price') - pl.col('price').mean())) # Task 1
print(df.select(pl.col('price').n_unique())) # Task 2
print(df.select(pl.col('price').rank('dense'))) # Task 3
预期结果
让我们来看一些正确翻译的例子。第一个例子要求我们将聚合值( AVG(price) )与列( price )进行比较,我们可以通过使用 OVER () 来广播聚合值来实现这一点:
SELECT
price - AVG(price) OVER () AS price_centered
FROM df;
对于第二个问题, COUNT(DISTINCT price) 结果比较接近,但需要注意的关键细节是,Polars 默认将空值包含在 n_unique 中,而 COUNT(DISTINCT ...) 则不会。因此,需要进行一些额外的后处理——一种方法是使用 CASE-WHEN 表达式:
SELECT
COUNT(DISTINCT price)
+ MAX(
CASE
WHEN price IS NULL THEN 1
ELSE 0
END
) AS price
FROM df;
对于最后一个问题,Polars 会保留空值,只对非空值进行排名,因此我们正在寻找类似这样的解决方案:
SELECT
price,
CASE
WHEN ((NOT (price IS NULL))) THEN (dense_rank() OVER (ORDER BY price ASC NULLS LAST))
ELSE NULL
END AS d
FROM df;
这些是模型答案的示例。你认为LLM的答案能与这些示例有多接近?
认识一下LLM
为此,我们将比较三款免费模型:
- 我们将使用 OpenAI 的 GPT-5.1(专有)版本在 ChatGPT 上运行。
- DeepSeek V3.1(开源,MIT 许可证)。我们将在 OpenRouter 上运行它。
- 阿里巴巴的 Qwen3 Coder 480B A35B(开源,Apache 2.0 许可证)。我们也会在 OpenRouter 上运行它。
第一个是大家都知道的。可惜的是,对很多人来说,他们对 LLM 的了解也就止步于此了。很多人甚至不知道还有开源的替代方案——让我们改变这种现状!
对 LLM 进行测试
以下是截至 2025 年 12 月 26 日 LLM 项目结果的总结:
任务1
- GPT:正确!
- Deepseek:正确!
- Qwen:答对了!好吧,这道题对他们来说太简单了。我们来做更难的题吧。
任务 2
- GPT:错误!
- Deepseek:错误!
- Qwen:不对!他们都犯了同样的错误,生成的查询语句大致如下:
SELECT
COUNT(DISTINCT price) AS n_unique
FROM df;
错误之处在于它会丢弃空值,而 Polars 则会包含空值。盲目信任 LLM 的转换结果而不进行检查可能会导致生产故障或错误的业务决策!
任务3
- GPT:正确!
- Deepseek:错误!
- Qwen:错误!后两者犯了同样的错误,输出类似这样的内容:
SELECT DENSE_RANK() OVER (ORDER BY price) FROM df;
错误的原因在于它将空值排在最后,而 Polars 会保留空值,只对非空元素进行排名。GPT 能够正确处理这一点,并输出:
SELECT
CASE
WHEN price IS NULL THEN NULL
ELSE DENSE_RANK() OVER (ORDER BY price)
END AS dense_rank_price
FROM df;
请注意,这里增加了保留空值的逻辑,而其他两个逻辑中则缺少这个逻辑。
改进提示词能否改善结果?
LLM 的故障确实存在某种模式:它们生成的代码看起来合情合理。唯一的问题是,仔细检查后发现,诸如空值处理之类的细节并非总是得到妥善处理。我们能否通过提醒 LLM 注意 Polar 的行为细节来解决这个问题呢?
答案是肯定的!实际上,通过附加……
请记住,Polars 模型会在 n_unique 中统计空值,并在 rank 中保留空值。
根据我们的提示词,我们发现所有三个模型都能对所有任务给出正确的结果!因此,可以使用这些模型将 Polars 数据转换为 SQL,但这需要一些技巧和领域知识来确保转换的正确性。
非人工智能解决方案:Narwhals
LLM(逻辑逻辑模型)极易产生幻觉,其输出结果绝不可盲目信任。如果我们想要一个更稳健、更可预测的解决方案,可以考虑使用名为 Narwhals 的开源工具。Narwhals 是一个轻量级的数据帧库兼容层——尤其支持 DuckDB,因此我们可以用它来生成 SQL。
以下是 Narwhals 对上述第二个任务(所有 LLM 都出错的任务)的解决方案示例:
import polars as pl
import narwhals as nw
data = {'price': [1, 4, None, 4]}
df = pl.DataFrame(data)
print(
nw.from_native(df).lazy('duckdb')
.select(nw.col('price').n_unique())
.to_native().sql_query()
)
输出结果的第一行显示
SELECT
(count(DISTINCT price)
+ max(CASE WHEN ((price IS NULL)) THEN (1) ELSE 0 END)) AS price
FROM ColumnDataCollection - [1 Chunks, 4 Rows]
而且这是正确的 SQL 转换——无需手动提示词工程!这种方法安全可靠,经过充分测试,不会产生任何不切实际的后果。缺点是它仅限于 Narwhals API 中包含的功能,而 LLM 至少可以尝试转换更复杂、更专业的查询。
如果您想资助未来与dataframes无关的工作流,或者需要定制 Narwhals 解决方案方面的帮助,可以联系 Quansight 。
结论
我们研究了如何将 Polars 代码转换为 SQL,并比较了不同的解决方案:
- 专有人工智能模型
- 开源人工智能模型
- Narwhals
我们发现,AI 模型可能会出现一些细微的错误,但可以通过更好的提示词工程进行纠正。最后,我们发现 Narwhals 无需提示词工程即可进行正确的翻译,但这种方法仅限于 Narwhals API 中已实现的功能。如果您计划使用 LLM 将 Polar 转换为 SQL,我们建议您首先尝试 Narwhals,以避免 LLM 出现“幻觉”(即模型无法正确翻译)。只有在 Narwhals API 无法满足您的任务需求时,才考虑使用 LLM。在这种情况下,请务必尽可能详细地描述预期行为,以便模型能够最大限度地提高翻译的准确性。
可前往笔者微信公众号原文交流:LLM 能否将 Polars 代码转换为 SQL
更多推荐



所有评论(0)