Py) 기초 - Pandas(시간 데이터)

Py) 기초 - Pandas(시간 데이터)

파이썬 기반 데이터분석을 위하여 Pandas 라이브러리 기반 시간 데이터를 다루는 방법을 알아보자.


※ 본 내용은 Load 함수 블럭에서 bike.csv 파일을 불러온 후에 진행한다.
bike.csv 다운받기 [클릭]

개요

일별 방문자 수, 일별 유동인구, 연간 관광객, 시간별 기온 등 세상에는 참 많은 데이터가 시간과 함께 기록되어있다. 데이터를 시간 기준으로 필터링하거나 집계하기 위해서는 단순 택스트로 인식되는 시간 데이터를 Pandas 가 지원하는 날짜 데이터 형식으로 변환하여야 한다. 이번 게시글에서는 이와 관련하여 알아보고자 한다.

시간 형식의 원소를 가지는 Pandas Series 객체는 .dt 접근자(accessor)를 사용하여 관련 어트리뷰트 또는 메서드를 사용할 수 있다. 이와 관련해서 pandas.Series.dt.date 부터 pandas.Series.dt.as_unit() 까지 총 54개의 어트리뷰트와 메서드가 있다.
※ Pandas 2.0.0 기준

실습

시리즈(Series)

먼저 날짜를 원소로 하는 시리즈 객체를 준비해보자.

1
2
3
4
5
ser1 = pd.Series(["2077-12-30", "2077-12-31"])
ser1
## 0 2077-12-30
## 1 2077-12-31
## dtype: object

상기 데이터는 표준 시간형식으로 보이나 데이터 형식이 “object”로 문자열로 취급된다. 그렇기에 다음의 코드 또한 정상동작하는 것을 알 수 있다.

1
2
3
4
ser1 + "@"
## 0 2077-12-30@
## 1 2077-12-31@
## dtype: object

Pandas에서 날짜 형식으로의 변환을 지원하는 함수는 to_datetime() 으로 기본은 나노초(nano second)인 시간 데이터 유형으로 자료유형이 바뀌는 것을 알 수 있다.

1
2
3
4
5
ser1_date = pd.to_datetime(ser1)
ser1_date
## 0 2077-12-30
## 1 2077-12-31
## dtype: datetime64[ns]

단, 표준 날짜 규격을 벗어난 원소의 경우 올바로 처리되지 않을 수 있다. 다음의 코드가 그 예시이다.

1
2
3
4
5
6
7
pd.to_datetime(pd.Series(["12/30 2023년", "12/31 2023년"]))
---------------------------------------------------------------------------
DateParseError Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_8488\891717922.py in <module>
----> 1 pd.to_datetime(pd.Series(["12/30 2023년", "12/31 2023년"]))
......
DateParseError: Unknown datetime string format, unable to parse: 12/30 2023년, at position 0

앞서 언급했지만 to_datetime() 함수는 표준 형식(YYYY:mm:dd HH:MM:SS) 또는 그와 유사한 형식이 입력될 경우 시간 데이터 형식으로 데이터를 바꿔준다. 그리고 지원하는 형식이 아닌 경우 사용자가 그 규칙이나 패턴을 별도로 지정할 수 있다. 이 때 사용하는 것이 to_datetime() 함수의 “format” 인자이다. 다음의 코드를 보자.

1
2
3
4
5
pd.to_datetime(pd.Series(["12/30 2023년", "12/31 2023년"]), 
format = "%m/%d %Y년")
## 0 2023-12-30
## 1 2023-12-31
## dtype: datetime64[ns]

여기서 “%Y”는 네 자리수 연도를 나타낸다. “%” 기호 뒤에 알파벳 한글자를 입력하여 어느 위치에 시간요소 중 어떤 것이 있는지 지칭할 수 있는데 지원하는 모든 형식을 확인하려면 파이썬 공식 홈페이지의 관련 페이지에 접속하면 된다.

해당 페이지에 접근하면 다음과 같은 화면을 볼 수 있다.
Python Date Time Format Page(1)

