파이썬 기반 데이터분석을 위하여 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 | ser1 = pd.Series(["2077-12-30", "2077-12-31"]) |
상기 데이터는 표준 시간형식으로 보이나 데이터 형식이 “object”로 문자열로 취급된다. 그렇기에 다음의 코드 또한 정상동작하는 것을 알 수 있다.
1 | ser1 + "@" |
Pandas에서 날짜 형식으로의 변환을 지원하는 함수는 to_datetime()
으로 기본은 나노초(nano second)인 시간 데이터 유형으로 자료유형이 바뀌는 것을 알 수 있다.
1 | ser1_date = pd.to_datetime(ser1) |
단, 표준 날짜 규격을 벗어난 원소의 경우 올바로 처리되지 않을 수 있다. 다음의 코드가 그 예시이다.
1 | pd.to_datetime(pd.Series(["12/30 2023년", "12/31 2023년"])) |
앞서 언급했지만 to_datetime()
함수는 표준 형식(YYYY:mm:dd HH:MM:SS) 또는 그와 유사한 형식이 입력될 경우 시간 데이터 형식으로 데이터를 바꿔준다. 그리고 지원하는 형식이 아닌 경우 사용자가 그 규칙이나 패턴을 별도로 지정할 수 있다. 이 때 사용하는 것이 to_datetime()
함수의 “format” 인자이다. 다음의 코드를 보자.
1 | pd.to_datetime(pd.Series(["12/30 2023년", "12/31 2023년"]), |
여기서 “%Y”는 네 자리수 연도를 나타낸다. “%” 기호 뒤에 알파벳 한글자를 입력하여 어느 위치에 시간요소 중 어떤 것이 있는지 지칭할 수 있는데 지원하는 모든 형식을 확인하려면 파이썬 공식 홈페이지의 관련 페이지에 접속하면 된다.
해당 페이지에 접근하면 다음과 같은 화면을 볼 수 있다.
그리고 스크롤을 조금 내려보면 다음과 같은 표를 볼 수 있다.
해당 표에 to_datetime()
함수의 “format” 인자에 사용할 수 있는 각종 문법을 확인할 수 있으니 참고하면 되겠다.
이제 앞에서 만든 “ser1_date” 객체에서 상세 시간 정보를 추출해보자. 시간 데이터로 구성된 Pandas Series 객체는 .dt 접근자(accessor)를 사용할 수 있으며 해당 접근자에는 시간 데이터를 다루는 다양한 어트리뷰트 또는 메서드가 내장되어 있다. 다음의 코드를 보자.
1 | ser1_date.dt.year |
“.dt” 접근자 뒤에 적혀있는 각 어트리뷰트 이름을 보면 어떤 내용이 뽑히는지 충분히 알 수 있을 것이라 생각된다. 그리고 시/분/초 의 경우 별도의 정보가 기록되어있지 않다면 0으로 지정(즉, 0시 0분 0초)되어 있기에 추출 결과가 모두 0인 것을 알 수 있다.
마지막 코드는 “.weekday” 어트리뷰트로 추출한 각 날짜의 요일정보인데 코드 실행 결과를 보면 “2077-12-30”일이 3으로 목요일이고 “2077-12-31”일이 4로 일요일을 뜻한다. 그리고 한 주의 시작이 일요일이 아니라 월요일로 설정되어있기 때문에 “.weekday” 어트리뷰트의 결과 중 0이 월요일이고 6이 일요일임을 명심하도록 하자.
“.dt”접근자의 .strftime()
메서드를 사용하면 원하는 형식으로 날짜 데이터를 변환할 수 있다. 단, 이 메서드의 실행 결과로 날짜 유형이 아닌 텍스트(“object”)가 반환되는 것을 알 수 있다.
1 | ser1_date.dt.strftime("%Y-%m") |
그런데 날짜 데이터를 다루다 보면 데이터가 숫자로만 주어지는 경우가 있다. 이럴 때는 어떻게 변환을 해야할까? 다음의 코드를 보자.
1 | ser2 = pd.Series([40001, 50000, 60000]) |
to_datetime()
함수에 숫자가 입력이 되면 “1970-01-01 00:00:00”을 기준으로 나노초 단위로 증감된 시간 데이터를 반환한다. 날짜를 셈함에 있어 그 기준을 바꿔주려면 “origin” 인자에 기준 시각을 입력하면 된다.
1 | ser2_date2 = pd.to_datetime(ser2, origin = "1900-01-01") |
그리고 시간 증감 단위를 변경하고자 한다면 “unit” 인자에 시간 단위를 입력하면 된다.
1 | ser2_date3 = pd.to_datetime(ser2, origin = "1900-01-01", unit = "D") |
입력할 수 있는 단위는 상기 내용 외에도 밀리초(ms), 마이크로초(us)도 있다.
그리고 조금 다른 형식의 숫자가 주어지는 경우도 있다. 년월일 또는 년월 형식으로 주어지는데 이 경우는 그대로 문자로 변환한 후 그 형식을 “format” 인자에 잘 명시해주면 된다.
1 | ser3 = pd.Series([207701, 207702, 207703, 207704]) |
데이터프레임(DataFrame)
이제 데이터프레임 객체를 기반으로 실습해보자. 게시물 상단의 링크를 통해 “bike.csv” 파일을 다운받고 불러오자.
1 | df = pd.read_csv("bike.csv") |
datetime | temp | |
---|---|---|
0 | 2011-01-01 00:00:00 | 9.84 |
1 | 2011-01-01 01:00:00 | 9.02 |
“datetime” 변수의 데이터 유형은 Object이다. 다음의 코드를 통해 해당 내용을 확인할 수 있다.
1 | df["datetime"].dtype |
앞에서 소개한 to_datetime()
을 사용해서 변수 데이터 유형을 변경할 수 있다.
1 | df["datetime"] = pd.to_datetime(df["datetime"]) |
이제 “datetime” 변수에서 각종 시간 정보를 추출할 수 있다.
1 | import numpy as np |
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 |