Получите номера недель между двумя датами с помощью Python

Я хотел бы найти самый питонический способ вывода списка номеров недель между двумя датами.

Например:

ввод

start = datetime.date(2011, 12, 25) 
end = datetime.date(2012, 1, 21)

вывод

find_weeks(start, end)
>> [201152, 201201, 201202, 201203]

Я изо всех сил пытался использовать библиотеку datetime без особого успеха


person Ludo    schedule 22.02.2018    source источник
comment
Можете ли вы привести пример того, что вы пробовали до сих пор?   -  person asongtoruin    schedule 22.02.2018


Ответы (4)


Что-то в строках (обновление: удален менее читаемый вариант)

import datetime

def find_weeks(start,end):
    l = []
    for i in range((end-start).days + 1):
        d = (start+datetime.timedelta(days=i)).isocalendar()[:2] # e.g. (2011, 52)
        yearweek = '{}{:02}'.format(*d) # e.g. "201152"
        l.append(yearweek)
    return sorted(set(l))

start = datetime.date(2011, 12, 25) 
end = datetime.date(2012, 1, 21)

print(find_weeks(start,end)[1:]) # [1:] to exclude first week.

Возврат

['201152', '201201', '201202', '201203']

Чтобы включить первую неделю (201151), просто удалите [1:] после вызова функции

person Anton vBR    schedule 22.02.2018
comment
Интересный подход! Похоже, более Pythonic-версия, которую просил OP, +1 - person asongtoruin; 22.02.2018
comment
@asongtoruin Спасибо. У вас тоже было что-то хорошее, но я думаю, что безопаснее просто использовать все дни и set (). - person Anton vBR; 22.02.2018
comment
Спасибо обоим. Возможно, даже более питоническим было бы превратить его в понимание однострочного списка. Однако сложнее понять, что происходит. - person Ludo; 22.02.2018
comment
@Ludo Вначале у меня было понимание списка, но когда он не помещается в одну строку, я больше не думаю, что он питонический (сначала удобочитаемость!). В свете этого я выделил строку формата. - person Anton vBR; 22.02.2018
comment
@Ludo, являющийся Pythonic, не обязательно является основным и конечным. Читаемость тоже важна - я думаю, что в этом ответе хорошее сочетание лаконичности, но очень понятности. - person asongtoruin; 22.02.2018

.isocalendar() - ваш друг - он возвращает кортеж (year, week of year, day of week). Мы используем это для сброса даты начала на начало недели, а затем добавляем неделю каждый раз, пока не пройдем дату окончания:

import datetime


def find_weeks(start_date, end_date):
    subtract_days = start_date.isocalendar()[2] - 1
    current_date = start_date + datetime.timedelta(days=7-subtract_days)
    weeks_between = []
    while current_date <= end_date:
        weeks_between.append(
            '{}{:02d}'.format(*current_date.isocalendar()[:2])
        )
        current_date += datetime.timedelta(days=7)
    return weeks_between

start = datetime.date(2011, 12, 25)
end = datetime.date(2012, 1, 21)

print(find_weeks(start, end))

Это печатает

['201152', '201201', '201202', '201203']
person asongtoruin    schedule 22.02.2018

Использование панд

import pandas as pd

dates=pd.date_range(start=start,end=end,freq='W')
date_index=dates.year.astype(str)+dates.weekofyear.astype(str).str.zfill(2)
date_index.tolist()
person Rayadurai    schedule 22.02.2018
comment
Мне нравится решение формата pandas для удобочитаемости, но оно работает медленнее, чем решение datetime: datetime: 46,9 мкс ± 442 нс на цикл (среднее ± стандартное отклонение из 7 прогонов, 10000 циклов в каждом) pandas: 883 мкс ± 30,1 мкс на цикл (среднее ± стандартное отклонение 7 прогонов по 1000 циклов в каждом) - person Ludo; 22.02.2018
comment
Я изменил формат ... но это решение все еще не включает последнюю неделю. - person Anton vBR; 22.02.2018

Предлагаю вам следующее удобное для чтения решение:

import datetime

start = datetime.date(2011, 12, 25) 
end = datetime.date(2012, 1, 21)

def find_weeks(start, end):
    l = []
    while (start.isocalendar()[1] != end.isocalendar()[1]) or (start.year != end.year):
        l.append(start.isocalendar()[1] + 100*start.year)
        start += datetime.timedelta(7)
    l.append(start.isocalendar()[1] + 100*start.year)
    return (l[1:])


print(find_weeks(start, end))

>> [201252, 201201, 201202, 201203]
person Laurent H.    schedule 22.02.2018