簡體   English   中英

獲取周開始日不同於周一的周數 - Python

[英]Get week number with week start day different than monday - Python

我有一個帶有日期列的數據集。 我想獲取與每個日期相關的周數。 我知道我可以使用:

x['date'].isocalendar()[1]

但它給了我開始日 = 星期一的周數。 雖然我需要一周從星期五開始。

你建議我怎么做?

tl;博士

ISO 標准”和“您想要的”部分是為了闡明您的需求。

您可以將代碼復制粘貼到“解決方案”部分,看看結果是否是您想要的。


ISO標准

定義

  • 周從星期一開始。
  • 每周的一年是星期四所在的公歷年。

Python 標准庫datetime結果

>>> datetime(2020, 1, 1).isocalendar()
(2020, 1, 3)  # The 3rd day of the 1st week in 2020
>>> datetime(2019, 12, 31).isocalendar()
(2020, 1, 2)  # The 2nd day of the 1st week in 2020
>>> datetime(2019, 1, 1).isocalendar()
(2019, 1, 2)
>>> datetime(2017, 1, 1).isocalendar()
(2016, 52, 7)
>>> datetime(2016, 12, 26).isocalendar()
(2016, 52, 1)
>>> datetime(2015, 12, 31).isocalendar()
(2015, 53, 4)
>>> datetime(2016, 1, 1).isocalendar()
(2015, 53, 5)

日歷草圖

#                 Mo Tu Wd Th Fr Sa Sn
# [2019-52w] DEC/ 23 24 25 26 27 28 29 /DEC
# [2020-1w]  DEC/ 30 31  1  2  3  4  5 /JAN

# [2019-1w]  DEC/ 31  1  2  3  4  5  6 /JAN

# [2016-52w] DEC/ 26 27 28 29 30 31  1 /JAN

# [2015-53w] DEC/ 28 29 30 31  1  2  3 /JAN
# [2016-1w]  JAN/  4  5  6  7  8  9 10 /JAN 

你想要什么

定義

  • 周從星期五開始。
  • 每周的一年是星期一所在的公歷年。

日歷草圖

#                 Fr Sa Sn. Mo Tu Wd Th 
# [2019-51w] DEC/ 20 21 22. 23 24 25 26  /DEC
# [2019-52w] DEC/ 27 28 29. 30 31  1  2  /JAN
# [2020-1w]  JAN/  3  4  5.  6  7  8  9  /JAN

# [2018-53w] DEC/ 28 29 30. 31  1  2  3  /JAN
# [2019-1w]  JAN/  4  5  6.  7  8  9 10  /JAN

# [2016-52w] DEC/ 23 24 25. 26 27 28 29  /DEC
# [2017-1w]  DEC/ 30 31  1.  2  3  4  5  /JAN

# [2015-52w] DEC/ 25 26 27. 28 29 30 31  /DEC
# [2016-1w]  JAN/  1  2  3.  4  5  6  7  /JAN 

解決方案

from datetime import datetime, timedelta
from enum import IntEnum

WEEKDAY = IntEnum('WEEKDAY', 'MON TUE WED THU FRI SAT SUN', start=1)

