Объединить один столбец df в список структур первой/последней пары по значению идентификатора. ⇐ Python
-
Anonymous
Объединить один столбец df в список структур первой/последней пары по значению идентификатора.
У меня есть временной ряд, содержащий значения идентификаторов. Серия состоит из фрагментов, в которых значение серии остается постоянным. Я хочу эффективно извлечь соответствующий список временных интервалов, в течение которых временной ряд не меняется для каждого значения.
Мой временной ряд представлен как polars.DataFrame с двумя столбцами: timestamp и value, см. пример ниже
импортировать поляры как pl даты = pl.date_range( start=datetime.date.fromisoformat("2011-01-01"), end=datetime.date.fromisoformat("2011-01-10"), интервал="1д", нетерпеливый = Истина ) кадр = pl.DataFrame({ «дата»: даты, "значение": ["A", "B", "A", "B", "B", "A", "A", "A", "B", "B"] }) В результате мне нужен кадр, в котором для каждого значения я получаю список всех интервалов/кортежей[дата, дата], в течение которых указанный выше ряд был постоянным, например. следующий кадр или аналогичный
>>> форма: (2, 2) ┌───────┬───────────────────────────────────┐ │ значение ┆ интервалы │ │ --- ┆ --- │ │ строка ┆ список[структура[2]] │ ╞═══════╪═══════════════════════════════ ════╡ │ А ┆ [{2011-01-01,2011-01-01}, {2011-… │ │ B ┆ [{2011-01-02,2011-01-02}, {2011-… │ └────────┴───────────────────────────────────┘ Мое текущее решение состоит из следующих шагов:
i) Укажите (включительно) начало и конец фрагмента в виде двух дополнительных логических столбцов
def is_start(expr: pl.Expr) -> pl.Expr: return (expr.shift(1) != expr).fill_null(True).alias("is_start") def is_end(выражение: pl.Expr) -> pl.Expr: return (expr.shift(-1) != expr).fill_null(True).alias("is_end") x =frame.with_columns(pl.col("value").pipe(is_start), pl.col("value").pipe(is_end)) >>>форма: (10, 4) ┌────────────┬───────┬───────────┬────────┐ │ дата ┆ значение ┆ is_start ┆ is_end │ │ --- ┆ --- ┆ --- ┆ --- │ │ дата ┆ строка ┆ bool ┆ bool │ ╞════════════╪═══════╪══════════╪═══════ ═╡ │ 01.01.2011 ┆ A ┆ правда ┆ правда │ │ 2011-01-02 ┆ B ┆ правда ┆ правда │ │ 03.01.2011 ┆ A ┆ правда ┆ правда │ │ 04.01.2011 ┆ B ┆ правда ┆ ложь │ │ … ┆ … ┆ … ┆ … │ │ 07.01.2011 ┆ A ┆ ложь ┆ ложь │ │ 08.01.2011 ┆ A ┆ ложь ┆ правда │ │ 09.01.2011 ┆ B ┆ правда ┆ ложь │ │ 10.01.2011 ┆ B ┆ ложь ┆ правда │ └────────────┴───────┴──────────┴────────┘ ii) Группировать по значениям и объединять даты начала и окончания в списки
y = x.groupby("value").agg( pl.col("дата").filter(pl.col("is_start")).alias("start_date"), pl.col("дата").filter(pl.col("is_end")).alias("end_date") ) >>>форма: (2, 3) ┌───────┬──────────────────────────────────┬───── ────────────────────────────────┐ │ значение ┆ начальная_дата ┆ конечная_дата │ │ --- ┆ --- ┆ --- │ │ строка ┆ список[дата] ┆ список[дата] │ ╞═══════╪═══════════════════════════════ ════╪═════ ══════════════════════════════╡ │ A ┆ [01.01.2011, 03.01.2011, 01.01.2011… ┆ [01.01.2011, 03.01.2011, 01.01.2011… │ │ B ┆ [2011-01-02, 2011-01-04, 2011-01… ┆ [2011-01-02, 2011-01-05, 2011-01… │ └───────┴──────────────────────────────────┴───── ────────────────────────────────┘ iii) Разбивайте списки на столбцы
z = y.explode("start_date", "end_date") >>> форма: (6, 3) ┌────────┬────────────┬────────────┐ │ значение ┆ начальная_дата ┆ конечная_дата │ │ --- ┆ --- ┆ --- │ │ ул ┆ дата ┆ дата │ ╞═══════╪════════════╪════════════╡ │ А ┆ 01.01.2011 ┆ 01.01.2011 │ │ А ┆ 03.01.2011 ┆ 03.01.2011 │ │ А ┆ 06.01.2011 ┆ 08.01.2011 │ │ Б ┆ 02.01.2011 ┆ 02.01.2011 │ │ Б ┆ 04.01.2011 ┆ 05.01.2011 │ │ Б ┆ 09.01.2011 ┆ 10.01.2011 │ └────────┴────────────┴────────────┘ iv) Группировать по «значению» (снова) и объединять в серии интервалов (структуры)
u = z.groupby("value").agg( pl.struct(pl.col("start_date"), pl.col("end_date")).alias("интервалы") ) >>> форма: (2, 2) ┌───────┬───────────────────────────────────┐ │ значение ┆ интервалы │ │ --- ┆ --- │ │ строка ┆ список[структура[2]] │ ╞═══════╪═══════════════════════════════ ════╡ │ А ┆ [{2011-01-01,2011-01-01}, {2011-… │ │ B ┆ [{2011-01-02,2011-01-02}, {2011-… │ └────────┴───────────────────────────────────┘ Мне интересно, существует ли более краткий (и эффективный) способ выразить это преобразование с помощью поляров. Особенно тот факт, что мне приходится дважды groupby "value" и что y уже содержит все даты начала и окончания, хотя и не в виде кортежей "интервалов", заставляет меня задуматься, а если это можно улучшить.
В конце концов мне придется применить это преобразование не для одного, а для многих (длинных) временных рядов, где я априори знаю, что количество «точек изменения» в каждом ряду довольно мало.
р>
Буду рад любым предложениям.
У меня есть временной ряд, содержащий значения идентификаторов. Серия состоит из фрагментов, в которых значение серии остается постоянным. Я хочу эффективно извлечь соответствующий список временных интервалов, в течение которых временной ряд не меняется для каждого значения.
Мой временной ряд представлен как polars.DataFrame с двумя столбцами: timestamp и value, см. пример ниже
импортировать поляры как pl даты = pl.date_range( start=datetime.date.fromisoformat("2011-01-01"), end=datetime.date.fromisoformat("2011-01-10"), интервал="1д", нетерпеливый = Истина ) кадр = pl.DataFrame({ «дата»: даты, "значение": ["A", "B", "A", "B", "B", "A", "A", "A", "B", "B"] }) В результате мне нужен кадр, в котором для каждого значения я получаю список всех интервалов/кортежей[дата, дата], в течение которых указанный выше ряд был постоянным, например. следующий кадр или аналогичный
>>> форма: (2, 2) ┌───────┬───────────────────────────────────┐ │ значение ┆ интервалы │ │ --- ┆ --- │ │ строка ┆ список[структура[2]] │ ╞═══════╪═══════════════════════════════ ════╡ │ А ┆ [{2011-01-01,2011-01-01}, {2011-… │ │ B ┆ [{2011-01-02,2011-01-02}, {2011-… │ └────────┴───────────────────────────────────┘ Мое текущее решение состоит из следующих шагов:
i) Укажите (включительно) начало и конец фрагмента в виде двух дополнительных логических столбцов
def is_start(expr: pl.Expr) -> pl.Expr: return (expr.shift(1) != expr).fill_null(True).alias("is_start") def is_end(выражение: pl.Expr) -> pl.Expr: return (expr.shift(-1) != expr).fill_null(True).alias("is_end") x =frame.with_columns(pl.col("value").pipe(is_start), pl.col("value").pipe(is_end)) >>>форма: (10, 4) ┌────────────┬───────┬───────────┬────────┐ │ дата ┆ значение ┆ is_start ┆ is_end │ │ --- ┆ --- ┆ --- ┆ --- │ │ дата ┆ строка ┆ bool ┆ bool │ ╞════════════╪═══════╪══════════╪═══════ ═╡ │ 01.01.2011 ┆ A ┆ правда ┆ правда │ │ 2011-01-02 ┆ B ┆ правда ┆ правда │ │ 03.01.2011 ┆ A ┆ правда ┆ правда │ │ 04.01.2011 ┆ B ┆ правда ┆ ложь │ │ … ┆ … ┆ … ┆ … │ │ 07.01.2011 ┆ A ┆ ложь ┆ ложь │ │ 08.01.2011 ┆ A ┆ ложь ┆ правда │ │ 09.01.2011 ┆ B ┆ правда ┆ ложь │ │ 10.01.2011 ┆ B ┆ ложь ┆ правда │ └────────────┴───────┴──────────┴────────┘ ii) Группировать по значениям и объединять даты начала и окончания в списки
y = x.groupby("value").agg( pl.col("дата").filter(pl.col("is_start")).alias("start_date"), pl.col("дата").filter(pl.col("is_end")).alias("end_date") ) >>>форма: (2, 3) ┌───────┬──────────────────────────────────┬───── ────────────────────────────────┐ │ значение ┆ начальная_дата ┆ конечная_дата │ │ --- ┆ --- ┆ --- │ │ строка ┆ список[дата] ┆ список[дата] │ ╞═══════╪═══════════════════════════════ ════╪═════ ══════════════════════════════╡ │ A ┆ [01.01.2011, 03.01.2011, 01.01.2011… ┆ [01.01.2011, 03.01.2011, 01.01.2011… │ │ B ┆ [2011-01-02, 2011-01-04, 2011-01… ┆ [2011-01-02, 2011-01-05, 2011-01… │ └───────┴──────────────────────────────────┴───── ────────────────────────────────┘ iii) Разбивайте списки на столбцы
z = y.explode("start_date", "end_date") >>> форма: (6, 3) ┌────────┬────────────┬────────────┐ │ значение ┆ начальная_дата ┆ конечная_дата │ │ --- ┆ --- ┆ --- │ │ ул ┆ дата ┆ дата │ ╞═══════╪════════════╪════════════╡ │ А ┆ 01.01.2011 ┆ 01.01.2011 │ │ А ┆ 03.01.2011 ┆ 03.01.2011 │ │ А ┆ 06.01.2011 ┆ 08.01.2011 │ │ Б ┆ 02.01.2011 ┆ 02.01.2011 │ │ Б ┆ 04.01.2011 ┆ 05.01.2011 │ │ Б ┆ 09.01.2011 ┆ 10.01.2011 │ └────────┴────────────┴────────────┘ iv) Группировать по «значению» (снова) и объединять в серии интервалов (структуры)
u = z.groupby("value").agg( pl.struct(pl.col("start_date"), pl.col("end_date")).alias("интервалы") ) >>> форма: (2, 2) ┌───────┬───────────────────────────────────┐ │ значение ┆ интервалы │ │ --- ┆ --- │ │ строка ┆ список[структура[2]] │ ╞═══════╪═══════════════════════════════ ════╡ │ А ┆ [{2011-01-01,2011-01-01}, {2011-… │ │ B ┆ [{2011-01-02,2011-01-02}, {2011-… │ └────────┴───────────────────────────────────┘ Мне интересно, существует ли более краткий (и эффективный) способ выразить это преобразование с помощью поляров. Особенно тот факт, что мне приходится дважды groupby "value" и что y уже содержит все даты начала и окончания, хотя и не в виде кортежей "интервалов", заставляет меня задуматься, а если это можно улучшить.
В конце концов мне придется применить это преобразование не для одного, а для многих (длинных) временных рядов, где я априори знаю, что количество «точек изменения» в каждом ряду довольно мало.
р>
Буду рад любым предложениям.