pandas를 활용한 날짜 및 시간 데이터 처리
1. pandas를 활용한 날짜 및 시간 데이터 처리
1) Timestamp 생성
- pandas 라이브러리에서 Timestamp 객체를 생성하려면 pd.Timestamp()를 사용합니다. 이는 특정 날짜와 시간에 대한 타임스탬프를 생성합니다.
- 문자열을 Timestamp로 변환하기 위해 pd.to_datetime() 메서드를 사용합니다. 이 메서드는 날짜 문자열을 Timestamp 객체로 변환합니다.
import pandas as pd
# Timestamp 생성
ts1 = pd.Timestamp('2023-07-29 12:30:00')
print("Timestamp:", ts1)
print(f"ts 타입: {type(ts1)}")
# 문자열로 부터 Timestamp 생성
date_string = '2023-07-29'
ts2 = pd.to_datetime(date_string)
print("문자열에서 생성:", ts2)
print(f"변환된 변수 ts의 타입: {type(ts2)}")
Timestamp: 2023-07-29 12:30:00
ts 타입: <class 'pandas._libs.tslibs.timestamps.Timestamp'>
문자열에서 생성: 2023-07-29 00:00:00
변환된 변수 ts의 타입: <class 'pandas._libs.tslibs.timestamps.Timestamp'>
2) Timestamp 속성 접근 및 시간대 변경
pandas의 Timestamp 객체는 날짜와 시간 데이터를 효율적으로 처리할 수 있는 다양한 속성과 메서드를 제공합니다.
※ Timestamp 속성 접근
Timestamp 객체는 연도, 월, 일, 요일 등 다양한 속성을 가지고 있습니다.
이러한 속성에 접근하여 원하는 시간 정보를 추출할 수 있습니다.
- 연도, 월, 일: year, month, day 속성을 통해 각각 연도, 월, 일을 얻을 수 있습니다.
- 요일: dayofweek 속성을 통해 요일을 숫자로 얻을 수 있습니다. (0: 월요일, 6: 일요일)
※ 시간대 변경
실제 세계에서는 시간대가 다르기 때문에, 데이터 분석 시 시간대를 고려해야 합니다.
pandas의 Timestamp 객체는 tz_localize와 tz_convert 메서드를 통해 시간대를 설정하고 변환할 수 있습니다.
- naive 시간:
시간대 정보가 명시되지 않은 Timestamp를 의미합니다.
즉, ts = pd.Timestamp('2023-07-29')와 같이 시간대를 지정하지 않고 생성된 Timestamp는 naive 시간입니다.
이러한 naive 시간은 전 세계 여러 시간대를 나타내는지 명확하지 않아, 시간대 관련 계산이나 비교를 할 때 주의해야 합니다. - tz_localize:
아직 시간대 정보가 없는 Timestamp 객체에 시간대를 부여합니다. - tz_convert:
이미 시간대가 설정된 Timestamp 객체의 시간대를 다른 시간대로 변환합니다.
※ 예시 코드
from datetime import timedelta
import pandas as pd
# 특정 날짜의 Timestamp 생성
ts = pd.Timestamp('2023-07-29')
# Timestamp 속성 접근
print("연도:", ts.year)
print("월:", ts.month)
print("일:", ts.day)
print("요일:", ts.dayofweek)
# 시간대 변경
print("naive 시간:", ts)
ts_utc = ts.tz_localize('UTC')
print("세계 표준 시간:", ts_utc)
ts_kst = ts_utc.tz_convert('Asia/Seoul')
print("한국 시간:", ts_kst)
연도: 2023
월: 7
일: 29
요일: 5
naive 시간: 2023-07-29 00:00:00
세계 표준 시간: 2023-07-29 00:00:00+00:00
한국 시간: 2023-07-29 09:00:00+09:00
3) Dataframe 문자열 컬럼을 Timestamp로 변환
많은 데이터셋에서 날짜와 시간 정보는 문자열 형태로 저장되어 있습니다.
pandas의 to_datetime 함수는 이러한 문자열을 pandas의 고유한 날짜/시간 객체인 Timestamp로 변환하여
시계열 분석에 필요한 다양한 연산과 분석을 수행할 수 있도록 지원합니다.
Timestamp 객체는 다양한 시간대를 지원하며, 나노초 단위까지의 정밀도를 제공하여 시계열 데이터 분석의 정확성을 높입니다.
이번에는 애플 주가 5분봉 데이터셋을 사용하여 'Datetime' 열을 문자열에서 Timestamp 객체로 변환하는 과정을 살펴보겠습니다.
이를 통해 시계열 분석을 위한 기반을 마련하고, 다양한 날짜 기반 연산과 분석을 수행할 수 있도록 합니다.
변환 과정에서 Datetime 컬럼은 pandas의 datetime64[ns] dtype을 가진 Series로 변환됩니다.
즉, 전체 컬럼의 데이터 타입이 날짜/시간 데이터로 변경되며, 각 요소는 Timestamp 객체로 변환되어 더욱 정교한 날짜/시간 연산이 가능해집니다.
애플 주가 5분봉 데이터('AAPL_2023-09_2023-12.csv')를 불러와 'Datetime' 컬럼을 Timestamp 객체로 변환하여 데이터를 처리하는 과정을 확인해 보겠습니다.
'Datetime' 컬럼이 날짜를 나타내는 컬럼입니다
import pandas as pd
aapl_5min = pd.read_csv('AAPL_2023-09_2023-12.csv')
print(f"기간 : {aapl_5min['Datetime'].iloc[0]} ~ {aapl_5min['Datetime'].iloc[-1]}")
aapl_5min.head()
to_datetime 함수를 이용하여 'Datetime' 컬럼 데이터 (Series)와 각 요소에 대한 타입이 어떻게 변화되는지 살펴 보겠습니다.
print(f"변환전 'Datetime'컬럼(Series)의 dtype : {aapl_5min['Datetime'].dtype}")
print(f"변환전 'Datetime'컬럼(Series)의 각 요소 type : {type(aapl_5min['Datetime'][0])}")
aapl_5min['Datetime'] = pd.to_datetime(aapl_5min['Datetime'])
print(f"변환전 'Datetime'컬럼(Series)의 dtype : {aapl_5min['Datetime'].dtype}")
print(f"변환후 'date'컬럼(Series)의 각 요소 type : {type(aapl_5min['Datetime'][0])}")
print(f"'date'컬럼(Series)의 각 요소 type : {aapl_5min['Datetime'][0]}")
변환전 'Datetime'컬럼(Series)의 dtype : object
변환전 'Datetime'컬럼(Series)의 각 요소 type : <class 'str'>
변환전 'Datetime'컬럼(Series)의 dtype : datetime64[ns]
변환후 'date'컬럼(Series)의 각 요소 type : <class 'pandas._libs.tslibs.timestamps.Timestamp'>
'date'컬럼(Series)의 각 요소 type : 2023-09-29 19:55:00
※ 변환 전 데이터 타입 확인
nongsan_daily['date'].dtype를 사용하여 date 컬럼의 데이터 타입을 확인합니다.
변환 전에는 이 컬럼의 데이터 타입이 object입니다.
type(nongsan_daily['date'][0])를 사용하여 date 컬럼의 첫 번째 요소의 타입을 확인합니다.
변환 전에는 각 요소가 문자열(str) 타입입니다.
※ 문자열을 Timestamp로 변환
df['Datetime'] = pd.to_datetime(df['Datetime'])
문자열 형태의 날짜 데이터를 pandas의 Timestamp 객체로 변환하는 함수입니다.
이 함수를 사용하여 'date' 컬럼의 모든 요소를 Timestamp 객체로 변환합니다.
※ 변환 후 데이터 타입 확인
이는 해당 컬럼의 모든 값이 날짜와 시간을 나타내는 데이터 타입임을 의미합니다.
type(nongsan_daily['date'][0]) 부분은 변환된 'date' 컬럼의 첫 번째 요소의 타입을 확인합니다.
변환 후에는 <class 'pandas._libs.tslibs.timestamps.Timestamp'>와 같은 Timestamp 객체임을 확인할 수 있습니다.
Timestamp 객체는 pandas에서 날짜와 시간을 나타내는 특별한 객체로, 날짜/시간 연산을 지원합니다.
예를 들어, 두 날짜 사이의 차이 계산, 특정 기간의 데이터 추출 등이 가능합니다.
※ 'date' 컬럼(Series)의 타입과 'date' 컬럼의 요소 타입의 차이
Series의 타입:
Series의 타입은 전체 컬럼이 어떤 종류의 데이터를 담고 있는지를 나타냅니다.
pandas에서는 dtype 속성을 사용하여 이를 확인할 수 있습니다.
예를 들어, 'date' 컬럼 전체가 날짜와 시간 데이터를 담고 있다면, 변환 후 이 컬럼의 dtype은 datetime64[ns]가 됩니다.
이는 'date' 컬럼 내의 모든 값이 날짜와 시간을 나타내는 데이터 타입임을 의미합니다.
요소의 타입:
반면, 요소의 타입은 컬럼을 구성하는 개별 값, 즉 각 요소의 데이터를 나타냅니다.
pandas에서는 각 요소의 type을 확인하여 이를 알 수 있습니다.
예를 들어, 'date' 컬럼의 각 요소는 pandas의 Timestamp 객체입니다.
이는 각 날짜가 pandas의 고유한 날짜/시간 객체로 저장되어 있음을 의미합니다.
※ datetime 타입과 datetime64[ns] 타입의 차이
datetime
- Python의 표준 라이브러리인 datetime 모듈에서 제공하는 날짜와 시간을 나타내는 기본적인 클래스입니다.
- 단일 날짜와 시간 값을 나타내는 데 주로 사용됩니다.
- 다양한 연산(날짜 차이 계산, 포맷팅 등)을 지원하지만, 대규모 데이터 처리에는 비교적 느릴 수 있습니다.
datetime64[ns]
- NumPy에서 제공하는 날짜와 시간을 나타내는 데이터 타입입니다.
- pandas에서 날짜와 시간 데이터를 효율적으로 처리하기 위해 사용됩니다.
- 'ns'는 nanosecond(나노초)를 의미하며, 매우 정밀한 시간 단위까지 표현할 수 있습니다.
- NumPy 배열의 특성을 활용하여 벡터화된 연산을 지원하기 때문에 대규모 데이터 처리에 적합합니다.
4) 시간대 설정 및 변환
날짜/시간 데이터에 대한 직관적인 접근은 dt 속성을 통해 날짜의 연도, 월, 일, 요일 등을 쉽게 추출할 수 있습니다.
다양한 시간 단위 변환: 초, 분, 시간 등 다양한 시간 단위로 변환하거나, 특정 기간으로 그룹화할 수 있습니다.
다음은 자주 사용하는 dt의 주요 속성과 메서드 입니다.
- year: 연도를 반환합니다.
- month: 월을 반환합니다.
- day: 일을 반환합니다.
- hour: 시간을 반환합니다.
- minute: 분을 반환합니다.
- second: 초를 반환합니다.
- date: 날짜 부분만 반환합니다.
- time: 시간 부분만 반환합니다.
- tz_localize: 시간대를 설정합니다.
- tz_convert: 시간대를 변환합니다.
※ Timestamp 속성과의 유사점 및 차이점
유사점:
- 둘 다 날짜와 시간 정보를 다루는 데 사용됩니다. 다양한 속성과 메서드를 통해 시간 관련 정보에 접근할 수 있습니다.
차이점:
- 대상: Timestamp는 단일 시점을 나타내는 반면, dt 속성은 Series 전체에 적용되어 각 요소의 날짜/시간 정보를 다룹니다.
- 사용 방법: Timestamp는 객체 자체의 속성에 직접 접근하는 반면, dt 속성은 Series 객체에 .dt를 붙여 사용합니다.
※ 예시 코드
aapl_data = aapl_5min.copy()
# 미국 동부 시간대로 시간대 설정 (tz_localize)
aapl_data['Datetime_EST'] = aapl_data['Datetime'].dt.tz_localize('America/New_York')
# 한국 시간으로 변환 (tz_convert)
aapl_data['Datetime_KST'] = aapl_data['Datetime_EST'].dt.tz_convert('Asia/Seoul')
aapl_data[['Datetime', 'Datetime_EST', 'Datetime_KST']].head()
5) 특정 시간 데이터 추출(1)
정규 거래 시간(09:30:00 - 16:00:00) 데이터만을 추출해 보겠습니다.
import pandas as pd
aapl_data = aapl_5min.copy()
# 정규 거래 시간 필터링 (09:30:00 - 16:00:00)
regular_hours1 = aapl_data[
(aapl_data['Datetime'].dt.time >= pd.to_datetime('09:30:00').time()) &
(aapl_data['Datetime'].dt.time <= pd.to_datetime('16:00:00').time())
]
# 결과 출력
print(regular_hours1.head())
print(f"정규 거래 시간 데이터 수: {regular_hours1.shape[0]}")
Datetime Open High Low Close Volume
47 2023-09-29 16:00:00 170.770 170.854 170.639 170.843 20252334
48 2023-09-29 15:55:00 171.039 171.044 170.320 170.783 2735718
49 2023-09-29 15:50:00 171.039 171.483 170.758 171.042 1504667
50 2023-09-29 15:45:00 170.540 171.054 170.499 171.027 586999
51 2023-09-29 15:40:00 170.909 170.934 170.529 170.543 626840
정규 거래 시간 데이터 수: 6557
6) 특정 시간 데이터 추출(2)
이번에는 pandas의 between_time 메서드를 사용하여 동일한 작업을 수행해 보겠습니다.
between_time 메서드는 시간 인덱스를 사용하여 특정 시간 범위 내의 데이터를 간편하게 추출할 수 있습니다.
- set_index() 메서드를 사용하여 Datetime 열을 인덱스로 설정합니다. 이때, 인덱스 이름으로 'Datetime'을 지정합니다.
- between_time() 메서드를 사용하여 정규 거래 시간(09:30 - 16:00) 사이의 데이터를 필터링합니다. 이 메서드는 인덱스가 Datetime인 경우에 사용할 수 있습니다.
aapl_data = aapl_5min.copy()
# 정규 거래 시간 필터링 (09:30:00 - 16:00:00)
aapl_data = aapl_data.set_index('Datetime')
regular_hours2 = aapl_data.between_time('09:30', '16:00')
# 결과 출력
print(regular_hours2.head())
print(f"정규 거래 시간 데이터 수: {regular_hours2.shape[0]}")
Open High Low Close Volume
Datetime
2023-09-29 16:00:00 170.770 170.854 170.639 170.843 20252334
2023-09-29 15:55:00 171.039 171.044 170.320 170.783 2735718
2023-09-29 15:50:00 171.039 171.483 170.758 171.042 1504667
2023-09-29 15:45:00 170.540 171.054 170.499 171.027 586999
2023-09-29 15:40:00 170.909 170.934 170.529 170.543 626840
정규 거래 시간 데이터 수: 6557
7) 날짜 및 시간 데이터의 리샘플링 (Resampling)
리샘플링은 시계열 데이터 분석에서 필수적인 작업 중 하나로, 데이터를 원하는 시간 간격으로 변환할 수 있는 유용한 기능입니다.
시계열 데이터를 분석할 때, 데이터의 시간 간격을 조정함으로써 더 편리하게 트렌드를 파악할 수 있습니다.
※ 리샘플링의 활용
리샘플링을 통해 시계열 데이터를 다양한 시간 간격으로 요약할 수 있습니다.
예를 들어, 초단위 데이터를 분 단위로, 분 단위 데이터를 시간 단위로, 시간 단위 데이터를 일 단위로 변환할 수 있습니다.
이를 통해 데이터의 과도한 세분화를 완화하고 노이즈를 줄이며, 더 큰 트렌드를 파악할 수 있게 됩니다.
※ 리샘플링 후 사용할 수 있는 대표적인 함수들
mean()
리샘플링된 각 간격 내의 평균 값을 계산합니다.
용도: 데이터의 평균을 구하여 전반적인 트렌드를 파악할 때 유용합니다.
예시: data.resample('1H').mean()
1시간 단위로 리샘플링 후 평균 값을 계산합니다.
sum()
리샘플링된 각 간격 내의 값을 모두 더합니다.
용도: 특정 기간 동안의 총합을 구할 때 유용합니다. 예를 들어, 거래량 데이터의 총합을 구할 때 사용할 수 있습니다.
예시: data.resample('1D').sum()
일 단위로 리샘플링 후 총합을 계산합니다.
last()
리샘플링된 각 간격 내의 마지막 값을 반환합니다.
용도: 각 기간의 마지막 상태나 값을 확인할 때 유용합니다. 주식 데이터에서 주로 사용됩니다.
예시: data.resample('1H').last()
1시간 단위로 리샘플링 후 각 시간대의 마지막 값을 반환합니다.
first()
리샘플링된 각 간격 내의 첫 번째 값을 반환합니다.
용도: 각 기간의 시작 값을 파악할 때 유용합니다.
예시: data.resample('1D').first()
일 단위로 리샘플링 후 각 일의 첫 번째 값을 반환합니다.
min()
리샘플링된 각 간격 내의 최소값을 반환합니다.
용도: 특정 기간 동안의 최저치를 파악할 때 사용됩니다.
예시: data.resample('1D').min()
일 단위로 리샘플링 후 최소값을 반환합니다.
max()
리샘플링된 각 간격 내의 최대값을 반환합니다.
용도: 특정 기간 동안의 최고치를 파악할 때 사용됩니다.
예시: data.resample('1D').max()
일 단위로 리샘플링 후 최대값을 반환합니다.
ohlc
리샘플링된 각 간격 내의 open, high, low, close 값을 계산합니다.
용도: 금융 데이터 분석에서 자주 사용되며, 각 시간대의 시가(open), 고가(high), 저가(low), 종가(close)를 한 번에 계산할 수 있습니다.
예시: data.resample('1H').ohlc()
1시간 단위로 리샘플링 후 시가, 고가, 저가, 종가를 계산합니다.
count()
리샘플링된 각 간격 내의 값의 개수를 반환합니다.
용도: 각 기간 동안의 데이터 포인트 수를 파악할 때 유용합니다.
예시: data.resample('1H').count()
1시간 단위로 리샘플링 후 각 시간대의 데이터 포인트 수를 계산합니다.
※ 예시 코드
aapl_data = aapl_5min.copy()
aapl_data = aapl_data.set_index('Datetime')
aapl_hourly1 = aapl_data.resample('1h').last()
display(aapl_hourly1.head())
aapl_data = aapl_5min.copy()
aapl_hourly2 = aapl_data.resample('1h', on='Datetime').last()
display(aapl_hourly2.head())
8) 정규 거래 시간 데이터 추출 및 일별 거래량 계산
import pandas as pd
import matplotlib.pyplot as plt
# 원본 데이터프레임을 복사하여 사용
aapl_data = aapl_5min.copy()
aapl_data['Datetime'] = pd.to_datetime(aapl_data['Datetime'])
aapl_data = aapl_data.set_index('Datetime')
aapl_data = aapl_data.between_time('09:30', '16:00')
# 일별 거래량 계산 (거래량의 합계)
daily_volume = aapl_data['Volume'].resample('1D').sum()
# 일별 거래량을 막대 그래프로 시각화
plt.figure(figsize=(10, 4))
plt.bar(daily_volume.index, daily_volume, color='skyblue')
# 그래프 제목 및 레이블
plt.title('AAPL Daily Trading Volume')
plt.xlabel('Date')
plt.ylabel('Volume')
plt.grid(True)
plt.show()
9) 날짜 범위 생성 및 날짜 차집합 구하기
- pd.date_range() 메서드를 사용하여 날짜 범위를 생성합니다. 이 메서드는 start와 periods 매개변수로 시작 날짜와 생성할 기간의 수를 설정하며, freq 매개변수로 빈도를 지정합니다. 예: 시간 단위로 생성하려면 'h'를 사용합니다.
- normalize() 메서드를 사용하여 시간 정보를 제거하고 날짜만 남깁니다.
- difference() 메서드를 사용하여 두 날짜 범위 간의 차집합을 계산합니다. 이 메서드는 첫 번째 범위에서 두 번째 범위에 포함되지 않는 날짜를 반환합니다.
import pandas as pd
# 날짜 범위 생성
date_range = pd.date_range(start='2023-09-01 09:00:00', periods=10, freq='h')
print("Original Date Range with Time:")
print(date_range)
# normalize() 함수로 시간 정보 제거
normalized_dates = date_range.normalize()
print("\nNormalized Date Range (Date Only):")
print(normalized_dates)
# 차집합 계산
additional_dates = pd.date_range(start='2023-09-05', periods=5, freq='D')
print("\nAdditional Dates:")
print(additional_dates)
# 차집합 계산하여 차이점 출력
difference_dates = normalized_dates.difference(additional_dates)
print("\nDifference (Dates not in Additional Dates):")
print(difference_dates)
Original Date Range with Time:
DatetimeIndex(['2023-09-01 09:00:00', '2023-09-01 10:00:00',
'2023-09-01 11:00:00', '2023-09-01 12:00:00',
'2023-09-01 13:00:00', '2023-09-01 14:00:00',
'2023-09-01 15:00:00', '2023-09-01 16:00:00',
'2023-09-01 17:00:00', '2023-09-01 18:00:00'],
dtype='datetime64[ns]', freq='H')
Normalized Date Range (Date Only):
DatetimeIndex(['2023-09-01', '2023-09-01', '2023-09-01', '2023-09-01',
'2023-09-01', '2023-09-01', '2023-09-01', '2023-09-01',
'2023-09-01', '2023-09-01'],
dtype='datetime64[ns]', freq=None)
Additional Dates:
DatetimeIndex(['2023-09-05', '2023-09-06', '2023-09-07', '2023-09-08',
'2023-09-09'],
dtype='datetime64[ns]', freq='D')
Difference (Dates not in Additional Dates):
DatetimeIndex(['2023-09-01'], dtype='datetime64[ns]', freq=None)
10) 공휴일 추정하기
- pd.date_range()를 사용하여 주어진 기간 동안의 평일(월-금) 날짜를 생성하며, freq='B'는 평일을 나타냅니다.
- 평일 날짜에서 거래된 날짜를 제외하여 거래되지 않은 날짜(공휴일 가능성 있는 날짜)를 추출하려면 difference() 메서드를 사용합니다.
- difference(trading_days)는 weekdays와 trading_days 간의 차집합을 계산하여 거래되지 않은 날짜를 반환합니다.
import pandas as pd
# aapl_5min 데이터를 복사하고 Datetime을 인덱스로 설정
aapl_data = aapl_5min.copy()
aapl_data['Datetime'] = pd.to_datetime(aapl_data['Datetime'])
aapl_data = aapl_data.set_index('Datetime')
# 정규 거래 시간(09:30 - 16:00) 데이터만 필터링
market_hours_data = aapl_data.between_time('09:30', '16:00')
# 거래가 이루어진 날짜 추출 (normalize 사용)
trading_days = market_hours_data.index.normalize().unique()
# 분석 기간의 모든 평일(월-금) 날짜 생성
weekdays = pd.date_range(start=market_hours_data.index.min().normalize(),
end=market_hours_data.index.max().normalize(), freq='B')
# 평일에서 거래된 날짜를 제거한 후 남은 날짜를 구하기 (거래가 이루어지지 않은 날짜)
non_trading_days = weekdays.difference(trading_days)
print("공휴일일 가능성이 있는 날짜들:\n", non_trading_days)
공휴일일 가능성이 있는 날짜들:
DatetimeIndex(['2023-09-04', '2023-11-23', '2023-12-25'], dtype='datetime64[ns]', freq=None)