Py) 전처리 - 임의 주수 세기

Py) 전처리 - 임의 주수 세기

한 주의 시작 기준 요일이 월요일이 아닌 경우 주수(週數)를 세는 방법을 알아본다.


개요

다음과 같은 질문이 카톡방에 유입되었다.
카카오톡 오픈 카톡방 질문

상기 사항은 프로젝트 관리, 회계 처리, 종교적 사유 등 여러가지 이유로 일요일이나 월요일이 아닌 다른 요일을 한 주의 시작 요일로 정하는 경우가 있는데 이 때 해당 기준으로 주수(또는 주차)를 계산하고자 하는 경우 별도의 코드 작성을 통하여 자동화를 해야할 수 있다.

실습

본 실습은 날짜 데이터가 정렬이 되어있고, 중간에 비어있지 않은 경우를 가정한다. 그럼 다음과 같이 날짜 데이터를 준비해보자.

1
2
df = pd.DataFrame(dict(date = pd.date_range("2023-06-01", "2023-06-30")))
df.head()
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
2
3
4
df["wday" ] = df["date"].dt.weekday
df["wday2"] = df["wday"] - 2
df["wday3"] = df["wday2"] % 7
df.head(10)
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
2
3
4
df["wday4"] = df["wday3"] + 1
df["w_sum"] = df["wday4"].cumsum()
df["w_cnt1"] = df["w_sum"] // sum(range(1, 8))
df.head(10)
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
2
df["w_cnt2"] = df["w_cnt1"] + 1
df
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
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×