Как найти количество дней до нужной даты?

Дан df:

a = pd.DataFrame([{'id': 1, 'b': 1, 'd': 18.12.12},
                  {'id': 1, 'b': 2, 'd': 15.12.12},
                  {'id': 1, 'b': 2, 'd': 14.12.12},
                  {'id': 1, 'b': 2, 'd': 12.12.12},
                  {'id': 1, 'b': 4, 'd': 12.12.12},

                  {'id': 2, 'b': 1, 'd': 19.12.12},
                  {'id': 2, 'b': 2, 'd': 17.12.12},
                  {'id': 2, 'b': 2, 'd': 15.12.12},
                  {'id': 2, 'b': 2, 'd': 12.12.12},
                  {'id': 2, 'b': 3, 'd': 12.12.12},

                  {'id': 3, 'b': 1, 'd': 17.12.12},
                  {'id': 3, 'b': 2, 'd': 16.12.12},
                  {'id': 3, 'b': 2, 'd': 15.12.12},
                  {'id': 3, 'b': 2, 'd': 14.12.12},
                  {'id': 3, 'b': 6, 'd': 12.12.12}])

Как можно сгруппировать строки по значению id и по значению b, например равного 2, и по значениям в столбце d определить наименьшее прошедшее количество дней до даты в столбце d, того же id, но b == 1, занести в столбец k?

То есть должно получиться:

        pd.DataFrame([{'id': 1, 'b': 1, 'd': 18.12.12, 'k': 3},
                      {'id': 1, 'b': 2, 'd': 15.12.12, 'k': 3},
                      {'id': 1, 'b': 2, 'd': 14.12.12, 'k': 3},
                      {'id': 1, 'b': 2, 'd': 12.12.12, 'k': 3},
                      {'id': 1, 'b': 4, 'd': 12.12.12, 'k': 3},

                      {'id': 2, 'b': 1, 'd': 19.12.12, 'k': 2},
                      {'id': 2, 'b': 2, 'd': 17.12.12, 'k': 2},
                      {'id': 2, 'b': 2, 'd': 15.12.12, 'k': 2},
                      {'id': 2, 'b': 2, 'd': 12.12.12, 'k': 2},
                      {'id': 2, 'b': 3, 'd': 12.12.12, 'k': 2},

                      {'id': 3, 'b': 1, 'd': 17.12.12, 'k': 1},
                      {'id': 3, 'b': 2, 'd': 16.12.12, 'k': 1},
                      {'id': 3, 'b': 2, 'd': 15.12.12, 'k': 1},
                      {'id': 3, 'b': 2, 'd': 14.12.12, 'k': 1},
                      {'id': 3, 'b': 6, 'd': 12.12.12, 'k': 1}])

Предполагаю, что логика решения этой проблемы такая:

  1. мы создаем столбец g по сгруппированным значениям по id и b == 1, добавив в него значения из d

  2. далее группируем также по id, но уже по b == 2

  3. далее из значения в столбце g вычитаем выше сгруппированные значение в d (дата в столбце g всегда будет позднее)

  4. далее выбираем из них минимальное и заносим в столбец k

Возможно есть и более быстрый путь.


Ответы (3 шт):

Автор решения: Lisp

на самом деле всё очень просто, но этот способ легче чем твой! #для начала импортируем модуль datetime

from datetime import datetime, date, time

#далее пишем код!
now = datetime.today() #Это будет наша дата, вводить сюда ничего не нужно
NY = datetime(2022,1,2) #Эта же строка отвечает за дату до которой нам нужно считать, 
в примере новый год
d = NY-now #данная строка считает время до даты которую мы указали в строчке NY
#теперь принтуем!
print("До нового года:", d.days ,"день|дней!")
#на выходе получаем ответ:
До нового года: 294 день|дней!
→ Ссылка
Автор решения: strawdog

Мне не очень нравится это решения ввиду отсутствия изящества, но, тем не менее:

df["k"] =df[df["b"]==1].groupby("id")["d"].transform("first")
df["k"] = df["k"].fillna(method="ffill").dt.day - df["d"].dt.day
df["k"] = df.groupby("id")["k"].transform(lambda x: x[x.ne(0)].min())

df:

    id  b          d  k
0    1  1 2012-12-18  3
1    1  2 2012-12-15  3
2    1  2 2012-12-14  3
3    1  2 2012-12-12  3
4    1  4 2012-12-12  3
5    2  1 2012-12-19  2
6    2  2 2012-12-17  2
7    2  2 2012-12-15  2
8    2  2 2012-12-12  2
9    2  3 2012-12-12  2
10   3  1 2012-12-17  1
11   3  2 2012-12-16  1
12   3  2 2012-12-15  1
13   3  2 2012-12-14  1
14   3  6 2012-12-12  1
→ Ссылка
Автор решения: MaxU
days = ((a.query("b == 1").groupby("id")["d"].min()
         - 
         a.query("b == 2").groupby("id")["d"].max()
        ).dt.days.reset_index(name="k"))
res = a.merge(days, how="left")

результат:

In [67]: res
Out[67]:
    id  b          d  k
0    1  1 2012-12-18  3
1    1  2 2012-12-15  3
2    1  2 2012-12-14  3
3    1  2 2012-12-12  3
4    1  4 2012-12-12  3
5    2  1 2012-12-19  2
6    2  2 2012-12-17  2
7    2  2 2012-12-15  2
8    2  2 2012-12-12  2
9    2  3 2012-12-12  2
10   3  1 2012-12-17  1
11   3  2 2012-12-16  1
12   3  2 2012-12-15  1
13   3  2 2012-12-14  1
14   3  6 2012-12-12  1
→ Ссылка