그리고 스크롤을 조금 내려보면 다음과 같은 표를 볼 수 있다.
Python Date Time Format Page(2)

해당 표에 to_datetime() 함수의 “format” 인자에 사용할 수 있는 각종 문법을 확인할 수 있으니 참고하면 되겠다.

이제 앞에서 만든 “ser1_date” 객체에서 상세 시간 정보를 추출해보자. 시간 데이터로 구성된 Pandas Series 객체는 .dt 접근자(accessor)를 사용할 수 있으며 해당 접근자에는 시간 데이터를 다루는 다양한 어트리뷰트 또는 메서드가 내장되어 있다. 다음의 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ser1_date.dt.year
## 0 2077
## 1 2077
## dtype: int32

ser1_date.dt.month
## 0 12
## 1 12
## dtype: int32

ser1_date.dt.day
## 0 30
## 1 31
## dtype: int32

ser1_date.dt.hour
## 0 0
## 1 0
## dtype: int32

ser1_date.dt.minute
## 0 0
## 1 0
## dtype: int32

ser1_date.dt.second
## 0 0
## 1 0
## dtype: int32

ser1_date.dt.weekday # 0이 월요일, 6이 일요일
## 0 3
## 1 4
## dtype: int32

“.dt” 접근자 뒤에 적혀있는 각 어트리뷰트 이름을 보면 어떤 내용이 뽑히는지 충분히 알 수 있을 것이라 생각된다. 그리고 시/분/초 의 경우 별도의 정보가 기록되어있지 않다면 0으로 지정(즉, 0시 0분 0초)되어 있기에 추출 결과가 모두 0인 것을 알 수 있다.

마지막 코드는 “.weekday” 어트리뷰트로 추출한 각 날짜의 요일정보인데 코드 실행 결과를 보면 “2077-12-30”일이 3으로 목요일이고 “2077-12-31”일이 4로 일요일을 뜻한다. 그리고 한 주의 시작이 일요일이 아니라 월요일로 설정되어있기 때문에 “.weekday” 어트리뷰트의 결과 중 0이 월요일이고 6이 일요일임을 명심하도록 하자.

“.dt”접근자의 .strftime() 메서드를 사용하면 원하는 형식으로 날짜 데이터를 변환할 수 있다. 단, 이 메서드의 실행 결과로 날짜 유형이 아닌 텍스트(“object”)가 반환되는 것을 알 수 있다.

1
2
3
4
5
6
7
8
9
ser1_date.dt.strftime("%Y-%m")
## 0 2077-12
## 1 2077-12
## dtype: object

ser1_date.dt.strftime("%Y년 %m월")
## 0 2077년 12월
## 1 2077년 12월
## dtype: object

그런데 날짜 데이터를 다루다 보면 데이터가 숫자로만 주어지는 경우가 있다. 이럴 때는 어떻게 변환을 해야할까? 다음의 코드를 보자.

1
2
3
4
5
6
7
ser2 = pd.Series([40001, 50000, 60000])
ser2_date1 = pd.to_datetime(ser2)
ser2_date1
## 0 1970-01-01 00:00:00.000040001
## 1 1970-01-01 00:00:00.000050000
## 2 1970-01-01 00:00:00.000060000
## dtype: datetime64[ns]

to_datetime() 함수에 숫자가 입력이 되면 “1970-01-01 00:00:00”을 기준으로 나노초 단위로 증감된 시간 데이터를 반환한다. 날짜를 셈함에 있어 그 기준을 바꿔주려면 “origin” 인자에 기준 시각을 입력하면 된다.

1
2
3
4
5
6
ser2_date2 = pd.to_datetime(ser2, origin = "1900-01-01")
ser2_date2
## 0 1900-01-01 00:00:00.000040001
## 1 1900-01-01 00:00:00.000050000
## 2 1900-01-01 00:00:00.000060000
## dtype: datetime64[ns]

