Фильтр несмежной уникальности PolarsPython

Программы на Python
Ответить
Anonymous
 Фильтр несмежной уникальности Polars

Сообщение Anonymous »

TL;DR
Мне хотелось бы иметь возможность кратко (с производительностью) фильтровать уникальность непрерывных строк, а не уникальность непрерывных строк.
Контекст
У меня есть LazyFrame с уже правильно упорядоченными данными. Хотя каждая строка данных всегда будет действительно уникальной во всех столбцах (с указанием времени), конкретное подмножество (столбцы), которое меня интересует, может иметь несмежные дубликаты, например:
┌─────┬──────┬─────────────────────────────────────────────┐
│ id ┆ data ┆ note │
│ --- ┆ --- ┆ --- │
│ i64 ┆ str ┆ str │
╞═════╪══════╪═════════════════════════════════════════════╡
│ 0 ┆ A ┆ new value (first time it's seen) │
│ 1 ┆ A ┆ contiguous duplicate │
│ 2 ┆ B ┆ new value │
│ 3 ┆ A ┆ non-contiguous duplicate of A which I want! │
│ 4 ┆ C ┆ new value │
│ 5 ┆ D ┆ new value │
└─────┴──────┴─────────────────────────────────────────────┘

При использовании Polars.LazyFrame.unique эти две разные серии A в 0-1 и 3 не могут использоваться в качестве уникальных проверок непрерывной (не непрерывной) уникальности. Я мог бы сохранить строку 0 или строку 3, а не (при использовании Keep='last') сказать строку 1 и строку 3.
Я решил эту проблему для следующего набора данных MRE, используя следующий код, но мне интересно, есть ли лучший способ. Строки с (хочу) (исключительно для иллюстрации) — это те, которые мне нужны. Идентификаторы строк предназначены для визуального поиска строк. Единственными реальными дискриминантами здесь являются столбцы foo, bar и baz.
Я решил эту проблему, добавляя значение маски 0 или 1 каждый раз, когда строка не совпадает с предыдущей (над нужными столбцами). Затем совокупное суммирование этого значения маски дает уникальное значение (внутри самого столбца маски), которое я могу использовать unique, чтобы сохранить несмежные повторяющиеся значения и удалить смежные.
pl.Config(tbl_rows=23, fmt_str_lengths=42) # increase repr defaults

df = pl.from_repr("""
┌─────┬─────┬─────┬─────┬──────────────────────────────────────┐
│ id ┆ foo ┆ bar ┆ baz ┆ nb │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ i64 ┆ i64 ┆ str │
╞═════╪═════╪═════╪═════╪══════════════════════════════════════╡
│ a_1 ┆ 33 ┆ 33 ┆ 33 ┆ new (want) │
│ b_1 ┆ 22 ┆ 33 ┆ 33 ┆ new │
│ b_2 ┆ 22 ┆ 33 ┆ 33 ┆ contig dup │
│ b_3 ┆ 22 ┆ 33 ┆ 33 ┆ contig dup │
│ b_4 ┆ 22 ┆ 33 ┆ 33 ┆ contig dup │
│ b_5 ┆ 22 ┆ 33 ┆ 33 ┆ contig dup last (want) │
│ c_1 ┆ 22 ┆ 33 ┆ 22 ┆ new │
│ c_2 ┆ 22 ┆ 33 ┆ 22 ┆ contig dup │
│ c_3 ┆ 22 ┆ 33 ┆ 22 ┆ contig dup │
│ c_4 ┆ 22 ┆ 33 ┆ 22 ┆ contig dup │
│ c_5 ┆ 22 ┆ 33 ┆ 22 ┆ contig dup last (want) │
│ d_1 ┆ 22 ┆ 33 ┆ 33 ┆ new non-contig dup of b_1 etc (want) │
│ e_1 ┆ 22 ┆ 33 ┆ 22 ┆ new non-contig dup of c_1 etc (want) │
│ f_1 ┆ 22 ┆ 22 ┆ 22 ┆ new │
│ f_2 ┆ 22 ┆ 22 ┆ 22 ┆ contig dup │
│ f_3 ┆ 22 ┆ 22 ┆ 22 ┆ contig dup │
│ f_4 ┆ 22 ┆ 22 ┆ 22 ┆ contig dup │
│ f_5 ┆ 22 ┆ 22 ┆ 22 ┆ contig dup last (want) │
│ g_1 ┆ 11 ┆ 11 ┆ 11 ┆ new │
│ g_2 ┆ 11 ┆ 11 ┆ 11 ┆ contig dup │
│ g_3 ┆ 11 ┆ 11 ┆ 11 ┆ contig dup │
│ g_4 ┆ 11 ┆ 11 ┆ 11 ┆ contig dup │
│ g_5 ┆ 11 ┆ 11 ┆ 11 ┆ contig dup last (want) │
└─────┴─────┴─────┴─────┴──────────────────────────────────────┘
""")

Текущее решение:
(
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').cum_sum()
)
# Run unique over the cumulative sum of the mask.
.unique(
maintain_order=True,
subset='incr_grouping_mask',
keep='last'
)
)

Для получения правильного результата:
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 │
└─────┴─────┴─────┴─────┴───────────────────────────────────┴────────────────────┘


Подробнее здесь: https://stackoverflow.com/questions/757 ... ess-filter
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»