Суммирование значений на основе диапазонов дат в DataFrame с использованием Polars

У меня есть DataFrame (df), который содержит столбцы: ID, Initial Date, Final Date и Value, а также еще один DataFrame (dates), который содержит все дни для каждого идентификатора из df.

В кадре данных dates я хочу суммировать значения, если они существуют в диапазоне каждого ID.

Вот мой код

import polars as pl
from datetime import datetime

data = {
    "ID" : [1, 2, 3, 4, 5],
    "Initial Date" : ["2022-01-01", "2022-01-02", "2022-01-03", "2022-01-04", "2022-01-05"],
    "Final Date" : ["2022-01-03", "2022-01-06", "2022-01-07", "2022-01-09", "2022-01-07"],
    "Value" : [10, 20, 30, 40, 50]


}

df = pl.DataFrame(data)

dates = pl.datetime_range(
    start=datetime(2022,1,1),
    end=datetime(2022,1,7),
    interval = "1d",
    eager = True,
    closed = "both"
    ).to_frame("date")
shape: (5, 4)
┌─────┬──────────────┬────────────┬───────┐
│ ID  ┆ Initial Date ┆ Final Date ┆ Value │
│ --- ┆ ---          ┆ ---        ┆ ---   │
│ i64 ┆ str          ┆ str        ┆ i64   │
╞═════╪══════════════╪════════════╪═══════╡
│ 1   ┆ 2022-01-01   ┆ 2022-01-03 ┆ 10    │
│ 2   ┆ 2022-01-02   ┆ 2022-01-06 ┆ 20    │
│ 3   ┆ 2022-01-03   ┆ 2022-01-07 ┆ 30    │
│ 4   ┆ 2022-01-04   ┆ 2022-01-09 ┆ 40    │
│ 5   ┆ 2022-01-05   ┆ 2022-01-07 ┆ 50    │
└─────┴──────────────┴────────────┴───────┘
shape: (7, 1)
┌─────────────────────┐
│ date                │
│ ---                 │
│ datetime[μs]        │
╞═════════════════════╡
│ 2022-01-01 00:00:00 │
│ 2022-01-02 00:00:00 │
│ 2022-01-03 00:00:00 │
│ 2022-01-04 00:00:00 │
│ 2022-01-05 00:00:00 │
│ 2022-01-06 00:00:00 │
│ 2022-01-07 00:00:00 │
└─────────────────────┘

В этом случае 01 января 2022 г. это значение будет 10. 02 января 2022 г. оно будет 10 + 20, а 03 января 2022 г. оно будет 10 + 20 + 30 и т. д. . Другими словами, я хочу проверить, существует ли дата в диапазоне каждой строки в DataFrame (df), и если да, суммировать значения.

Я думаю, что подход к этому такой:

(
    dates.with_columns(
        pl.sum(
            pl.when(
                (df["Initial Date"] <= pl.col("date")) & (df["Final Date"] >= pl.col("date"))
            ).then(df["Value"]).otherwise(0)
        ).alias("Summed Value")
    )
    
)
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
6
0
96
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вы просто хотите узнать сумму значений на каждую дату в диапазонах в df, вам даже не нужен dates фрейм данных.

  • date_ranges() для создания столбца с диапазонами дат на основе начальной и конечной даты.
  • взорвать() для преобразования диапазонов дат в строки.
  • group_by() и agg() для суммирования значений.
(
    df
    .with_columns(date = pl.date_ranges("Initial Date", "Final Date"))
    .explode("date")
    .group_by("date", maintain_order = True)
    .agg(pl.col.Value.sum())
)

┌────────────┬───────┐
│ date       ┆ Value │
│ ---        ┆ ---   │
│ date       ┆ i64   │
╞════════════╪═══════╡
│ 2022-01-01 ┆ 10    │
│ 2022-01-02 ┆ 30    │
│ 2022-01-03 ┆ 60    │
│ 2022-01-04 ┆ 90    │
│ 2022-01-05 ┆ 140   │
│ 2022-01-06 ┆ 140   │
│ 2022-01-07 ┆ 120   │
│ 2022-01-08 ┆ 40    │
│ 2022-01-09 ┆ 40    │
└────────────┴───────┘

