导读:本期聚焦于小伙伴创作的《如何用Polars的repeat_by和flatten方法高效复制DataFrame中的行?》,敬请观看详情。在数据处理时,经常需要把某一行按照指定次数展开成多行,例如订单明细按购买数量生成记录。Polars提供了repeat_by配合flatten的组合来完成这一操作,比用循环或explode字符串更省内存。repeat_by会在每个元素位置生成一个列表,列表长度由传入的表达式决定,随后flatten将嵌套结构压平为普通列。这种方式完全基于Rust底层向量化计算,避免了Python层逐行遍历。实践中要注意repeat_by接收的是无缺失的整数表达式,若来源列含空值需先fill_null。此外,展开后原行的其他字段会自动对齐广播,不需要手动拼接。掌握该技巧能显著提升数据扩增与模拟任务的执行效率。

在处理表格数据的时候,我们常常遇到这样的需求:某一行需要根据某个计数值被复制成多行。比如一条购买记录里商品数量是三,就希望展开为三条明细。Polars作为用Rust编写的高性能DataFrame库,提供了repeat_by和flatten两个表达式方法来优雅地解决这一类行复制问题。这两个方法组合使用,可以在不写循环的情况下完成向量化展开。

如何用Polars的repeat_by和flatten方法高效复制DataFrame中的行?

repeat_by方法的基本用法

repeat_by是Polars中Expr对象的一个方法,它的作用是:对列中的每一个元素,根据给定的整数表达式重复该元素,生成一个列表(List类型)。例如,如果我们有一列数量qty,对某一列值调用repeat_by('qty'),就会把该列每个值重复qty次,放进一个列表里。这一步并不会增加行数,而是把单行内容变成嵌套结构。

下面用一个简单示例说明。假设我们有一个商品表,包含商品名称和数量,希望把名称按数量复制成列表:

import polars as pl

df = pl.DataFrame({
    'item': ['apple', 'banana', 'cherry'],
    'qty': [2, 3, 1]
})

# 对item列使用repeat_by,按qty重复
res = df.with_columns(
    pl.col('item').repeat_by('qty').alias('item_repeated')
)
print(res)

运行后,item_repeated列是一个列表列,apple对应['apple', 'apple'],banana对应['banana', 'banana', 'banana'],cherry对应['cherry']。可以看到,repeat_by非常直观地完成了“按次数重复”的语义,而且完全在引擎内部完成,没有Python层循环。

用flatten将嵌套列表展开为多行

repeat_by生成的列表仍然停留在单列中,要想真正“复制行”,还需要把列表拆开成独立的行。这时就要用到flatten。flatten是DataFrame或LazyFrame的方法,也可以作为表达式使用,它的功能是消除一层嵌套,将列表中的每个元素变为单独的行,同时保留其他列的对应值。

我们将上一步的结果继续处理:

# 接上面res
expanded = res.select(
    pl.col('item_repeated').flatten(),
    pl.col('qty')
)
print(expanded)

此时输出已经是一个普通的DataFrame,item_repeated列变成了六行,每行一个水果名称,qty列的值也跟着原本的行对齐保留。如果我们在最开始就用with_columns生成重复列表,然后对整个DataFrame调用flatten,就能一步得到展开后的明细表。实际编码中常写成链式:

detail = df.with_columns(
    pl.col('item').repeat_by('qty').alias('item')
).flatten()

print(detail)

这样得到的detail里,item列直接是展开后的多行字符串,原qty列完整保留。相比用df.filter加循环拼接,这种写法既简洁又高效。

处理缺失值与复杂字段

在真实数据里,repeat_by依赖的整数表达式可能包含空值。如果qty列有null,repeat_by会报错或产生未知行为,因此要先处理缺失。通常可以用fill_null赋予默认值,比如填0表示不复制,或填1保证至少一行。

示例代码如下:

df2 = pl.DataFrame({
    'item': ['apple', 'banana', None],
    'qty': [2, None, 3]
})

safe = df2.with_columns(
    pl.col('qty').fill_null(0).alias('qty_safe')
).with_columns(
    pl.col('item').repeat_by('qty_safe').alias('item')
).flatten()

print(safe)

这里把qty的null填为0,banana就不会产生复制行。如果原表还有其他字段,如价格、分类,它们都会随着repeat_by生成的列表在flatten时自动广播对齐,不需要我们手动做笛卡尔积或者merge。这是Polars列式存储模型带来的便利:所有列在同一行号上保持长度一致。

性能对比与适用场景

很多初学者会用Python的for循环遍历DataFrame,把每一行append多次,这种写法在十万级以上数据就会非常慢,而且内存占用高。repeat_by加flatten完全在Rust端向量化执行,不仅代码短,而且速度通常快几十倍。我们可以用简单的时间测试感受差异:

import time

big = pl.DataFrame({
    'id': pl.arange(0, 100000, dtype=pl.Int32),
    'n': pl.Series([1,2,3,4,5]*20000)
})

t0 = time.time()
out = big.with_columns(
    pl.col('id').repeat_by('n').alias('id')
).flatten()
print('polars repeat_by:', time.time() - t0)

在普通笔记本上,这段展开二十多万行的操作一般只需几十毫秒。它特别适合做数据增强、日志展开、订单明细生成、模拟用户行为等任务。需要注意的是,如果重复次数极大(如某些n为十万),展开后行数会暴涨,要提前估算内存;此时可考虑分块处理或使用LazyFrame的流式执行。

小结

通过repeat_by我们先按指定字段把元素变成列表,再用flatten压平,就完成了高效的行复制。核心要点是:repeat_by接收整数表达式、flatten消除嵌套、缺失值提前fill_null、其他列自动对齐。掌握这套组合,能让你在用Polars做数据变形时避开低效循环,写出更地道的向量化代码。

Polarsrepeat_byflatten修改时间:2026-08-03 04:03:27

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。