#python #python-3.x #date #datetime #series
#python #python-3.x #Дата #дата-время #Серии
Вопрос:
У меня есть серия списков (на самом деле np.arrays), элементами которых являются даты.
id
0a0fe3ed-d788-4427-8820-8b7b696a6033 [2019-01-30, 2019-01-31, 2019-02-01, 2019-02-0...
0a48d1e8-ead2-404a-a5a2-6b05371200b1 [2019-01-30, 2019-01-31, 2019-02-01, 2019-02-0...
0a9edba1-14e3-466a-8d0c-f8a8170cefc8 [2019-01-29, 2019-01-30, 2019-01-31, 2019-02-0...
Name: startDate, dtype: object
Для каждого элемента в серии (т. Е. Для каждого списка дат) я хочу сохранить самый длинный подсписок, в котором все даты являются последовательными. Я изо всех сил пытаюсь подойти к этому по-питоновски (простым / эффективным) способом. Единственный подход, который я могу придумать, — это использовать несколько циклов: перебирать значения серии (списки) и перебирать каждый элемент в списке. Затем я бы сохранил первую дату и количество последовательных дней и использовал временные значения для перезаписи результатов, если встречается более длинная последовательность последовательных дней. Однако это кажется крайне неэффективным. Есть ли лучший способ сделать это?
Комментарии:
1. преобразуйте даты в ординалы и получите самый длинный увеличивающийся подмассив. Я опубликовал ответ, который вы можете попробовать
Ответ №1:
Поскольку вы упомянули, что используете массивы дат numpy, имеет смысл придерживаться типов numpy вместо преобразования во встроенный тип. Я предполагаю, что ваши массивы имеют dtype ‘datetime64[D]’. В этом случае вы могли бы сделать что-то вроде
import numpy as np
date_list = np.array(['2005-02-01', '2005-02-02', '2005-02-03',
'2005-02-05', '2005-02-06', '2005-02-07', '2005-02-08', '2005-02-09',
'2005-02-11', '2005-02-12',
'2005-02-14', '2005-02-15', '2005-02-16', '2005-02-17',
'2005-02-19', '2005-02-20',
'2005-02-22', '2005-02-23', '2005-02-24',
'2005-02-25', '2005-02-26', '2005-02-27', '2005-02-28'],
dtype='datetime64[D]')
i0max, i1max = 0, 0
i0 = 0
for i1, date in enumerate(date_list):
if date - date_list[i0] != np.timedelta64(i1-i0, 'D'):
if i1 - i0 > i1max - i0max:
i0max, i1max = i0, i1
i0 = i1
print(date_list[i0max:i1max])
# output: ['2005-02-05' '2005-02-06' '2005-02-07' '2005-02-08' '2005-02-09']
Здесь i0
и i1
указывают начальные и конечные индексы текущего подмассива последовательных дат, а i0max
и i1max
начальные и конечные индексы самого длинного подмассива, найденного на данный момент. Решение использует тот факт, что разница между i
-й и нулевой записью в списке последовательных дат составляет ровно i
дней.
Ответ №2:
Вы можете преобразовать список в порядковые номера, которые увеличиваются для всех последовательных дат. Что означает next_date = previous_date 1
подробнее.
Затем найдите самый длинный последовательный подмассив.
Этот процесс займет O(n)->single loop
время, что является наиболее эффективным способом получения этого.
код
from datetime import datetime
def get_consecutive(date_list):
# convert to ordinals
v = [datetime.strptime(d, "%Y-%m-%d").toordinal() for d in date_list]
consecutive = []
run = []
dates = []
# get consecutive ordinal sequence
for i in range(1, len(v) 1):
run.append(v[i-1])
dates.append(date_list[i-1])
if i == len(v) or v[i-1] 1 != v[i]:
if len(consecutive) < len(run):
consecutive = dates
dates = []
run = []
return consecutive
ВЫВОД:
date_list = ['2019-01-29', '2019-01-30', '2019-01-31','2019-02-05']
get_consecutive(date_list )
# ordinales will be -> v = [737088, 737089, 737090, 737095]
OUTPUT:
['2019-01-29', '2019-01-30', '2019-01-31']
Теперь используйте get_consecutive
in df.column.apply(get_consecutive)
, это даст вам все увеличивающийся список дат. Или вы можете все функции для каждого списка, если вы используете какую-либо другую структуру данных.
Ответ №3:
Я собираюсь свести эту проблему к поиску последовательных дней в одном списке. Есть несколько приемов, которые делают его более питоническим, как вы просите. Следующий скрипт должен выполняться как есть. Я задокументировал, как это работает встроенным:
from datetime import timedelta, date
# example input
days = [
date(2020, 1, 1), date(2020, 1, 2), date(2020, 1, 4),
date(2020, 1, 5), date(2020, 1, 6), date(2020, 1, 8),
]
# store the longest interval and the current consecutive interval
# as we iterate through a list
longest_interval_index = current_interval_index = 0
longest_interval_length = current_interval_length = 1
# using zip here to reduce the number of indexing operations
# this will turn the days list into [(2020-01-1, 2020-01-02), (2020-01-02, 2020-01-03), ...]
# use enumerate to get the index of the current day
for i, (previous_day, current_day) in enumerate(zip(days, days[1:]), start=1):
if current_day - previous_day == timedelta(days= 1):
# we've found a consecutive day! increase the interval length
current_interval_length = 1
else:
# nope, not a consecutive day! start from this day and start
# counting from 1
current_interval_index = i
current_interval_length = 1
if current_interval_length > longest_interval_length:
# we broke the record! record it as the longest interval
longest_interval_index = current_interval_index
longest_interval_length = current_interval_length
print("Longest interval index:", longest_interval_index)
print("Longest interval: ", days[longest_interval_index:longest_interval_index longest_interval_length])
Должно быть достаточно легко превратить это в функцию многократного использования.