Я надеюсь подсчитать последовательные значения в столбце, предпочтительно используя выражения Polars.
import polars
df = pl.DataFrame(
{"values": [True,True,True,False,False,True,False,False,True,True]}
)
В приведенном выше примере фрейма данных я хотел бы подсчитать количество последовательных значений True.
Ниже приведен пример вывода с использованием пакета R Data.Table.
library(data.table)
dt <- data.table(value = c(T,T,T,F,F,T,F,F,T,T))
dt[, value2 := fifelse((1:.N) == .N & value == 1, .N, NA_integer_), by = rleid(value)]
dt
Любые идеи, кто это мог бы сделать эффективно, используя Polars?
[EDIT с новым подходом]
Я получил его работу с кодом ниже, но надеюсь, что есть более эффективный способ. Кто-нибудь знает имена полей структуры/словаря по умолчанию из value_counts?
(
df.lazy()
.with_row_count()
.with_column(
pl.when(pl.col("value") == False).then(
pl.col("row_nr")
).fill_null(
strategy = "forward"
).alias("id_consecutive_Trues")
)
.with_column(
pl.col("id_consecutive_Trues").value_counts(sort = True)
)
.with_column(
(
pl.col("id_consecutive_Trues").arr.eval(
pl.element().struct().rename_fields(["value", "count"]).struct.field("count")
).arr.max()
- pl.lit(1)
).alias("max_consecutive_true_values")
)
.collect()
)
Это можно рассматривать как групповую операцию.
Обычный способ генерировать идентификаторы групп для последовательных значений — проверить != .shift()
и использовать .cumsum()
Здесь я выделил каждый шаг отдельно .with_columns()
, но его можно упростить:
(
df
.with_columns(
(pl.col("values") != pl.col("values").shift(+1))
.alias("change"))
.with_columns(
pl.col("change").cumsum()
.alias("group"))
.with_columns(
pl.count().over("group"))
.with_columns(
(pl.col("values") != pl.col("values").shift(-1))
.alias("keep"))
.with_columns(
pl.when(pl.col("values") & pl.col("keep"))
.then(pl.col("count"))
.alias("values2"))
)
shape: (10, 6)
┌────────┬────────┬───────┬───────┬───────┬─────────┐
│ values | change | group | count | keep | values2 │
│ --- | --- | --- | --- | --- | --- │
│ bool | bool | u32 | u32 | bool | u32 │
╞════════╪════════╪═══════╪═══════╪═══════╪═════════╡
│ true | true | 1 | 3 | false | null │
│ true | false | 1 | 3 | false | null │
│ true | false | 1 | 3 | true | 3 │
│ false | true | 2 | 2 | false | null │
│ false | false | 2 | 2 | true | null │
│ true | true | 3 | 1 | true | 1 │
│ false | true | 4 | 2 | false | null │
│ false | false | 4 | 2 | true | null │
│ true | true | 5 | 2 | false | null │
│ true | false | 5 | 2 | true | 2 │
└────────┴────────┴───────┴───────┴───────┴─────────┘
Один из возможных способов написать это «менее подробным» образом:
df.with_columns(
pl.when(
pl.col("values") &
pl.col("values").shift_and_fill(-1, False).is_not())
.then(
pl.count().over(
(pl.col("values") != pl.col("values").shift())
.cumsum()))
)
shape: (10, 2)
┌────────┬───────┐
│ values | count │
│ --- | --- │
│ bool | u32 │
╞════════╪═══════╡
│ true | null │
│ true | null │
│ true | 3 │
│ false | null │
│ false | null │
│ true | 1 │
│ false | null │
│ false | null │
│ true | null │
│ true | 2 │
└────────┴───────┘
Спасибо! Я думал использовать смену, но понял, что это будет слишком дорого. Любое понимание того, будет ли ваше решение более эффективным, чем подход? Я буду сравнивать оба. Еще раз спасибо за быстрый ответ!
Я бы подумал, что он более эффективен, поскольку, кажется, выполняет меньше работы, чем ваш пример, но я могу ошибаться. Если вы используете .arr.eval
, вы можете попробовать добавить parallel=True, чтобы увидеть, влияет ли это на ваш тест.
Имена полей
.value_counts
— это название того, что вы считаете, аcounts
так чтоid_consecutive_Trues
иcounts
в данном случае.