Если вы действительно хотите использовать dates, вы можете join() получить результат на dates dataframe:

(
    df
    .with_columns(date = pl.date_ranges("Initial Date", "Final Date"))
    .explode("date")
    .group_by("date", maintain_order = True)
    .agg(pl.col.Value.sum())
    .join(dates, on = "date", how = "semi")
)

┌────────────┬───────┐
│ date       ┆ Value │
│ ---        ┆ ---   │
│ date       ┆ i64   │
╞════════════╪═══════╡
│ 2022-01-01 ┆ 10    │
│ 2022-01-02 ┆ 30    │
│ 2022-01-03 ┆ 60    │
│ 2022-01-04 ┆ 90    │
│ 2022-01-05 ┆ 140   │
│ 2022-01-06 ┆ 140   │
│ 2022-01-07 ┆ 120   │
└────────────┴───────┘

Или просто filter() результат:

(
    df
    .with_columns(date = pl.date_ranges("Initial Date", "Final Date"))
    .explode("date")
    .group_by("date", maintain_order = True)
    .agg(pl.col.Value.sum())
    .filter(pl.col.date.is_between(datetime(2022,1,1), datetime(2022,1,7)))
)

┌────────────┬───────┐
│ date       ┆ Value │
│ ---        ┆ ---   │
│ date       ┆ i64   │
╞════════════╪═══════╡
│ 2022-01-01 ┆ 10    │
│ 2022-01-02 ┆ 30    │
│ 2022-01-03 ┆ 60    │
│ 2022-01-04 ┆ 90    │
│ 2022-01-05 ┆ 140   │
│ 2022-01-06 ┆ 140   │
│ 2022-01-07 ┆ 120   │
└────────────┴───────┘

Альтернативным решением было бы использовать соединение неравенства, но поляры в этом отношении не очень хороши (пока). Но в этом случае вы можете использовать интеграцию DuckDB с Polars.

duckdb.sql("""
    select
        d.date,
        sum(df.value) as value
    from df
        inner join dates as d on
            d.date between df."Initial Date" and df."Final Date"
    group by
        d.date
    order by
        d.date
""").pl()

┌─────────────────────┬───────────────┐
│ date                ┆ value         │
│ ---                 ┆ ---           │
│ datetime[μs]        ┆ decimal[38,0] │
╞═════════════════════╪═══════════════╡
│ 2022-01-01 00:00:00 ┆ 10            │
│ 2022-01-02 00:00:00 ┆ 30            │
│ 2022-01-03 00:00:00 ┆ 60            │
│ 2022-01-04 00:00:00 ┆ 90            │
│ 2022-01-05 00:00:00 ┆ 140           │
│ 2022-01-06 00:00:00 ┆ 140           │
│ 2022-01-07 00:00:00 ┆ 120           │
└─────────────────────┴───────────────┘

Другие вопросы по теме

При циклическом просмотре кадра данных в поисках строковых значений. Как распечатать строки без дублирования, если поисковый запрос встречается несколько раз?
Как применить результат функции Python к новому столбцу в фрейме данных Polars
Почему преобразование вложенного словаря Python в фрейм данных pandas приводит к ошибке «не имеет атрибута 'items'»?
Можем ли мы получить записи из тиббла, используя индексную матрицу?
Применить функцию к двум столбцам pandas и назначить их обратно исходному фрейму данных, что вызовет предупреждение в будущем
Странное поведение при обновлении значений с использованием iloc в фрейме данных pandas
Суммирование строк в DataFrame Pandas, где количество суммируемых строк основано на значениях столбцов в другом DataFrame
Гистограмма с накоплением с использованием matplotlib и pandas dataframe
Есть ли название для этого типа структурированных данных и как его более эффективно использовать?
Применить функцию возврата списка ко всем строкам в DataFrame pandas