#python #pandas #loops #timedelta
#python #pandas #циклы #timedelta
Вопрос:
У меня есть фрейм данных, который выглядит следующим образом:
Name Date
Person A 2019-06-18
Person A 2019-05-14
Person A 2019-04-03
Person B 2019-05-19
Person C 2019-05-16
Person C 2019-05-23
Person C 2019-05-15
Person D 2019-06-21
Что я хочу сделать, так это изменить даты всех, кто находится между 5/14 и 6/14, и вычесть 7 дней. Если после завершения этой операции они все еще находятся в этом диапазоне, вычтите еще 7 дней.
В конце я хочу, чтобы данные выглядели так:
Name Date
Person A 2019-06-18
Person A 2019-05-07
Person A 2019-04-03
Person B 2019-05-12
Person C 2019-05-09
Person C 2019-05-09
Person C 2019-05-08
Person D 2019-06-21
(После этого шага я сверну его так, чтобы у каждого пользователя была одна строка со всеми их датами, а затем отправлю им их информацию — но я думаю, что смогу разобраться с этим самостоятельно.)
Прямо сейчас у меня «работает» следующий код:
df = df[(df['Date'] >= '2019-05-14') amp; (df['Date'] <= '2019-06-14')]
df['Date'] = df['Date'] - pd.Timedelta(days=7)
Однако я не знаю, как это сделать, и я также не знаю, как применить это без потери моих исходных данных.
Из-за этого мой код создает этот фрейм:
Name Date
Person A 2019-05-07
Person B 2019-05-12
Person C 2019-05-16
Person C 2019-05-08
Ответ №1:
Я бы просто подсчитал, сколько раз вам нужно вычесть из каждой даты, и сделал это за один шаг
m = df.Date.between('2019-05-14', '2019-06-14')
u = df[m]
d = u.Date - pd.Timestamp('2019-05-13')
o = np.ceil(d.dt.days / 7)
df.loc[m, 'Date'] = df.loc[m, 'Date'] - (o * np.timedelta64(7, 'D'))
Name Date
0 Person A 2019-06-18
1 Person A 2019-05-07
2 Person A 2019-04-03
3 Person B 2019-05-12
4 Person C 2019-05-09
5 Person C 2019-05-09
6 Person C 2019-05-08
7 Person D 2019-06-21
Вот версия, которая не изменяет фрейм на месте:
m = df.Date.between('2019-05-14', '2019-06-14')
d = df.Date - pd.Timestamp('2019-05-13')
o = np.ceil(d.dt.days / 7)
df.assign(Date=np.where(m, df.Date - (o * np.timedelta64(7, 'D')), df.Date))
Name Date
0 Person A 2019-06-18
1 Person A 2019-05-07
2 Person A 2019-04-03
3 Person B 2019-05-12
4 Person C 2019-05-09
5 Person C 2019-05-09
6 Person C 2019-05-08
7 Person D 2019-06-21
Ответ №2:
Я предполагаю, что Date
столбец имеет datetime64
тип.
Начальным шагом является определение «граничных дат»:
start_date = pd.to_datetime('2019-05-14')
end_date = pd.to_datetime('2019-06-14')
Затем мы должны определить функцию, которая будет применяться к каждой дате:
def fn(dat):
inRng = (dat >= start_date) amp; (dat <= end_date)
dat2 = dat
if inRng:
diffWeeks = int((dat - start_date) / np.timedelta64(1, 'W')) 1
dat2 -= np.timedelta64(diffWeeks, 'W')
return dat2
И последний шаг — применить эту функцию.
Чтобы упростить сравнение исходных данных и результатов, я решил заменить
результат в новом столбце ( Dat2
):
df['Dat2'] = df.Date.apply(fn)
Когда вы печатаете свой фрейм данных, вы получите:
Name Date Dat2
0 Person A 2019-06-18 2019-06-18
1 Person A 2019-05-14 2019-05-07
2 Person A 2019-04-03 2019-04-03
3 Person B 2019-05-19 2019-05-12
4 Person C 2019-05-16 2019-05-09
5 Person C 2019-05-23 2019-05-09
6 Person C 2019-05-15 2019-05-08
7 Person D 2019-06-21 2019-06-21
Ответ №3:
Мы можем создать простой цикл с range()
и после этого использовать numpy.where
для условного изменения каждой строки, если она находится между двумя датами:
for i in range(2):
df['Date'] = np.where(df['Date'].between('20190514','20190614'),
df['Date'] - pd.Timedelta(days=7),
df['Date'])
print(df)
Name Date
0 Person A 2019-06-18
1 Person A 2019-05-07
2 Person A 2019-04-03
3 Person B 2019-05-12
4 Person C 2019-05-09
5 Person C 2019-05-09
6 Person C 2019-05-08
7 Person D 2019-06-21
Комментарии:
1. ваш метод с диапазоном 2 работает с указанными данными, но если дата, например
20190613
, не будет, вам нужен как минимум диапазон 4 или 5, я думаю, поскольку интервал между датами, который вы просматриваете, составляет один месяц2. В Op конкретно указано вычитать дважды. Вот почему я использовал диапазон. Но мы можем добавить 5 на всякий случай..
3. Я вижу, я понимаю, что OP не хочет никакой даты между этими двумя границами, и операция должна выполняться до тех пор. Просто интерпретация вопроса 🙂
Ответ №4:
Вы можете сделать это, написав функцию, а затем применив ее к столбцу даты. pd.Series.apply
Метод работает путем передачи каждого значения в предоставленную функцию. Внутри функции у вас есть простой цикл while, который продолжает вычитать 7 дней, пока вы не окажетесь в пределах желаемого диапазона дат.
from datetime import datetime as dt
from datetime import timedelta
def date_modifier(x):
d = x
while True:
if d >= dt(2019, 5, 14) and d<=dt(2019, 6, 14):
d-= timedelta(days=7)
else:
return d
df['Date-Mod'] = df['Date'].apply(date_modifier)
Выдает следующий вывод:
Name Date Date-Mod
0 PersonA 2019-06-18 2019-06-18
1 PersonA 2019-05-14 2019-05-07
2 PersonA 2019-04-03 2019-04-03
3 PersonB 2019-05-19 2019-05-12
4 PersonC 2019-05-16 2019-05-09
5 PersonC 2019-05-23 2019-05-09
6 PersonC 2019-05-15 2019-05-08
7 PersonD 2019-06-21 2019-06-21