class CustomizedCalendar:

    def __init__(self, start_weekday, indicator_weekday=None):
        self.start_weekday = start_weekday
        self.indicator_delta = 3 if not (indicator_weekday) else (indicator_weekday - start_weekday) % 7

    def get_week_start(self, date):
        delta = date.isoweekday() - self.start_weekday
        return date - timedelta(days=delta % 7)

    def get_week_indicator(self, date):
        week_start = self.get_week_start(date)
        return week_start + timedelta(days=self.indicator_delta)

    def get_first_week(self, year):
        indicator_date = self.get_week_indicator(datetime(year, 1, 1))
        if indicator_date.year == year:  # The date "year.1.1" is on 1st week.
            return self.get_week_start(datetime(year, 1, 1))
        else:  # The date "year.1.1" is on the last week of "year-1".
            return self.get_week_start(datetime(year, 1, 8))
    
    def calculate(self, date):
        year = self.get_week_indicator(date).year
        first_date_of_first_week = self.get_first_week(year)
        diff_days = (date - first_date_of_first_week).days
        return year, (diff_days // 7 + 1), (diff_days % 7 + 1)

if __name__ == '__main__':
    # Use like this:
    my_calendar = CustomizedCalendar(start_weekday=WEEKDAY.FRI, indicator_weekday=WEEKDAY.MON)
    print(my_calendar.calculate(datetime(2020, 1, 2)))

去測試

我們可以簡單地使用原始 ISO 設置初始化CustomizedCalendar ,並驗證結果是否與原始isocalendar()的結果相同。

my_calendar = CustomizedCalendar(start_weekday=WEEKDAY.MON)
s = datetime(2019, 12, 19)
for delta in range(20):
    print my_calendar.calculate(s) == s.isocalendar()
    s += timedelta(days=1)

這是最小的邏輯:

你只需要在星期一增加 3 天就可以到星期四。 只需將天數添加到星期一並致電 ISO Weeknumber。 你會得到轉移后的周數。

from datetime import datetime, timedelta

x = datetime(2020, 1, 2) # this is Thursday and week 1 in ISO calendar; should be 1 in custom calendar w/ week starting Thu
y = datetime(2020, 1, 3) # this is Friday and week 1 in ISO calendar; should be 2 in custom calendar
print(x)
print(y)

def weeknum(dt):
    return dt.isocalendar()[1]

def myweeknum(dt):
    offsetdt = dt + timedelta(days=3);  # you add 3 days to Mon to get to Thu 
    return weeknum(offsetdt);

print(weeknum(x));
print(myweeknum(x));

print(weeknum(y));
print(myweeknum(y));

輸出:

2020-01-02 00:00:00
2020-01-03 00:00:00
1
1
1
2

從下面復制函數,然后weeknumber(2020, 8, 21, 'Tuesday')將為您提供 2020 年 8 月 21 日所在的星期數,星期數從Tuesday開始,2020 年第一個Tuesday之前的天數將有星期數字0

# necessary imports
from datetime import date, timedelta
import time

您可以將此答案(依賴於此答案)用於問題如何使用 Python 選擇一年中的所有星期日? 獲取指定年份中的所有星期一、星期二、星期三、...星期日。

一個輔助函數:

def weeknum(dayname):
    if dayname == 'Monday':   return 0
    if dayname == 'Tuesday':  return 1
    if dayname == 'Wednesday':return 2
    if dayname == 'Thursday': return 3
    if dayname == 'Friday':   return 4
    if dayname == 'Saturday': return 5
    if dayname == 'Sunday':   return 6

或者,(使用這個):

def weeknum(dayname):
    return time.strptime('Sunday', "%A").tm_wday

我們將要使用的主要功能:

def alldays(year, whichDayYouWant):
    d = date(year, 1, 1)
    d += timedelta(days = (weeknum(whichDayYouWant) - d.weekday()) % 7)
    while d.year == year:
        yield d
        d += timedelta(days = 7)

現在要獲取給定日期的周數,請執行以下操作:

def weeknumber(year, month, day, weekstartsonthisday):
    specificdays = [d for d in alldays(year, weekstartsonthisday)]
    return len([specificday for specificday in specificdays if specificday <= datetime.date(year,month,day)])

specificdays是一年中datetime.date對象的列表,與weekstartsonthisday是同一工作日。 例如, [d for d in alldays(2020,'Tuesday')]開頭是這樣的:

[datetime.date(2020, 1, 7),
 datetime.date(2020, 1, 14),
 datetime.date(2020, 1, 21),
 datetime.date(2020, 1, 28),
 datetime.date(2020, 2, 4),
...

提醒一下,2020 年是這樣開始的:

在此處輸入圖片說明

該列表[specificday for specificday in specificdays if specificday <= datetime.date(year,month,day)]將包含列表specificday S(即星期一,星期二,...,無論你指定),即發生在某一年在你約會之前。 這個len()會給我們一周的數字。 一年中第一個specificday日之前的天數將在0周內。

幾個例子:

  • weeknumber(2020,1,1,'Tuesday')返回: 0

  • weeknumber(2020,1,6,'Tuesday')返回: 0

  • weeknumber(2020,1,7,'Tuesday')返回: 1

  • weeknumber(2020,12,31,'Tuesday')返回: 52

  • weeknumber(2020,1,1,'Wednesday')返回: 1

看起來不錯。

如果您希望每個日期的年份恰好是日期本身的年份,那么還有另一種形式的周定義,如下所示。

如果一周從星期一開始

#                 Mo Tu Wd Th Fr Sa Sn
# [2019-52w] DEC/ 23 24 25 26 27 28 29
# [2019-53w] DEC/ 30 31
# [2020-1w]  JAN/        1  2  3  4  5
# [2020-2w]  JAN/  6  7  8  9 10 11 12

# [2018-53w] DEC/ 31  
# [2019-1w]  JAN/     1  2  3  4  5  6

如果一周從周五開始

#                 Fr Sa Sn. Mo Tu Wd Th 
# [2019-53w] DEC/ 27 28 29. 30 31
# [2020-1w]  JAN/                  1  2
# [2020-2w]  JAN/  3  4  5.  6  7  8  9

# [2018-53w] DEC/ 28 29 30. 31  
# [2019-1w]  JAN/               1  2  3
# [2019-2w]  JAN/  4  5  6.  7  8  9 10

解決方案

from datetime import datetime, timedelta
from enum import IntEnum

WEEKDAY = IntEnum('WEEKDAY', 'MON TUE WED THU FRI SAT SUN', start=1)

def get_week_number(start, date):
    year_start = datetime(date.year, 1, 1) - timedelta(days=(datetime(date.year, 1, 1).isoweekday() - start) % 7)
    return date.year, (date-year_start).days // 7 + 1, (date-year_start).days % 7 + 1

if __name__ == '__main__':
    # usage:
    print(get_week_number(WEEKDAY.FRI, datetime(2018, 12, 19)))

聚會有點晚了,這是我們解決的方法

   def get_week(date, weekday_start):
    # Number of days since day 1 of the year. First day is day 0.
    number_of_days = date.timetuple().tm_yday - 1
    # Get the first day of the week in int. Monday is 0, Tuesday is 1, etc.
    first_day_of_week = time.strptime(weekday_start if weekday_start else 'MONDAY', "%A").tm_wday
    # Get the first day of the year. isoweekday considers Monday as 1 and Sunday as 2, thus why -1.
    first_day_of_year = (datetime(date.year, 1, 1).isoweekday()) - 1
    # Get the week number
    return (number_of_days + abs(first_day_of_week - first_day_of_year)) // 7 + 1

我們將一周的第一部分視為第 1 周。這意味着如果一年從星期二開始,星期一設置為 weekday_start,那么第一周將只有 6 天。

weekday_start 是一個將 MONDAY、TUESDAY、WEDNESDAY 等作為輸入的字符串。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM