我希望能够简洁地(具有性能)过滤连续行唯一性,而不是连续行唯一性。
我有一个 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 │
└─────┴─────┴─────┴─────┴───────────────────────────────────┴────────────────────┘
我想出了另外两个变体,一个使用
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()
)