如何在Polars中实现仅保留组合的交叉连接(无需先生成全组合)
如何在Polars中高效生成两列的组合(非排列)结果,无需先做全交叉连接?
我需要连接两个完全相同的列,仅保留组合(而非排列)结果(即只保留a < b的行)。目前的实现是先执行全交叉连接再过滤,代码如下:
import polars as pl df1 = pl.DataFrame({"a": range(3)}) df2 = pl.DataFrame({"b": range(3)}) expected_output = df1.join(df2, how="cross").filter(pl.col("a") < pl.col("b"))
预期输出为:
shape: (3, 2) ┌─────┬─────┐ │ a ┆ b │ │ --- ┆ --- │ │ i32 ┆ i32 │ ╞═════╪═════╡ │ 0 ┆ 1 │ │ 0 ┆ 2 │ │ 1 ┆ 2 │ └─────┴─────┘
请问有没有不需要先生成全组合的实现方法?
当然有,你可以直接生成符合a < b条件的索引对,再映射到原数据,避免全量交叉连接带来的性能开销,数据量越大,这种方式的优势越明显。
方法1:索引映射法(推荐)
通过行索引生成合法的组合对,再关联原数据:
import polars as pl # 用单个DataFrame替代两个相同的df,减少冗余 df = pl.DataFrame({"val": range(3)}) result = df.with_row_index("idx").select( pl.col("val").alias("a"), # 对每个行索引,生成从idx+1到末尾的索引序列并展开 pl.int_range(pl.col("idx") + 1, len(df)).explode().alias("b_idx") ).join(df.with_row_index("idx"), left_on="b_idx", right_on="idx") \ .select("a", pl.col("val").alias("b"))
执行结果与预期一致:
shape: (3, 2) ┌─────┬─────┐ │ a ┆ b │ │ --- ┆ --- │ │ i32 ┆ i32 │ ╞═════╪═════╡ │ 0 ┆ 1 │ │ 0 ┆ 2 │ │ 1 ┆ 2 │ └─────┴─────┘
方法2:窗口函数法
如果数据本身是有序的,也可以用窗口函数结合explode实现,但性能不如索引映射法:
import polars as pl df = pl.DataFrame({"val": range(3)}) result = df.select( pl.col("val").alias("a"), pl.col("val").filter(pl.col("val") > pl.col("val").over()).explode().alias("b") )
性能说明
当数据量较大时(比如1000行),全交叉连接会生成100万行再过滤,而索引映射法直接生成约50万行目标数据,内存占用和执行时间都会大幅降低。
内容的提问来源于stack exchange,提问作者gggg
相关产品推荐
相关产品推荐

