한 주의 시작 기준 요일이 월요일이 아닌 경우 주수(週數)를 세는 방법을 알아본다.
개요
다음과 같은 질문이 카톡방에 유입되었다.
상기 사항은 프로젝트 관리, 회계 처리, 종교적 사유 등 여러가지 이유로 일요일이나 월요일이 아닌 다른 요일을 한 주의 시작 요일로 정하는 경우가 있는데 이 때 해당 기준으로 주수(또는 주차)를 계산하고자 하는 경우 별도의 코드 작성을 통하여 자동화를 해야할 수 있다.
실습
본 실습은 날짜 데이터가 정렬이 되어있고, 중간에 비어있지 않은 경우를 가정한다. 그럼 다음과 같이 날짜 데이터를 준비해보자.
1 | df = pd.DataFrame(dict(date = pd.date_range("2023-06-01", "2023-06-30"))) |
date | |
---|---|
0 | 2023-06-01 |
1 | 2023-06-02 |
2 | 2023-06-03 |
3 | 2023-06-04 |
4 | 2023-06-05 |
날짜 처리를 위한 질문에 대응하기 위해 특정 일자를 대상으로 .replace()
메서드 또는 cut()
함수를 사용하거나 기준 데이터프레임을 Join 연산으로 붙여서 처리하는 등 한 달 밖에 되지 않기 때문에 번거롭더라도 시간을 조금 투자하면 충분히 처리할 수 있는 문제이다. 하지만 이런 문제에 대응할 때는 각 조건마다 일일이 코드를 작성하기가 어려울 정도로 데이터가 많은 경우를 가정해야 한다. 그래서 요일과 주수를 직접 계산하는 방식으로 접근하고자 한다.
이제 저 날짜에서 요일을 뽑기 위해 .dt.weekday
를 사용하면 월요일은 0, 일요일은 6이 된다. 문제를 해결하기 위해서는 수요일이 0으로 되어야 하기 때문에 추가 조작이 필요하다. 여기서 .shift()
메서드를 떠올릴 수 있으나 해당 방법을 사용하는 경우 중간에 날짜가 비어있는 경우 대응하기 어렵다. 그래서 특정 요일을 기준으로 하기 위한 offset을 주고(특정 숫자를 더함) 7로 나눈 나머지를 취하는 방식을 택했다.
1 | df["wday" ] = df["date"].dt.weekday |
date | wday | wday2 | wday3 | |
---|---|---|---|---|
0 | 2023-06-01 | 3 | 1 | 1 |
1 | 2023-06-02 | 4 | 2 | 2 |
2 | 2023-06-03 | 5 | 3 | 3 |
3 | 2023-06-04 | 6 | 4 | 4 |
4 | 2023-06-05 | 0 | -2 | 5 |
5 | 2023-06-06 | 1 | -1 | 6 |
6 | 2023-06-07 | 2 | 0 | 0 |
7 | 2023-06-08 | 3 | 1 | 1 |
8 | 2023-06-09 | 4 | 2 | 2 |
9 | 2023-06-10 | 5 | 3 | 3 |
이제 주차 계산을 위하여 숫자로 표기된 요일의 누적합을 구하고 1주일치의 누적합으로 나눈 몫을 구할 예정인데 여기서 한 주의 시작일이 0으로 표기되어있는 것이 문제이다. 0은 덧셈의 항등원으로 누적합을 실시할 경우 변화가 없으니 일괄로 1을 더해준 다음 나눗셈을 실시한다.
1 | df["wday4"] = df["wday3"] + 1 |
date | wday | wday2 | wday3 | wday4 | w_sum | w_cnt1 | |
---|---|---|---|---|---|---|---|
0 | 2023-06-01 | 3 | 1 | 1 | 2 | 2 | 0 |
1 | 2023-06-02 | 4 | 2 | 2 | 3 | 5 | 0 |
2 | 2023-06-03 | 5 | 3 | 3 | 4 | 9 | 0 |
3 | 2023-06-04 | 6 | 4 | 4 | 5 | 14 | 0 |
4 | 2023-06-05 | 0 | -2 | 5 | 6 | 20 | 0 |
5 | 2023-06-06 | 1 | -1 | 6 | 7 | 27 | 0 |
6 | 2023-06-07 | 2 | 0 | 0 | 1 | 28 | 1 |
7 | 2023-06-08 | 3 | 1 | 1 | 2 | 30 | 1 |
8 | 2023-06-09 | 4 | 2 | 2 | 3 | 33 | 1 |
9 | 2023-06-10 | 5 | 3 | 3 | 4 | 37 | 1 |
그리고 주수의 셈은 보통 1부터 시작하기 때문에 1을 더해주고 최종 결과를 확인해보면 다음과 같다.
1 | df["w_cnt2"] = df["w_cnt1"] + 1 |
date | wday | wday2 | wday3 | wday4 | w_sum | w_cnt1 | w_cnt2 | |
---|---|---|---|---|---|---|---|---|
0 | 2023-06-01 | 3 | 1 | 1 | 2 | 2 | 0 | 1 |
1 | 2023-06-02 | 4 | 2 | 2 | 3 | 5 | 0 | 1 |
2 | 2023-06-03 | 5 | 3 | 3 | 4 | 9 | 0 | 1 |
3 | 2023-06-04 | 6 | 4 | 4 | 5 | 14 | 0 | 1 |
4 | 2023-06-05 | 0 | -2 | 5 | 6 | 20 | 0 | 1 |
5 | 2023-06-06 | 1 | -1 | 6 | 7 | 27 | 0 | 1 |
6 | 2023-06-07 | 2 | 0 | 0 | 1 | 28 | 1 | 2 |
7 | 2023-06-08 | 3 | 1 | 1 | 2 | 30 | 1 | 2 |
8 | 2023-06-09 | 4 | 2 | 2 | 3 | 33 | 1 | 2 |
9 | 2023-06-10 | 5 | 3 | 3 | 4 | 37 | 1 | 2 |
10 | 2023-06-11 | 6 | 4 | 4 | 5 | 42 | 1 | 2 |
11 | 2023-06-12 | 0 | -2 | 5 | 6 | 48 | 1 | 2 |
12 | 2023-06-13 | 1 | -1 | 6 | 7 | 55 | 1 | 2 |
13 | 2023-06-14 | 2 | 0 | 0 | 1 | 56 | 2 | 3 |
14 | 2023-06-15 | 3 | 1 | 1 | 2 | 58 | 2 | 3 |
15 | 2023-06-16 | 4 | 2 | 2 | 3 | 61 | 2 | 3 |
16 | 2023-06-17 | 5 | 3 | 3 | 4 | 65 | 2 | 3 |
17 | 2023-06-18 | 6 | 4 | 4 | 5 | 70 | 2 | 3 |
18 | 2023-06-19 | 0 | -2 | 5 | 6 | 76 | 2 | 3 |
19 | 2023-06-20 | 1 | -1 | 6 | 7 | 83 | 2 | 3 |
20 | 2023-06-21 | 2 | 0 | 0 | 1 | 84 | 3 | 4 |
21 | 2023-06-22 | 3 | 1 | 1 | 2 | 86 | 3 | 4 |
22 | 2023-06-23 | 4 | 2 | 2 | 3 | 89 | 3 | 4 |
23 | 2023-06-24 | 5 | 3 | 3 | 4 | 93 | 3 | 4 |
24 | 2023-06-25 | 6 | 4 | 4 | 5 | 98 | 3 | 4 |
25 | 2023-06-26 | 0 | -2 | 5 | 6 | 104 | 3 | 4 |
26 | 2023-06-27 | 1 | -1 | 6 | 7 | 111 | 3 | 4 |
27 | 2023-06-28 | 2 | 0 | 0 | 1 | 112 | 4 | 5 |
28 | 2023-06-29 | 3 | 1 | 1 | 2 | 114 | 4 | 5 |
29 | 2023-06-30 | 4 | 2 | 2 | 3 | 117 | 4 | 5 |