导读:本期聚焦于弥生美月创作的《如何用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两个表达式方法来优雅地解决这一类行复制问题。这两个方法组合使用,可以在不写循环的情况下完成向量化展开,既简洁又高效。

repeat_by方法的基本用法与原理

repeat_by是Polars中Expr对象的一个方法,它的作用是:对列中的每一个元素,根据给定的整数表达式重复该元素,生成一个列表(List类型)。例如,如果我们有一列数量qty,对某一列值调用repeat_by('qty'),就会把该列每个值重复qty次,放进一个列表里。这一步并不会增加行数,而是把单行内容变成嵌套结构。理解这一点非常重要,因为很多初学者会误以为repeat_by直接就完成了行复制,实际上它只是第一步的准备工作。

下面用一个简单示例说明。假设我们有一个商品表,包含商品名称和数量,希望把名称按数量复制成列表。我们先创建一个包含item和qty两列的DataFrame,然后对item列调用repeat_by方法,传入qty列作为重复次数的依据。执行后,结果中的item_repeated列是一个列表列,apple对应['apple', 'apple'],banana对应['banana', 'banana', 'banana'],cherry对应['cherry']。可以看到,repeat_by非常直观地完成了"按次数重复"的语义,而且完全在引擎内部完成,没有Python层循环。

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列中每一行的值变成了一个列表。这种嵌套结构是Polars列式存储模型的自然产物,它允许我们在不改变DataFrame行数的情况下,预先计算出每个元素需要展开后的完整内容。repeat_by方法接收的参数是一个整数表达式,可以是列名,也可以是常量或更复杂的表达式,灵活性很高。

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

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

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

# 接上面res的结果,将列表列展开为多行
expanded = res.select(
    pl.col('item_repeated').flatten(),
    pl.col('qty')
)
print(expanded)

# 也可以写成链式调用,一步到位
detail = df.with_columns(
    pl.col('item').repeat_by('qty').alias('item')
).flatten()

print(detail)

这样得到的detail里,item列直接是展开后的多行字符串,原qty列完整保留。相比用df.filter加循环拼接,这种写法既简洁又高效。flatten的核心优势在于它是向量化的操作,在Rust端原生执行,不需要在Python层逐行处理。当列表长度不一致时,flatten也能正确处理,它会按照每个列表的实际长度展开,不会强制对齐到统一长度。这种特性使得repeat_by和flatten的组合非常适合处理真实世界中不规则的数据展开需求。

处理缺失值与复杂字段场景

在真实数据里,repeat_by依赖的整数表达式可能包含空值。如果qty列有null,repeat_by会报错或产生未知行为,因此要先处理缺失。通常可以用fill_null赋予默认值,比如填0表示不复制,或填1保证至少一行。这个预处理步骤非常关键,因为Polars在遇到null作为重复次数时无法确定应该生成多少个元素,直接调用会导致运行时错误或数据不一致。

下面通过一个包含缺失值的示例来演示完整的处理流程。我们创建一个item和qty列都有null的DataFrame,先用fill_null(0)处理qty列的空值,然后再调用repeat_by和flatten。这里把qty的null填为0,banana就不会产生复制行。如果原表还有其他字段,如价格、分类,它们都会随着repeat_by生成的列表在flatten时自动广播对齐,不需要我们手动做笛卡尔积或者merge。

# 创建包含缺失值的数据
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)

这是Polars列式存储模型带来的便利:所有列在同一行号上保持长度一致。当某一列被展开为多行时,其他列会自动按照对应关系进行广播,无需额外的join操作。需要注意的是,如果item本身也是null,repeat_by会生成一个包含null的列表,flatten后该null会成为一个独立的行。如果这不是期望的行为,可以在repeat_by之前先用filter过滤掉item为null的行,或者在flatten之后用drop_nulls清理。对于包含日期、结构体等复杂类型的列,repeat_by和flatten同样适用,因为它们操作的是列级别的数据结构,与元素的具体类型无关。

性能对比与适用场景分析

很多初学者会用Python的for循环遍历DataFrame,把每一行append多次,这种写法在十万级以上数据就会非常慢,而且内存占用高。repeat_by加flatten完全在Rust端向量化执行,不仅代码短,而且速度通常快几十倍。我们可以用简单的时间测试感受差异:创建一个包含十万行数据的DataFrame,其中重复次数列n的值在1到5之间循环,展开后的总行数约为三十万行。

import time

# 构造十万行测试数据
big = pl.DataFrame({
    'id': pl.arange(0, 100000, dtype=pl.Int32),
    'n': pl.Series([1, 2, 3, 4, 5] * 20000)
})

# 使用repeat_by加flatten向量化展开
t0 = time.time()
out = big.with_columns(
    pl.col('id').repeat_by('n').alias('id')
).flatten()
print('polars repeat_by:', time.time() - t0)
print('展开后行数:', out.height)

在普通笔记本上,这段展开二十多万行的操作一般只需几十毫秒。它特别适合做数据增强、日志展开、订单明细生成、模拟用户行为等任务。需要注意的是,如果重复次数极大(如某些n为十万),展开后行数会暴涨,要提前估算内存;此时可考虑分块处理或使用LazyFrame的流式执行来控制内存峰值。另外,当重复次数列的值分布极度不均匀时,比如少数行有极大的重复次数,展开操作可能导致内存激增,建议先用describequantile检查数据分布,再决定是否需要分批处理。

小结与要点回顾

通过repeat_by我们先按指定字段把元素变成列表,再用flatten压平,就完成了高效的行复制。核心要点是:repeat_by接收整数表达式作为重复次数,生成List类型的嵌套列;flatten消除嵌套结构,将列表元素展开为独立行;缺失值要提前用fill_null处理,避免运行时错误;其他列在flatten时会自动广播对齐,无需手动关联。掌握这套组合,能让你在用Polars做数据变形时避开低效循环,写出更地道的向量化代码。

在实际项目中,这套方法组合的应用场景非常广泛。比如电商系统中的订单展开,一条订单记录包含多个商品及数量,需要展开为明细行进行后续分析;日志处理中,一条聚合日志可能包含多个子事件,需要展开后逐条分析;数据增强场景下,需要根据权重或次数复制样本以平衡数据分布。无论哪种场景,repeat_by和flatten都能提供一致的、高性能的解决方案。建议在使用时始终关注数据规模和内存占用,对于超大数据集优先考虑LazyFrame模式,让Polars的查询优化器自动规划执行策略,从而在保证正确性的同时最大化性能表现。

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

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