У меня есть некоторые данные, которые я хотел бы разделить на четыре группы, основанные на определенных моментах времени — моменты времени, заданные определенными датами.
У меня есть такие данные (предположим, что df
уже создано):
df["date"] = pd.to_datetime(df["date"], format = "%Y-%m-%d")
df["year"] = df["date"].dt.year
df["month"] = df["date"].dt.month
df.groupby(by = "year", as_index = False).agg({"month":pd.Series.nunique})
Обратите внимание, что с этими данными 2015 и 2022 годы не являются полными годами.
Я думал, что, поскольку у меня есть данные за 84 месяца (3 + (6*12) + 9 = 84)
, я мог бы разделить данные на четыре группы, чтобы в каждой группе было всего данных примерно за 21 месяц 84 / 4 = 21
.
Чтобы сделать это, я бы сначала начал с самой ранней даты в моем наборе данных, которая равна 2015-10-02
. К этим самым ранним данным я бы добавил 21 месяц:
from dateutil.relativedelta import relativedelta
min_date = df["date"].min().date()
print([min_date, min_date + relativedelta(months = 21)]
#output
[datetime.date(2015, 10, 2), datetime.date(2017, 7, 2)]
Этот диапазон дат будет представлять собой первую ячейку, в которую попадет первая группа.
Вторая группа попадет в диапазон дат, где минимальная дата будет на один день больше, чем максимальная дата диапазона дат предыдущей группы:
"2017-07-02" + relativedelta(days = 1) = "2017-07-03"
Это гарантирует, что бины разных групп не перекрываются.
В последней группе будет немного меньше данных, так как она будет включать данные до последней даты во всем наборе данных, которая составляет 2022-09-30
В целом, корзины диапазона дат для разных групп будут выглядеть примерно так
Я знаю, что могу найти эти диапазоны дат вручную и использовать их для фильтрации набора данных для создания групп с помощью np.select
, но это не очень эффективно.
df["Group"] = np.select(
condlist = [
(df["date"] >= "2015-10-02") & (df["date"] <= "2017-07-02"),
(df["date"] >= "2017-07-03") & (df["date"] <= "2019-04-03"),
(df["date"] >= "2019-04-04") & (df["date"] <= "2021-01-04"),
(df["date"] >= "2021-01-05") & (df["date"] <= "2022-09-30")
],
choicelist = ["A", "B", "C", "D"]
)
Конечно, должен быть способ найти эти значения (так, как я хочу) без необходимости находить их вручную.
Вы можете взглянуть на pd.cut.
# toy data
df = pd.DataFrame(pd.date_range('2020-01-01', '2022-01-01'), columns = ['date'])
date
0 2020-01-01
1 2020-01-02
2 2020-01-03
3 2020-01-04
4 2020-01-05
.. ...
Вы можете создать метки и границы для бункеров.
from numpy import datetime64
bin_labels = [1, 2, 3, 4]
cut_bins = [datetime64('2019-12-31'), datetime64('2020-04-01'), datetime64('2020-12-31'), datetime64('2021-09-01'), datetime64('2022-01-01')]
И сохраните корзины в новый столбец.
df['cut'] = pd.cut(df['date'], bins = cut_bins, labels = bin_labels]
date cut
0 2020-01-01 1
1 2020-01-02 1
2 2020-01-03 1
3 2020-01-04 1
4 2020-01-05 1
.. ... ..
727 2021-12-28 4
728 2021-12-29 4
729 2021-12-30 4
730 2021-12-31 4
731 2022-01-01 4
Надеюсь, поможет.
Я нашел способ, который, как мне кажется, работает (для тех, кто может быть заинтересован в объединении значений даты и времени в будущем) - предположим, что данные такие же, как указано в описании вопроса:
from dateutil.relativedelta import relativedelta
import numpy as np
dates = []
start = df["date"].min().date()
dates.append(np.datetime64(start))
while start <= df["date"].max().date():
start = start + relativedetla(months = 21)
dates.append(np.datetime64(start))
df["Group"] = pd.cut(
df["date"], bins = dates,
labels = ["A", "B", "C", "D"],
right = False #right = False ensures no group overlap in date values
)
Спасибо за ваш ответ, функция numpy datetime64 была функцией, которая помогла мне получить ответ, который я хотел