그리고 시간 증감 단위를 변경하고자 한다면 “unit” 인자에 시간 단위를 입력하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
ser2_date3 = pd.to_datetime(ser2, origin = "1900-01-01", unit = "D")
ser2_date3
## 0 2009-07-09
## 1 2036-11-23
## 2 2064-04-10
## dtype: datetime64[ns]

ser2_date4 = pd.to_datetime(ser2, origin = "1900-01-01", unit = "s")
ser2_date4
## 0 1900-01-01 11:06:41
## 1 1900-01-01 13:53:20
## 2 1900-01-01 16:40:00
## dtype: datetime64[ns]

입력할 수 있는 단위는 상기 내용 외에도 밀리초(ms), 마이크로초(us)도 있다.

그리고 조금 다른 형식의 숫자가 주어지는 경우도 있다. 년월일 또는 년월 형식으로 주어지는데 이 경우는 그대로 문자로 변환한 후 그 형식을 “format” 인자에 잘 명시해주면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
ser3 = pd.Series([207701, 207702, 207703, 207704])
ser3
## 0 207701
## 1 207702
## 2 207703
## 3 207704
## dtype: int64

pd.to_datetime(ser3.astype("str"), format = "%Y%m")
## 0 2077-01-01
## 1 2077-02-01
## 2 2077-03-01
## 3 2077-04-01
## dtype: datetime64[ns]

ser4 = pd.Series([20770112, 20770223, 20770301, 20770402])
ser4
## 0 20770112
## 1 20770223
## 2 20770301
## 3 20770402
## dtype: int64

pd.to_datetime(ser4.astype("str"), format = "%Y%m%d")
## 0 2077-01-12
## 1 2077-02-23
## 2 2077-03-01
## 3 2077-04-02
## dtype: datetime64[ns]

데이터프레임(DataFrame)

이제 데이터프레임 객체를 기반으로 실습해보자. 게시물 상단의 링크를 통해 “bike.csv” 파일을 다운받고 불러오자.

1
2
3
df = pd.read_csv("bike.csv")
df = df[["datetime", "temp"]].copy()
df.head(2)
datetime temp
0 2011-01-01 00:00:00 9.84
1 2011-01-01 01:00:00 9.02

“datetime” 변수의 데이터 유형은 Object이다. 다음의 코드를 통해 해당 내용을 확인할 수 있다.

1
2
df["datetime"].dtype
## dtype('O')

앞에서 소개한 to_datetime() 을 사용해서 변수 데이터 유형을 변경할 수 있다.

1
2
3
df["datetime"] = pd.to_datetime(df["datetime"])
df["datetime"].dtype
## dtype('<M8[ns]')

이제 “datetime” 변수에서 각종 시간 정보를 추출할 수 있다.

1
2
3
4
5
6
7
8
import numpy as np
df["year" ] = df["datetime"].dt.year
df["month"] = df["datetime"].dt.month
df["date" ] = df["datetime"].dt.date
df["hour" ] = df["datetime"].dt.hour
df["wday" ] = df["datetime"].dt.weekday
df["is_wend"] = np.where(df["wday"] >= 5, 1, 0)
df.head()
datetime temp year month date hour wday is_wend
0 2011-01-01 00:00:00 9.84 2011 1 2011-01-01 0 5 1
1 2011-01-01 01:00:00 9.02 2011 1 2011-01-01 1 5 1
2 2011-01-01 02:00:00 9.02 2011 1 2011-01-01 2 5 1
3 2011-01-01 03:00:00 9.84 2011 1 2011-01-01 3 5 1
4 2011-01-01 04:00:00 9.84 2011 1 2011-01-01 4 5 1

참고로 “is_wend”는 주말여부를 나타내는 변수이다. “wday” 변수와 같이 비교해보면 토요일과 일요일이 1이고 나머지가 0인 변수라는 것을 알 수 있고, 향후 주중/주말을 비교하는 각종 분석에 사용되는 변수가 되겠다.

1
pd.crosstab(df["wday"], df["is_wend"])
is_wend 0 1
wday
0 1551 0
1 1539 0
2 1551 0
3 1553 0
4 1529 0
5 0 1584
6 0 1579
Your browser is out-of-date!

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

×