현재 금융권에서 종사하고 있다보니 기본적으로 모든 정보들이 날짜를 포함하게 된다.
게다가 시, 분, 초가 필요한 경우는 거의 없고, 일자 단위의 계산이 필요한 경우가 99%다.
따라서 파이썬 내에서 날짜 계산하는 방식을 기록으로 남기고자 한다.
파이썬에서는 기본적으로 datetime 라이브러리를 사용해서 해당 양식을 사용한다.
그런데 해당 포맷은 보통 시/분/초까지를 포함하므로, 이를 감안하여 전처리를 진행해야한다.
1. 문자열 → 날짜 (datetime.datetime.strptime : string parse time)
strptime('yyyymmdd', '%Y%m%d')
strptime을 이용해서 정해진 날짜형식(%Y%m%d)대로 해당 문자열을 인식해서 datetime 포맷으로 변형함
>>> import datetime
>>> d1_str = '20210607'
>>> d1_dt = datetime.datetime.strptime(d1_str,'%Y%m%d')
>>> d1_dt
datetime.datetime(2021, 6, 7, 0, 0)
>>> print(d1_dt)
2021-06-07 00:00:00
그런데 날짜형식(%Y%m%d) 중 연도에 해당하는 %Y 만 월(%m), 일(%d)과 다르게 대문자임을 알 수 있는데 만약 인식하고자 하는 문자열의 연도가 4자리인 경우는 대문자, 2자리인경우는 소문자를 사용한다.
>>> d2_dt = datetime.datetime.strptime(d1_str,'%y%m%d')
Traceback (most recent call last):
~~~~
ValueError: unconverted data remains: 607
>>> d2_str = '210607'
>>> d3_dt = datetime.datetime.strptime(d2_str,'%y%m%d')
>>> print(d3_dt)
2021-06-07 00:00:00
해당 포맷에서는 시, 분, 초를 포함하고 있으므로 이를 제거하려면 일자함수로 한번 더 가공을 해줘야한다.
>>> d1_dt
datetime.datetime(2021, 6, 7, 0, 0)
>>> d1 = d1_dt.date()
>>> d1
datetime.date(2021, 6, 7)
>>> print(d1)
2021-06-07
2. 날짜 → 문자열 (datetime.datetime.strftime : string format time)
strftime(datetime(yyyy,mm,dd), '%Y%m%d')
strptime의 반대사용, datetime 날짜를 다시 문자형태(%Y%m%d)로 수정
>>> d1
datetime.date(2021, 6, 7)
>>> datetime.datetime.strftime(d1,'%Y%m%d')
'20210607'
3. Pandas에서 문자열 → 날짜 (pandas.to_datetime)
pd.to_datetime(날짜 Series)
아래와 같은 데이터프레임이 있다고 가정하자.
>>> import pandas as pd
>>> df = pd.DataFrame({'date1' : ['20160517','20220505','20181017'],
... 'date2' : ['20190522','20220828','20200315'],
... 'name' : ['jhon','kim','lee']})
>>> df
date1 date2 name
0 20160517 20190522 jhon
1 20220505 20220828 kim
2 20181017 20200315 lee
판다스(pandas)에서 to_datetime을 통해 문자열을 날짜 형태로 쉽게 바꾸자
이때 날짜는 시/분/초를 포함하고 있다.
>>> df['date1_'] = pd.to_datetime(df['date1'])
>>> df['date2_'] = pd.to_datetime(df['date2'])
>>> df
date1 date2 name date1_ date2_
0 20160517 20190522 jhon 2016-05-17 2019-05-22
1 20220505 20220828 kim 2022-05-05 2022-08-28
2 20181017 20200315 lee 2018-10-17 2020-03-15
>>> df['date1_'][0]
Timestamp('2016-05-17 00:00:00')
4. Pandas에서 날짜 → 문자열 ( apply(lambda x : x.strftime() or x.dt.strftime())
① Series.apply(lambda x : x.strftime('%Y-%m-%d'))
apply 함수를 통해 컬럼별로 strftime 을 적용해서 문자열로 변경한다.
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date1 3 non-null object
1 date2 3 non-null object
2 name 3 non-null object
3 date1_ 3 non-null datetime64[ns]
4 date2_ 3 non-null datetime64[ns]
dtypes: datetime64[ns](2), object(3)
memory usage: 148.0+ bytes
>>> df['date1_str'] = df['date1_'].apply(lambda x : x.strftime('%Y-%m-%d'))
>>> df['date2_str'] = df['date2_'].apply(lambda x : x.strftime('%Y-%m-%d'))
>>> df.head()
date1 date2 name date1_ date2_ date1_str date2_str
0 20160517 20190522 jhon 2016-05-17 2019-05-22 2016-05-17 2019-05-22
1 20220505 20220828 kim 2022-05-05 2022-08-28 2022-05-05 2022-08-28
2 20181017 20200315 lee 2018-10-17 2020-03-15 2018-10-17 2020-03-15
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date1 3 non-null object
1 date2 3 non-null object
2 name 3 non-null object
3 date1_ 3 non-null datetime64[ns]
4 date2_ 3 non-null datetime64[ns]
5 date1_str 3 non-null object
6 date2_str 3 non-null object
dtypes: datetime64[ns](2), object(5)
memory usage: 172.0+ bytes
② DataFrame.apply(lambda x : x.dt.strftime('%Y-%m-%d'))
그런데 위 방식은 날짜 컬럼이 여러 개일 때 코드가 길어지는 단점이 있다.
그래서 컬럼별 변화가 아닌, 여러 컬럼을 선택 후 한번에 날짜형태를 문자열형태로 변화시키기 위해서는 아래와 같이 strftime함수 앞에 dt를 붙여야 가능하다.
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date1 3 non-null object
1 date2 3 non-null object
2 name 3 non-null object
3 date1_ 3 non-null datetime64[ns]
4 date2_ 3 non-null datetime64[ns]
dtypes: datetime64[ns](2), object(3)
memory usage: 148.0+ bytes
>>> df[['date1_str', 'date2_str']] = df[['date1_', 'date2_']].apply(lambda x : x.dt.strftime('%Y-%m-%d'))
>>> df.head()
date1 date2 name date1_ date2_ date1_str date2_str
0 20160517 20190522 jhon 2016-05-17 2019-05-22 2016-05-17 2019-05-22
1 20220505 20220828 kim 2022-05-05 2022-08-28 2022-05-05 2022-08-28
2 20181017 20200315 lee 2018-10-17 2020-03-15 2018-10-17 2020-03-15
>>> df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 date1 3 non-null object
1 date2 3 non-null object
2 name 3 non-null object
3 date1_ 3 non-null datetime64[ns]
4 date2_ 3 non-null datetime64[ns]
5 date1_str 3 non-null object
6 date2_str 3 non-null object
dtypes: datetime64[ns](2), object(5)
memory usage: 172.0+ bytes
만약 중간에 dt를 뺀다면 아래와 같은 에러가 발생한다.
>>> df[['date1_str', 'date2_str']] = df[['date1_', 'date2_']].apply(lambda x : x.strftime('%Y-%m-%d'))
Traceback (most recent call last):
~~~~
AttributeError: 'Series' object has no attribute 'strftime'
5. Pandas에서 특정 경과일 후(datetime.timedelta)
날짜 Series + datetime.timedelta(days = 365)
날짜연산을 하기 위해서는 일단 날짜형태로 변환 후, 특정 기간을 더하거나 뺄 때는 datetime의 timedelta를 이용한다.
이때, timedelta는 인자로 받을 수 있는 단위가 주(weeks), 일(days), 시간(hours), 분(minutes), 초(seconds)이다.
>>> df['date1_1y'] = df['date1_'] + datetime.timedelta(days = 365)
>>> df
date1 date2 name date1_ date2_ date1_1y
0 20160517 20190522 jhon 2016-05-17 2019-05-22 2017-05-17
1 20220505 20220828 kim 2022-05-05 2022-08-28 2023-05-05
2 20181017 20200315 lee 2018-10-17 2020-03-15 2019-10-17
6. Pandas에서 경과기간 구하기(numpy.timedelta)
(날짜2 - 날짜1)/np.timedelta(1,'D')
(날짜2 - 날짜1)/np.timedelta(1,'M')
(날짜2 - 날짜1)/np.timedelta(1,'Y')
date1_부터 date2_ 까지의 경과기간을 구할때는 두 컬럼을 빼주면 된다.
이때 경과기간은 일(days)로 저장이 되며, 형식은 Timedelta를 가진다.
>>> df['diff_days'] = df['date2_'] - df['date1_']
>>> df
date1 date2 name date1_ date2_ date1_1y diff_days
0 20160517 20190522 jhon 2016-05-17 2019-05-22 2017-05-17 1100 days
1 20220505 20220828 kim 2022-05-05 2022-08-28 2023-05-05 115 days
2 20181017 20200315 lee 2018-10-17 2020-03-15 2019-10-17 515 days
>>> df['diff_days'][0]
Timedelta('1100 days 00:00:00')
하지만 해당 경과일을 다른 계산에 이용하기 위해서는 timedelta가 아닌 숫자형태로 변환할 필요가 있다.
이를 위해 np.timedelta를 이용하는데, 이를 통해 경과일, 경과월, 경과연도를 구할 수 있다.
>>> df['diff_days2'] = (df['date2_'] - df['date1_'])/np.timedelta64(1,'D')
>>> df
date1 date2 name date1_ date2_ date1_1y diff_days diff_days2 diff_months diff_years
0 20160517 20190522 jhon 2016-05-17 2019-05-22 2017-05-17 1100 days 1100.0 36.140372 3.011698
1 20220505 20220828 kim 2022-05-05 2022-08-28 2023-05-05 115 days 115.0 3.778312 0.314859
2 20181017 20200315 lee 2018-10-17 2020-03-15 2019-10-17 515 days 515.0 16.920265 1.410022
>>> df['diff_days2'][0]
1100.0
>>> df['diff_months'] = (df['date2_'] - df['date1_'])/np.timedelta64(1,'M')
>>> df['diff_years'] = (df['date2_'] - df['date1_'])/np.timedelta64(1,'Y')
>>> df
date1 date2 name date1_ date2_ date1_1y diff_days diff_days2 diff_months diff_years
0 20160517 20190522 jhon 2016-05-17 2019-05-22 2017-05-17 1100 days 1100.0 36.140372 3.011698
1 20220505 20220828 kim 2022-05-05 2022-08-28 2023-05-05 115 days 115.0 3.778312 0.314859
2 20181017 20200315 lee 2018-10-17 2020-03-15 2019-10-17 515 days 515.0 16.920265 1.410022
7. 윤년인지 판별하기(calendar.isleap)
calender.isleap()
calendar 라이브러리에서 isleap함수를 쓰면 해당연도가 윤년인지 알 수 있다.
참고로 윤년에 대한 정의는 다음과 같다.
1. 연수가 4로 나누어 떨어지는 해는 윤년
2. 연수가 100으로 나누어 떨어지는 해는 평년
3. 연수가 400으로 나누어 떨어지는 해는 윤년
>>> import calendar
>>> df['date1_']
0 2016-05-17
1 2022-05-05
2 2018-10-17
Name: date1_, dtype: datetime64[ns]
>>> df['date1_'][0].year
2016
>>> calendar.isleap(df['date1_'][0].year)
True
참고로 isleap함수는 시리즈, 리스트, 문자열을 인식 못하고 무조건 숫자형만을 인식한다.
>>> calendar.isleap(df['date1_'])
Traceback (most recent call last):
~~~
TypeError: cannot perform __mod__ with this index type: DatetimeIndex
>>> calendar.isleap([2022,2020])
Traceback (most recent call last):
~~~
TypeError: unsupported operand type(s) for %: 'list' and 'int'
>>> calendar.isleap('2022')
Traceback (most recent call last):
~~~
TypeError: not all arguments converted during string formatting
>>> calendar.isleap(2022)
False
8. pandas에서 몇년 후 구하기(pd.to_timedelta)
날짜Series + pd.to_timedelta(정수Series)
파이썬에서 날짜연산은 기본적으로 datetime포맷 ± timedelta 포맷 형태를 따르고 있기 때문에 더하고자 하는 숫자컬럼을 timedelta포맷으로 바꿔줘야할 필요가 있다.
아래와 같은 data가 있다고 가정하자.
>>> df2 = pd.DataFrame({'date' : ['20160517','20220505','20181017'],
... 'interval_year' : [2,3,4]})
>>>
>>> df2['date_'] = pd.to_datetime(df2['date'])
>>> df2
date interval_year date_
0 20160517 2 2016-05-17
1 20220505 3 2022-05-05
2 20181017 4 2018-10-17
목표는 date_컬럼에서 interval_year 연수 뒤의 날짜를 구하는 것이다.
예) 첫번째 행 → 2016년 5월 17일의 2년 뒤는 2018년 5월 17일
>>> df2['date_'] + pd.to_timedelta(df2['interval_year']*365)
0 2016-05-17 00:00:00.000000730
1 2022-05-05 00:00:00.000001095
2 2018-10-17 00:00:00.000001460
dtype: datetime64[ns]
이때, 정확한 날짜로 떨어지지 않고 약간의 소수점이 붙는 이유는 1년이 정확히 365일이 아니기 때문이다.
>>> df2['date_'][0]
Timestamp('2016-05-17 00:00:00')
>>> pd.to_timedelta(df2['interval_year']*365)[0]
Timedelta('0 days 00:00:00.000000')
>>> df2['date_'][0] + pd.to_timedelta(df2['interval_year']*365)[0]
Timestamp('2016-05-17 00:00:00.000000730')
'python > 메모장' 카테고리의 다른 글
[Python] 딕셔너리 포함여부 및 계산 (0) | 2023.01.08 |
---|---|
[Python] 딕셔너리 key와 value 바꾸기 및 value로 key 찾기 (1) | 2023.01.08 |
[Python] 리스트, 딕셔너리 순위 매기기 (0) | 2023.01.08 |
[Python] pandas 문자열에서 컴마(,)제거 및 숫자변환 (0) | 2022.01.31 |
[Python] 판다스(pandas)에서 특정 컬럼/행 제거하기(pd.drop) (0) | 2022.01.31 |
댓글