У меня есть pl.DataFrame
со столбцом, содержащим списки с целыми числами. Мне нужно утверждать, что каждое последовательное целое число появляется максимум два раза подряд.
Например, список, содержащий [1,1,0,-1,1]
, подойдет, так как число 1 появляется максимум два раза подряд (первые два элемента, за которыми следует ноль).
Этот список должен привести к неверному утверждению: [1,1,1,0,-1]
Число 1
появляется три раза подряд.
Вот игрушечный пример, где row2
должно приводить к ошибочному утверждению.
import polars as pl
row1 = [0, 1, -1, -1, 1, 1, -1, 0]
row2 = [1, -1, -1, -1, 0, 0, 1, -1]
df = pl.DataFrame({"list": [row1, row2]})
print(f"row1: {row1}")
print(f"row2: {row2}")
print(df)
row1: [0, 1, -1, -1, 1, 1, -1, 0]
row2: [1, -1, -1, -1, 0, 0, 1, -1]
shape: (2, 1)
┌───────────────┐
│ list │
│ --- │
│ list[i64] │
╞═══════════════╡
│ [0, 1, … 0] │
│ [1, -1, … -1] │
└───────────────┘
🤔 А знаете ли вы, что...
Python поддерживает динамическую типизацию, что облегчает разработку.
Вам наверняка понадобится метод для последовательной проверки чисел и фильтрации Dataframe оттуда.
Что-то вроде:
def check_consecutive_repeats(list):
count = 1
prev = list[0]
for i in range(1, len(list)):
if list[i] == prev:
count += 1
if count > 2:
return False
else:
count = 1
prev = list[i]
return True
Это будет перебирать список и проверять, повторяется ли данный символ более 2 раз, если да, то он вернет False.
Отсюда вы можете решить, что делать со списком, который не удался.
Другие решения вы также можете найти здесь.
Можно использовать следующее.
Выполните кодирование серий списка с помощью pl.Expr.rle. Это создает список структур. Каждая структура содержит (уникальное) значение списка и соответствующую длину выполнения.
Проверьте, не превышает ли максимальная длина пробега в списке 2.
Убедитесь, что результат имеет тип bool
, выбрав первый (и единственный) элемент в результирующем списке (используя pl.Expr.list.first).
df.with_columns(
ok=pl.col("list").list.eval(
pl.element().rle().struct.field("len").max() <= 2
).list.first()
)
shape: (2, 2)
┌──────────────────────────────┬───────┐
│ list ┆ ok │
│ --- ┆ --- │
│ list[i64] ┆ bool │
╞══════════════════════════════╪═══════╡
│ [0, 1, -1, -1, 1, 1, -1, 0] ┆ true │
│ [1, -1, -1, -1, 0, 0, 1, -1] ┆ false │
└──────────────────────────────┴───────┘
Выполнение всего этого в pl.Expr.list.eval можно избежать, развернув список. Затем необходима оконная функция ( pl.Expr.over), чтобы обеспечить вычисление максимума отдельно для каждого списка.
max_run_length = pl.col("list").explode().rle().struct.field("len").max().over(pl.int_range(pl.len()))
df.with_columns(passed=max_run_length <= 2)
Результат будет тот же.
Использование map_elements с генератором и коротким замыканием:
N = 2
df.with_columns(
valid=pl.col('list').map_elements(
lambda x: all(
any(x[i] != x[i+1:i+N+1]) for i in range(len(x)-N+1)
), return_dtype=pl.Boolean
)
)
Выход:
┌───────────────┬───────┐
│ list ┆ valid │
│ --- ┆ --- │
│ list[i64] ┆ bool │
╞═══════════════╪═══════╡
│ [0, 1, … 0] ┆ true │
│ [1, -1, … -1] ┆ false │
└───────────────┴───────┘