Polars 非连续唯一性过滤器

问题描述 投票:0回答:1

TL;博士

我希望能够简洁地(具有性能)过滤连续行唯一性,而不是连续行唯一性。

上下文

我有一个 LazyFrame,其中的数据已经正确排序。虽然数据的每一行在所有列中始终是真正唯一的(因为它带有时间戳),但我感兴趣的特定子集(列)可以具有不连续的重复项,例如:

id 数据 注意
0 A 新价值(第一次见)
1 A 连续重复
2 新价值
3 A 我想要的 A 的非连续副本!
4 C 新价值
5 D 新价值

当使用

polars.LazyFrame.unique
时,这两个不同系列的
A
0-1
3
不能作为连续(不连续)唯一性的唯一检查。我可以保留行
0
3
而不是(如果使用
keep='last'
)说行
1
3
.

我已经使用以下代码为以下 MRE 数据集解决了这个问题,但我想知道是否有更好的方法。带有

(want)
(仅供说明)的行是我想要的。行 ID 有助于对行进行视觉搜索,这里唯一真正的判别是
foo
bar
baz
列。

我通过添加掩码值

0
1
来解决这个问题,每次一行与前一行(在所需列上)不同时。然后将这个掩码值累加起来得到一个唯一值(在掩码列本身内),我可以运行
unique
以保留不连续的重复值,同时删除连续的重复值。

id 酒吧 巴兹 nb
a_1 33 33 33 新(想要)
b_1 22 33 33
b_2 22 33 33 重叠群复制
b_3 22 33 33 重叠群复制
b_4 22 33 33 重叠群复制
b_5 22 33 33 contig dup last(想要)
c_1 22 33 22
c_2 22 33 22 重叠群复制
c_3 22 33 22 重叠群复制
c_4 22 33 22 重叠群复制
c_5 22 33 22 contig dup last(想要)
d_1 22 33 33 b_1 等的新非重叠群复制(想要)
e_1 22 33 22 c_1 等的新非重叠群复制(想要)
f_1 22 22 22
f_2 22 22 22 重叠群复制
f_3 22 22 22 重叠群复制
f_4 22 22 22 重叠群复制
f_5 22 22 22 contig dup last(想要)
g_1 11 11 11
g_2 11 11 11 重叠群复制
g_3 11 11 11 重叠群复制
g_4 11 11 11 重叠群复制
g_5 11 11 11 contig dup last(想要)

当前解决方案:

dirty_df = pl.scan_csv(non_contig_data)

clean_df = (
    dirty_df
    # Construct the increment mask of 0 or 1.
    .with_columns(
        (
            pl.col('foo').diff().cast(pl.Boolean) |
            pl.col('bar').diff().cast(pl.Boolean) |
            pl.col('baz').diff().cast(pl.Boolean)
        ).fill_null(True).cast(pl.Int32).alias('incr_grouping_mask')
    )
    # Cumulatively sum that mask.
    .with_columns(
        pl.col('incr_grouping_mask').cumsum()
    )
    # Run unique over the cumulative sum of the mask.
    .unique(
        maintain_order=True,
        subset='incr_grouping_mask',
        keep='last'
    )
    .collect()
)

print(clean_df)

产生正确的结果:

shape: (7, 6)
┌─────┬─────┬─────┬─────┬───────────────────────────────────┬────────────────────┐
│ id  ┆ foo ┆ bar ┆ baz ┆ nb                                ┆ incr_grouping_mask │
│ --- ┆ --- ┆ --- ┆ --- ┆ ---                               ┆ ---                │
│ str ┆ i64 ┆ i64 ┆ i64 ┆ str                               ┆ i32                │
╞═════╪═════╪═════╪═════╪═══════════════════════════════════╪════════════════════╡
│ a_1 ┆ 33  ┆ 33  ┆ 33  ┆ new (want)                        ┆ 1                  │
│ b_5 ┆ 22  ┆ 33  ┆ 33  ┆ contig dup last (want)            ┆ 2                  │
│ c_5 ┆ 22  ┆ 33  ┆ 22  ┆ contig dup last (want)            ┆ 3                  │
│ d_1 ┆ 22  ┆ 33  ┆ 33  ┆ new non-contig dup of b_1 etc (w… ┆ 4                  │
│ e_1 ┆ 22  ┆ 33  ┆ 22  ┆ new non-contig dup of c_1 etc (w… ┆ 5                  │
│ f_5 ┆ 22  ┆ 22  ┆ 22  ┆ contig dup last (want)            ┆ 6                  │
│ g_5 ┆ 11  ┆ 11  ┆ 11  ┆ contig dup last (want)            ┆ 7                  │
└─────┴─────┴─────┴─────┴───────────────────────────────────┴────────────────────┘
python-polars
1个回答
0
投票

我想出了另外两个变体,一个使用

pl.concat_list
,另一个使用
pl.fold
。我为感兴趣的列添加了一个简单的前缀,以便我可以使用正则表达式选择它们。

pl.concat_list

clean_df = (
    df
    .with_columns(
        pl.concat_list(
            pl.col('^dp_.*$')
            .diff()
            .cast(pl.Boolean)
            .fill_null(True)
        )
            .arr
            .contains(True)
            .cast(pl.Int32)
            .alias('incr_grouping_mask')
            .cumsum()
    )
    .unique(
        maintain_order=True,
        subset='incr_grouping_mask',
        keep='last'
    )
    .collect()
)

pl.fold

clean_df = (
    df
    .with_columns(
        pl.fold(
            acc=pl.lit(False),
            function=lambda acc, x: acc | x,
            exprs=pl.col('^dp_.*$')
                .diff()
                .cast(pl.Boolean)
                .fill_null(True)
        )
            .cast(pl.Int32)
            .alias('incr_grouping_mask')
            .cumsum()
    )
    .unique(
        maintain_order=True,
        subset='incr_grouping_mask',
        keep='last'
    )
    .collect()
)
© www.soinside.com 2019 - 2024. All rights reserved.