datetime 라이브러리는 날짜와 시간을 처리하는 등의 다양한 기능을 제공하는 파이썬 라이브러리입니다. datetime 라이브러리에는 날짜를 처리하는 date 오브젝트, 시간을 처리하는 time 오브젝트, 날짜와 시간을 모두 처리하는 datetime 오브젝트가 포함되어 있습니다. 앞으로 3개의 오브젝트를 명확히 구분하기 위해 영문을 그대로 살려 date, time, datetime 오브젝트라고 부르겠습니다.
1.datetime 오브젝트를 사용하기 위해 datetime 라이브러리를 불러옵니다.
from datetime import datetime
2.now, today 메서드를 사용하면 다음과 같이 현재 시간을 출력할 수 있습니다.
now1 = datetime.now()
print(now1)
2021-03-31 22:06:48.500875
now2 = datetime.today()
print(now2)
2021-03-31 22:07:09.540215
3.다음은 datetime 오브젝트를 생성할 때 시간을 직접 입력하여 인자로 전달한 것입니다. 각 변수를 출력하여 확인해 보면 입력한 시간을 바탕으로 datetime 오브젝트가 생성된 것을 알 수 있습니다.
t1 = datetime.now()
t2 = datetime(1970, 1, 1)
t3 = datetime(1970, 12, 12, 13, 24, 34)
print(t1)
print(t2)
print(t3)
2021-03-31 22:09:28.106546 1970-01-01 00:00:00 1970-12-12 13:24:34
4.datetime 오브젝트를 사용하는 이유 중 하나는 시간 계산을 할 수 있다는 점입니다. 다음은 두 datetime 오브젝트의 차이를 구한 것입니다.
diff1 = t1 - t2
print(diff1)
print(type(diff1))
18717 days, 22:09:28.106546 <class 'datetime.timedelta'>
diff2 = t2 - t1
print(diff2)
print(type(diff2))
-18718 days, 1:50:31.893454 <class 'datetime.timedelta'>
import pandas as pd
import os
ebola = pd.read_csv('data/country_timeseries.csv')
2.ebola 데이터프레임을 보면 문자열로 저장된 Date 열이 있는 것을 알 수 있습니다.
print(ebola.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 122 entries, 0 to 121 Data columns (total 18 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Date 122 non-null object 1 Day 122 non-null int64 2 Cases_Guinea 93 non-null float64 3 Cases_Liberia 83 non-null float64 4 Cases_SierraLeone 87 non-null float64 5 Cases_Nigeria 38 non-null float64 6 Cases_Senegal 25 non-null float64 7 Cases_UnitedStates 18 non-null float64 8 Cases_Spain 16 non-null float64 9 Cases_Mali 12 non-null float64 10 Deaths_Guinea 92 non-null float64 11 Deaths_Liberia 81 non-null float64 12 Deaths_SierraLeone 87 non-null float64 13 Deaths_Nigeria 38 non-null float64 14 Deaths_Senegal 22 non-null float64 15 Deaths_UnitedStates 18 non-null float64 16 Deaths_Spain 16 non-null float64 17 Deaths_Mali 12 non-null float64 dtypes: float64(16), int64(1), object(1) memory usage: 17.3+ KB None
3.to_datetime 메서드를 사용하면 Date 열의 자료형을 datetime 오브젝트로 변환할 수 있습니다. 다음과 같이 to_datetime 메서드를 사용하여 Date 열의 자료형을 datetime 오브젝트로 변환한 다음 ebola 데이터프레임에 새로운 열로 추가합니다.
ebola['date_dt'] = pd.to_datetime(ebola['Date'])
print(ebola.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 122 entries, 0 to 121 Data columns (total 19 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Date 122 non-null object 1 Day 122 non-null int64 2 Cases_Guinea 93 non-null float64 3 Cases_Liberia 83 non-null float64 4 Cases_SierraLeone 87 non-null float64 5 Cases_Nigeria 38 non-null float64 6 Cases_Senegal 25 non-null float64 7 Cases_UnitedStates 18 non-null float64 8 Cases_Spain 16 non-null float64 9 Cases_Mali 12 non-null float64 10 Deaths_Guinea 92 non-null float64 11 Deaths_Liberia 81 non-null float64 12 Deaths_SierraLeone 87 non-null float64 13 Deaths_Nigeria 38 non-null float64 14 Deaths_Senegal 22 non-null float64 15 Deaths_UnitedStates 18 non-null float64 16 Deaths_Spain 16 non-null float64 17 Deaths_Mali 12 non-null float64 18 date_dt 122 non-null datetime64[ns] dtypes: datetime64[ns](1), float64(16), int64(1), object(1) memory usage: 18.2+ KB None
4.to_datetime 메서드를 좀 더 자세히 알아보자. 시간 형식 지정자(%d, %m, %y)와 기호(/, -)를 적절히 조합하여 format 인자에 전달하면 그 형식에 맞게 정리된 datetime 오브젝트를 얻을 수 있습니다. 다음 실습을 참고하여 format 인자의 사용법을 꼭 익혀두세요.
test_df1 = pd.DataFrame({'order_day' :['01/01/15', '02/01/15', '03/01/15']})
test_df1['date_dt1'] = pd.to_datetime(test_df1['order_day'], format='%d/%m/%y')
test_df1['date_dt2'] = pd.to_datetime(test_df1['order_day'], format='%m/%d/%y')
test_df1['date_dt3'] = pd.to_datetime(test_df1['order_day'], format='%y/%m/%d')
print(test_df1)
order_day date_dt1 date_dt2 date_dt3 0 01/01/15 2015-01-01 2015-01-01 2001-01-15 1 02/01/15 2015-01-02 2015-02-01 2002-01-15 2 03/01/15 2015-01-03 2015-03-01 2003-01-15
test_df2 = pd.DataFrame({'order_day':['01-01-15', '02-01-15', '03-01-15']})
test_df2['date_dt'] = pd.to_datetime(test_df2['order_day'], format='%d-%m-%y')
print(test_df2)
order_day date_dt 0 01-01-15 2015-01-01 1 02-01-15 2015-01-02 2 03-01-15 2015-01-03
앞에서는 to_datemtime 메서드를 사용하여 문자열로 저장되어 있는 Date 열을 datetime 오브젝트로 변환했습니다. 하지만 datetime 오브젝트로 변화하려는 열을 지정하여 데이터 집합을 불러오는 것이 더 간단합니다.
1.다음은 read_csv 메서드의 parse_dates 인자에 datetime 오브젝트로 변환하고자 하는 열의 이름을 전달하여 데이터 집합을 불러온 것입니다. 결과를 보면 Date 열이 문자열이 아니라 datetime 오브젝트라는 것을 확인할 수 있습니다.
ebola1 = pd.read_csv('data/country_timeseries.csv', parse_dates=['Date'])
print(ebola1.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 122 entries, 0 to 121 Data columns (total 18 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Date 122 non-null datetime64[ns] 1 Day 122 non-null int64 2 Cases_Guinea 93 non-null float64 3 Cases_Liberia 83 non-null float64 4 Cases_SierraLeone 87 non-null float64 5 Cases_Nigeria 38 non-null float64 6 Cases_Senegal 25 non-null float64 7 Cases_UnitedStates 18 non-null float64 8 Cases_Spain 16 non-null float64 9 Cases_Mali 12 non-null float64 10 Deaths_Guinea 92 non-null float64 11 Deaths_Liberia 81 non-null float64 12 Deaths_SierraLeone 87 non-null float64 13 Deaths_Nigeria 38 non-null float64 14 Deaths_Senegal 22 non-null float64 15 Deaths_UnitedStates 18 non-null float64 16 Deaths_Spain 16 non-null float64 17 Deaths_Mali 12 non-null float64 dtypes: datetime64[ns](1), float64(16), int64(1) memory usage: 17.3 KB None
date_series = pd.Series(['2018-05-16', '2018-05-17', '2018-05-18'])
d1 = pd.to_datetime(date_series)
print(d1)
0 2018-05-16 1 2018-05-17 2 2018-05-18 dtype: datetime64[ns]
2.datetime 오브젝트(d1)의 year, month, day 속성을 이용하면 년, 월, 일 정보를 바로 추출할 수 있습니다.
print(d1[0].year)
2018
print(d1[0].month)
5
print(d1[0].day)
16
ebola = pd.read_csv('data/country_timeseries.csv')
ebola['date_dt'] = pd.to_datetime(ebola['Date'])
2.다음은 dt 접근자를 사용하지 않고 인덱스가 3인 데이터의 년, 월, 일 데이터를 추출한 것입니다.
print(ebola[['Date', 'date_dt']].head)
<bound method NDFrame.head of Date date_dt 0 1/5/2015 2015-01-05 1 1/4/2015 2015-01-04 2 1/3/2015 2015-01-03 3 1/2/2015 2015-01-02 4 12/31/2014 2014-12-31 .. ... ... 117 3/27/2014 2014-03-27 118 3/26/2014 2014-03-26 119 3/25/2014 2014-03-25 120 3/24/2014 2014-03-24 121 3/22/2014 2014-03-22 [122 rows x 2 columns]>
print(ebola['date_dt'][3].year)
2015
print(ebola['date_dt'][3].month)
1
print(ebola['date_dt'][3].day)
2
3.과정 2와 같은 방법은 date_dt 열의 특정 데이터를 인덱스로 접근해야 하기 때문에 불편합니다. 다음은 dt 접근자로 date_dt 열에 한 번에 접근한 다음 year 속성을 이용하여 연도값을 추출한 것입니다. 추출한 연도값은 ebola 데이터프레임의 새로운 열(year)로 추가했습니다.
ebola['year'] = ebola['date_dt'].dt.year
print(ebola[['Date', 'date_dt', 'year']].head())
Date date_dt year 0 1/5/2015 2015-01-05 2015 1 1/4/2015 2015-01-04 2015 2 1/3/2015 2015-01-03 2015 3 1/2/2015 2015-01-02 2015 4 12/31/2014 2014-12-31 2014
4.다음은 과정 3을 응용하여 월, 일 데이터를 한 번에 추출해서 새로운 열(month, day)로 추가한 것입니다.
ebola['month'], ebola['day'] = (ebola['date_dt'].dt.month, ebola['date_dt'].dt.day)
print(ebola[['Date', 'date_dt', 'year', 'month', 'day']].head())
Date date_dt year month day 0 1/5/2015 2015-01-05 2015 1 5 1 1/4/2015 2015-01-04 2015 1 4 2 1/3/2015 2015-01-03 2015 1 3 3 1/2/2015 2015-01-02 2015 1 2 4 12/31/2014 2014-12-31 2014 12 31
5.ebola 데이터프레임에 새로 추가한 date_dt, year, month, day 열의 자료형을 출력한 것입니다. date_dt 열은 datetime 오브젝트이고 나머지는 정수형이라는 것을 알 수 있습니다.
print(ebola.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 122 entries, 0 to 121 Data columns (total 22 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Date 122 non-null object 1 Day 122 non-null int64 2 Cases_Guinea 93 non-null float64 3 Cases_Liberia 83 non-null float64 4 Cases_SierraLeone 87 non-null float64 5 Cases_Nigeria 38 non-null float64 6 Cases_Senegal 25 non-null float64 7 Cases_UnitedStates 18 non-null float64 8 Cases_Spain 16 non-null float64 9 Cases_Mali 12 non-null float64 10 Deaths_Guinea 92 non-null float64 11 Deaths_Liberia 81 non-null float64 12 Deaths_SierraLeone 87 non-null float64 13 Deaths_Nigeria 38 non-null float64 14 Deaths_Senegal 22 non-null float64 15 Deaths_UnitedStates 18 non-null float64 16 Deaths_Spain 16 non-null float64 17 Deaths_Mali 12 non-null float64 18 date_dt 122 non-null datetime64[ns] 19 year 122 non-null int64 20 month 122 non-null int64 21 day 122 non-null int64 dtypes: datetime64[ns](1), float64(16), int64(4), object(1) memory usage: 21.1+ KB None
print(ebola.iloc[-5:, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 117 3/27/2014 5 103.0 8.0 6.0 118 3/26/2014 4 86.0 NaN NaN 119 3/25/2014 3 86.0 NaN NaN 120 3/24/2014 2 86.0 NaN NaN 121 3/22/2014 0 49.0 NaN NaN
2.121행에서 볼 수 있듯이 에볼라가 발생하기 시작한 날은 2014년 03월 22일입니다. 다음은 min 메서드를 사용하여 에볼라의 최초 발병일을 찾은 것입니다.
print(ebola['date_dt'].min())
print(type(ebola['date_dt'].min()))
2014-03-22 00:00:00 <class 'pandas._libs.tslibs.timestamps.Timestamp'>
3.에볼라의 최초 발병일을 알아냈으니 Date 열에서 에볼라의 최초 발병일을 빼면 에볼라의 진행 정도를 알 수 있습니다.
ebola['outbreak_d'] = ebola['date_dt'] - ebola['date_dt'].min()
print(ebola[['Date', 'Day', 'outbreak_d']].head())
Date Day outbreak_d 0 1/5/2015 289 289 days 1 1/4/2015 288 288 days 2 1/3/2015 287 287 days 3 1/2/2015 286 286 days 4 12/31/2014 284 284 days
이번에는 파산한 은행 데이터를 불러와 분기별로 파산한 은행이 얼마나 되는지 계산해 보겠습니다. 이번에는 그래프로도 시각화해 보겠습니다.
1.다음은 파산한 은행 데이터 집합을 불러온 것입니다. banks 데이터프레임의 앞부분을 살펴보면 Closing Date, Updated Date 열의 데이터 자료형이 시계열 데이터라는 것을 알 수 있습니다.
banks = pd.read_csv('data/banklist.csv')
print(banks.head())
Bank Name City ST CERT \ 0 Washington Federal Bank for Savings Chicago IL 30570 1 The Farmers and Merchants State Bank of Argonia Argonia KS 17719 2 Fayette County Bank Saint Elmo IL 1802 3 Guaranty Bank, (d/b/a BestBank in Georgia & Mi... Milwaukee WI 30003 4 First NBC Bank New Orleans LA 58302 Acquiring Institution Closing Date Updated Date 0 Royal Savings Bank 15-Dec-17 20-Dec-17 1 Conway Bank 13-Oct-17 20-Oct-17 2 United Fidelity Bank, fsb 26-May-17 26-Jul-17 3 First-Citizens Bank & Trust Company 5-May-17 26-Jul-17 4 Whitney Bank 28-Apr-17 5-Dec-17
2.Closing Date, Updated Date 열의 데이터 자료형은 문자열입니다. 다음은 read_csv 메서드의 parse_dates 속성을 이용하여 문자열로 저장된 두 열을 datetime 오브젝트로 변환하여 불러온 것입니다.
banks_no_dates = pd.read_csv('data/banklist.csv')
print(banks_no_dates.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 555 entries, 0 to 554 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Bank Name 555 non-null object 1 City 555 non-null object 2 ST 555 non-null object 3 CERT 555 non-null int64 4 Acquiring Institution 555 non-null object 5 Closing Date 555 non-null object 6 Updated Date 555 non-null object dtypes: int64(1), object(6) memory usage: 30.5+ KB None
banks = pd.read_csv('data/banklist.csv', parse_dates=[5, 6])
print(banks.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 555 entries, 0 to 554 Data columns (total 7 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Bank Name 555 non-null object 1 City 555 non-null object 2 ST 555 non-null object 3 CERT 555 non-null int64 4 Acquiring Institution 555 non-null object 5 Closing Date 555 non-null datetime64[ns] 6 Updated Date 555 non-null datetime64[ns] dtypes: datetime64[ns](2), int64(1), object(4) memory usage: 30.5+ KB None
3.dt 접근자와 quarter 속성을 이용하면 은행이 파산한 분기를 알 수 있습니다. 다음은 dt 접근자와 year.quarter 속성을 이용하여 은행이 파산한 연도,분기를 새로운 열로 추가한것입니다.
banks['closing_quarter'], banks['closing_year'] = (banks['Closing Date'].dt.quarter, banks['Closing Date'].dt.year)
print(banks.head())
Bank Name City ST CERT \ 0 Washington Federal Bank for Savings Chicago IL 30570 1 The Farmers and Merchants State Bank of Argonia Argonia KS 17719 2 Fayette County Bank Saint Elmo IL 1802 3 Guaranty Bank, (d/b/a BestBank in Georgia & Mi... Milwaukee WI 30003 4 First NBC Bank New Orleans LA 58302 Acquiring Institution Closing Date Updated Date \ 0 Royal Savings Bank 2017-12-15 2017-12-20 1 Conway Bank 2017-10-13 2017-10-20 2 United Fidelity Bank, fsb 2017-05-26 2017-07-26 3 First-Citizens Bank & Trust Company 2017-05-05 2017-07-26 4 Whitney Bank 2017-04-28 2017-12-05 closing_quarter closing_year 0 4 2017 1 4 2017 2 2 2017 3 2 2017 4 2 2017
4.이제 연도별로 파산한 은행이 얼마나 되는지 알아보자. grouby 메서드를 사용하면 연도별로 파산한 은행의 개수를 구할 수 있다.
closing_year = banks.groupby(['closing_year']).size()
print(closing_year)
closing_year 2000 2 2001 4 2002 11 2003 3 2004 4 2007 3 2008 25 2009 140 2010 157 2011 92 2012 51 2013 24 2014 18 2015 8 2016 5 2017 8 dtype: int64
5.각 연도별, 분기별로 파산한 은행의 개수도 알아보겠습니다. 다음은 banks 데이터프레임을 연도별로 그룹화한 다음 다시 분기별로 그룹화하여 출력한 것입니다.
closing_year_q = banks.groupby(['closing_year', 'closing_quarter']).size()
print(closing_year_q)
closing_year closing_quarter 2000 4 2 2001 1 1 2 1 3 2 2002 1 6 2 2 3 1 4 2 2003 1 1 2 1 4 1 2004 1 3 2 1 2007 1 1 3 1 4 1 2008 1 2 2 2 3 9 4 12 2009 1 21 2 24 3 50 4 45 2010 1 41 2 45 3 41 4 30 2011 1 26 2 22 3 26 4 18 2012 1 16 2 15 3 12 4 8 2013 1 4 2 12 3 6 4 2 2014 1 5 2 7 3 2 4 4 2015 1 4 2 1 3 1 4 2 2016 1 1 2 2 3 2 2017 1 3 2 3 4 2 dtype: int64
6.다음은 과정 5에서 얻은 값으로 그래프를 그린 것입니다.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax = closing_year.plot()
plt.show()
fig, ax = plt.subplots()
ax = closing_year_q.plot()
plt.show()
vUQwfuBy3tYUhsvyLnft
이번에는 pandas-datareader 라이브러리를 이용하여 주식 데이터를 불러오겠습니다. 다음을 아나콘다 프롬프트에 입력하여 pandas-datereader 라이브러리를 설치하세요.
pip install pandas_datareader
Collecting pandas_datareader Downloading https://files.pythonhosted.org/packages/22/f1/a02eea03628865c3d4c29849cda8c8a73f43358048aee47d79dfbfa6c6c3/pandas_datareader-0.9.0-py3-none-any.whl (107kB) 100% |████████████████████████████████| 112kB 354kB/s ta 0:00:01 Collecting requests>=2.19.0 (from pandas_datareader) Downloading https://files.pythonhosted.org/packages/29/c1/24814557f1d22c56d50280771a17307e6bf87b70727d975fd6b2ce6b014a/requests-2.25.1-py2.py3-none-any.whl (61kB) 100% |████████████████████████████████| 61kB 1.0MB/s ta 0:00:011 Collecting lxml (from pandas_datareader) Downloading https://files.pythonhosted.org/packages/1f/1d/a4485412268b38043a6c0f873245b5d9315c6615bcf44776759a2605dca5/lxml-4.6.3-cp36-cp36m-manylinux1_x86_64.whl (5.5MB) 100% |████████████████████████████████| 5.5MB 182kB/s ta 0:00:011 Collecting pandas>=0.23 (from pandas_datareader) Using cached https://files.pythonhosted.org/packages/c3/e2/00cacecafbab071c787019f00ad84ca3185952f6bb9bca9550ed83870d4d/pandas-1.1.5-cp36-cp36m-manylinux1_x86_64.whl Collecting certifi>=2017.4.17 (from requests>=2.19.0->pandas_datareader) Downloading https://files.pythonhosted.org/packages/5e/a0/5f06e1e1d463903cf0c0eebeb751791119ed7a4b3737fdc9a77f1cdfb51f/certifi-2020.12.5-py2.py3-none-any.whl (147kB) 100% |████████████████████████████████| 153kB 336kB/s ta 0:00:01 Collecting chardet<5,>=3.0.2 (from requests>=2.19.0->pandas_datareader) Downloading https://files.pythonhosted.org/packages/19/c7/fa589626997dd07bd87d9269342ccb74b1720384a4d739a1872bd84fbe68/chardet-4.0.0-py2.py3-none-any.whl (178kB) 100% |████████████████████████████████| 184kB 373kB/s ta 0:00:01 Collecting urllib3<1.27,>=1.21.1 (from requests>=2.19.0->pandas_datareader) Downloading https://files.pythonhosted.org/packages/09/c6/d3e3abe5b4f4f16cf0dfc9240ab7ce10c2baa0e268989a4e3ec19e90c84e/urllib3-1.26.4-py2.py3-none-any.whl (153kB) 100% |████████████████████████████████| 153kB 264kB/s ta 0:00:01 Collecting idna<3,>=2.5 (from requests>=2.19.0->pandas_datareader) Downloading https://files.pythonhosted.org/packages/a2/38/928ddce2273eaa564f6f50de919327bf3a00f091b5baba8dfa9460f3a8a8/idna-2.10-py2.py3-none-any.whl (58kB) 100% |████████████████████████████████| 61kB 630kB/s ta 0:00:011 Collecting numpy>=1.15.4 (from pandas>=0.23->pandas_datareader) Using cached https://files.pythonhosted.org/packages/45/b2/6c7545bb7a38754d63048c7696804a0d947328125d81bf12beaa692c3ae3/numpy-1.19.5-cp36-cp36m-manylinux1_x86_64.whl Collecting python-dateutil>=2.7.3 (from pandas>=0.23->pandas_datareader) Using cached https://files.pythonhosted.org/packages/d4/70/d60450c3dd48ef87586924207ae8907090de0b306af2bce5d134d78615cb/python_dateutil-2.8.1-py2.py3-none-any.whl Collecting pytz>=2017.2 (from pandas>=0.23->pandas_datareader) Using cached https://files.pythonhosted.org/packages/70/94/784178ca5dd892a98f113cdd923372024dc04b8d40abe77ca76b5fb90ca6/pytz-2021.1-py2.py3-none-any.whl Collecting six>=1.5 (from python-dateutil>=2.7.3->pandas>=0.23->pandas_datareader) Using cached https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl Installing collected packages: certifi, chardet, urllib3, idna, requests, lxml, numpy, six, python-dateutil, pytz, pandas, pandas-datareader Successfully installed certifi-2020.12.5 chardet-4.0.0 idna-2.10 lxml-4.6.3 numpy-1.19.5 pandas-1.1.5 pandas-datareader-0.9.0 python-dateutil-2.8.1 pytz-2021.1 requests-2.25.1 six-1.15.0 urllib3-1.26.4 Note: you may need to restart the kernel to use updated packages.
1.다음은 get_data_quanal 메서드에 TSLA라는 문자열을 전달하여 테슬라의 주식데이터를 내려받은 다음 to_csv 메서드를 사용하여 data 폴더 안에 'tesla_stock_quandl.csv'라는 이름으로 저장한 것입니다.
pd.core.common.is_list_like = pd.api.types.is_list_like
import pandas_datareader as pdr
tesla = pdr.get_data_quandl('TSLA', api_key = 'vUQwfuBy3tYUhsvyLnft')
tesla.to_csv =('data/tesla_stock_quandl.csv')
2.tesla 데이터프레임의 Date 열은 문자열로 저장되어 있습니다. 즉, datetime 오브젝트로 자료형을 변환해야 시간 계산을 할 수 있습니다.
print(tesla.head())
Open High Low Close Volume ExDividend \ Date 2018-03-27 304.00 304.27 277.18 279.18 13696168.0 0.0 2018-03-26 307.34 307.59 291.36 304.18 8324639.0 0.0 2018-03-23 311.25 311.61 300.45 301.54 6600538.0 0.0 2018-03-22 313.89 318.82 308.18 309.10 4914307.0 0.0 2018-03-21 310.25 322.44 310.19 316.53 5927881.0 0.0 SplitRatio AdjOpen AdjHigh AdjLow AdjClose AdjVolume Date 2018-03-27 1.0 304.00 304.27 277.18 279.18 13696168.0 2018-03-26 1.0 307.34 307.59 291.36 304.18 8324639.0 2018-03-23 1.0 311.25 311.61 300.45 301.54 6600538.0 2018-03-22 1.0 313.89 318.82 308.18 309.10 4914307.0 2018-03-21 1.0 310.25 322.44 310.19 316.53 5927881.0
3.Date 열을 Datetime 형으로 변환하려면 read_csv 메서드로 데이터 집합(tesla_stock_quandl.csv)을 불러올 때 parse_dates 인자에 Date 열을 전달하면 됩니다.
tesla = pd.read_csv('data/tesla_stock_quandl.csv', parse_dates=[0])
print(tesla.info())
<class 'pandas.core.frame.DataFrame'> RangeIndex: 1949 entries, 0 to 1948 Data columns (total 13 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Date 1949 non-null datetime64[ns] 1 Open 1949 non-null float64 2 High 1949 non-null float64 3 Low 1949 non-null float64 4 Close 1949 non-null float64 5 Volume 1949 non-null float64 6 ExDividend 1949 non-null float64 7 SplitRatio 1949 non-null float64 8 AdjOpen 1949 non-null float64 9 AdjHigh 1949 non-null float64 10 AdjLow 1949 non-null float64 11 AdjClose 1949 non-null float64 12 AdjVolume 1949 non-null float64 dtypes: datetime64[ns](1), float64(12) memory usage: 198.1 KB None
4.Date 열의 자료형이 datetime 오브젝트로 변환되었습니다. 이제 dt 접근자를 사용할 수 있습니다. 다음은 불린 추출로 2010년 6월의 데이터만 추출한 것입니다.
print(tesla.loc[(tesla.Date.dt.year == 2010) & (tesla.Date.dt.month == 6)])
Date Open High Low Close Volume ExDividend \ 1947 2010-06-30 25.79 30.4192 23.30 23.83 17187100.0 0.0 1948 2010-06-29 19.00 25.0000 17.54 23.89 18766300.0 0.0 SplitRatio AdjOpen AdjHigh AdjLow AdjClose AdjVolume 1947 1.0 25.79 30.4192 23.30 23.83 17187100.0 1948 1.0 19.00 25.0000 17.54 23.89 18766300.0
1.계속해서 테슬라 주식 데이터를 사용하여 실습을 진행하겠습니다. 다음은 Date 열을 tesla 데이터프레임의 인덱스로 지정한 것입니다.
tesla.index = tesla['Date']
print(tesla.index)
DatetimeIndex(['2018-03-27', '2018-03-26', '2018-03-23', '2018-03-22', '2018-03-21', '2018-03-20', '2018-03-19', '2018-03-16', '2018-03-15', '2018-03-14', ... '2010-07-13', '2010-07-12', '2010-07-09', '2010-07-08', '2010-07-07', '2010-07-06', '2010-07-02', '2010-07-01', '2010-06-30', '2010-06-29'], dtype='datetime64[ns]', name='Date', length=1949, freq=None)
2.datetime 오브젝트를 인덱스로 지정하면 다음과 같은 방법으로 원하는 시간의 데이터를 바로 추출할 수 있습니다. 다음은 2015년의 데이터를 추출한 것입니다.
print(tesla['2015'].iloc[:5,:5])
Date Open High Low Close Date 2015-12-31 2015-12-31 238.51 243.450 238.3700 240.01 2015-12-30 2015-12-30 236.60 243.634 235.6707 238.09 2015-12-29 2015-12-29 230.06 237.720 229.5470 237.19 2015-12-28 2015-12-28 231.49 231.980 225.5400 228.95 2015-12-24 2015-12-24 230.56 231.880 228.2800 230.57
3.다음은 2010년 6월의 데이터를 추출한 것입니다.
print(tesla['2010-06'].iloc[:, :5])
Date Open High Low Close Date 2010-06-30 2010-06-30 25.79 30.4192 23.30 23.83 2010-06-29 2010-06-29 19.00 25.0000 17.54 23.89
1.Date 열에서 Date 열의 최솟값(2010-06-29)을 빼면 데이터를 수집한 이후에 시간이 얼마나 흘렀는지 알 수 있습니다. 다음은 Date 열에서 Date 열의 최솟값을 뺀 다음 ref_date 열로 추가한 것입니다.
tesla['ref_date'] = tesla['Date'] - tesla['Date'].min()
print(tesla.head())
Date Open High Low Close Volume ExDividend \ Date 2018-03-27 2018-03-27 304.00 304.27 277.18 279.18 13696168.0 0.0 2018-03-26 2018-03-26 307.34 307.59 291.36 304.18 8324639.0 0.0 2018-03-23 2018-03-23 311.25 311.61 300.45 301.54 6600538.0 0.0 2018-03-22 2018-03-22 313.89 318.82 308.18 309.10 4914307.0 0.0 2018-03-21 2018-03-21 310.25 322.44 310.19 316.53 5927881.0 0.0 SplitRatio AdjOpen AdjHigh AdjLow AdjClose AdjVolume \ Date 2018-03-27 1.0 304.00 304.27 277.18 279.18 13696168.0 2018-03-26 1.0 307.34 307.59 291.36 304.18 8324639.0 2018-03-23 1.0 311.25 311.61 300.45 301.54 6600538.0 2018-03-22 1.0 313.89 318.82 308.18 309.10 4914307.0 2018-03-21 1.0 310.25 322.44 310.19 316.53 5927881.0 ref_date Date 2018-03-27 2828 days 2018-03-26 2827 days 2018-03-23 2824 days 2018-03-22 2823 days 2018-03-21 2822 days
2.다음과 같이 ref_date 열을 인덱스로 지정했습니다. 이제 시간 간격(ref_date)을 이용하여 데이터를 추출할 수 있습니다.
tesla.index = tesla['ref_date']
print(tesla.iloc[:5,:5])
Date Open High Low Close ref_date 2828 days 2018-03-27 304.00 304.27 277.18 279.18 2827 days 2018-03-26 307.34 307.59 291.36 304.18 2824 days 2018-03-23 311.25 311.61 300.45 301.54 2823 days 2018-03-22 313.89 318.82 308.18 309.10 2822 days 2018-03-21 310.25 322.44 310.19 316.53
3.다음은 데이터를 수집한 이후 최초 5일의 데이터를 추출한 것입니다.
print(tesla['5 days':].iloc[:5, :5])
Date Open High Low Close ref_date 3 days 2010-07-02 23.00 23.1000 18.71 19.20 2 days 2010-07-01 25.00 25.9200 20.27 21.96 1 days 2010-06-30 25.79 30.4192 23.30 23.83 0 days 2010-06-29 19.00 25.0000 17.54 23.89
1.테슬라 주식 데이터는 특징 일에 누락된 데이터가 없습니다. 그래서 이번에는 에볼라 데이터 집합을 사용하겠습니다. 가장 앞쪽의 데이터를 살펴보면 2015년 01월 01일의 데이터가 누락된 것을 알 수 있습니다.
ebola = pd.read_csv('data/country_timeseries.csv', parse_dates=[0])
print(ebola.iloc[:5, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 0 2015-01-05 289 2776.0 NaN 10030.0 1 2015-01-04 288 2775.0 NaN 9780.0 2 2015-01-03 287 2769.0 8166.0 9722.0 3 2015-01-02 286 NaN 8157.0 NaN 4 2014-12-31 284 2730.0 8115.0 9633.0
2.뒤쪽의 데이터도 마찬가지입니다. 2014년 03월 23일의 데이터가 누락되었습니다.
print(ebola.iloc[-5:, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 117 2014-03-27 5 103.0 8.0 6.0 118 2014-03-26 4 86.0 NaN NaN 119 2014-03-25 3 86.0 NaN NaN 120 2014-03-24 2 86.0 NaN NaN 121 2014-03-22 0 49.0 NaN NaN
3.다음은 date_range 메서드를 사용하여 2014년 12월 31일부터 2015년 01월 05일 사이의 시간 인덱스(DatetimeIndex)를 생성한 것입니다.
head_range = pd.date_range(start='2014-12-31', end='2015-01-05')
print(head_range)
DatetimeIndex(['2014-12-31', '2015-01-01', '2015-01-02', '2015-01-03', '2015-01-04', '2015-01-05'], dtype='datetime64[ns]', freq='D')
4.다음은 원본 데이터를 손상시키는 것을 방지하기 위해 ebola 데이터프레임의 앞쪽 5개의 데이터를 추출하여 새로운 데이터프레임을 만든 것입니다. 이때 Date 열을 인덱스로 먼저 지정하지 않으면 오류가 발생합니다. 반드시 Date 열을 인덱스로 지정한 다음 과정 3에서 생성한 시간 범위를 인덱스로 지정해야 합니다.
ebola_5 = ebola.head()
ebola_5.index = ebola_5['Date']
ebola_5.reindex(head_range)
print(ebola_5.iloc[:5, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone Date 2015-01-05 2015-01-05 289 2776.0 NaN 10030.0 2015-01-04 2015-01-04 288 2775.0 NaN 9780.0 2015-01-03 2015-01-03 287 2769.0 8166.0 9722.0 2015-01-02 2015-01-02 286 NaN 8157.0 NaN 2014-12-31 2014-12-31 284 2730.0 8115.0 9633.0
import matplotlib.pyplot as plt
ebola.index = ebola['Date']
fig, ax = plt.subplots()
ax = ebola.iloc[0:, 1:].plot(ax=ax)
ax.legend(fontsize=7, loc=2, borderaxespad=0.)
plt.show()
2.그런데 과정 1의 그래프는 각 나라의 에볼라 발병일이 달라 그래프가 그려지기 시작한 지점도 다릅니다. 달리기 속도를 비교하려면 같은 출발선에서 출발하여 시간을 측정해야 겠죠? 에볼라의 확산 속도도 같은 방법으로 측정해야 합니다. 즉, 각 나라의 발병일을 가장 처음 에볼라가 발병한 Guinea와 동일한 위치로 옮겨야 나라별 에볼라의 확산 속도를 제대로 비교할 수 있습니다.
ebola_sub = ebola[['Day', 'Cases_Guinea', 'Cases_Liberia']]
print(ebola_sub.tail(10))
Day Cases_Guinea Cases_Liberia Date 2014-04-04 13 143.0 18.0 2014-04-01 10 127.0 8.0 2014-03-31 9 122.0 8.0 2014-03-29 7 112.0 7.0 2014-03-28 6 112.0 3.0 2014-03-27 5 103.0 8.0 2014-03-26 4 86.0 NaN 2014-03-25 3 86.0 NaN 2014-03-24 2 86.0 NaN 2014-03-22 0 49.0 NaN
3.그래프를 그리기 위한 데이터프레임 준비하기
ebola = pd.read_csv('data/country_timeseries.csv', parse_dates=['Date'])
print(ebola.head().iloc[:, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 0 2015-01-05 289 2776.0 NaN 10030.0 1 2015-01-04 288 2775.0 NaN 9780.0 2 2015-01-03 287 2769.0 8166.0 9722.0 3 2015-01-02 286 NaN 8157.0 NaN 4 2014-12-31 284 2730.0 8115.0 9633.0
print(ebola.tail().iloc[:, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone 117 2014-03-27 5 103.0 8.0 6.0 118 2014-03-26 4 86.0 NaN NaN 119 2014-03-25 3 86.0 NaN NaN 120 2014-03-24 2 86.0 NaN NaN 121 2014-03-22 0 49.0 NaN NaN
4.다음은 Date 열을 인덱스로 지정한 다음 ebola 데이터프레임의 Date 열의 최댓값과 최솟값으로 시간 범위를 생성하여 new_idx에 저장한 것입니다. 이렇게 하면 날짜가 아예 없었던 데이터의 인덱스를 생성할 수 있습니다.
ebola.index = ebola['Date']
new_idx = pd.date_range(ebola.index.min(), ebola.index.max())
5.그런데 new_idx를 살펴보면 ebola 데이터 집합에 있는 시간 순서와 반대로 생성되어 있습니다. 다음은 시간 순서를 맞추기 위해 reversed 메서드를 사용하여 인덱스를 반대로 뒤집은 것입니다.
print(new_idx)
DatetimeIndex(['2014-03-22', '2014-03-23', '2014-03-24', '2014-03-25', '2014-03-26', '2014-03-27', '2014-03-28', '2014-03-29', '2014-03-30', '2014-03-31', ... '2014-12-27', '2014-12-28', '2014-12-29', '2014-12-30', '2014-12-31', '2015-01-01', '2015-01-02', '2015-01-03', '2015-01-04', '2015-01-05'], dtype='datetime64[ns]', length=290, freq='D')
new_idx = reversed(new_idx)
6.다음은 reindex 메서드를 사용하여 새로 생성한 인덱스(new_idx)를 새로운 인덱스로 지정한 것입니다. 그러면 2015년 01월 01일 데이터와 같은 ebola 데이터프레임에 아예 없었던 날짜가 추가됩니다. 이제 그래프를 그리기 위한 데이터프레임이 준비되었습니다.
ebola = ebola.reindex(new_idx)
print(ebola.head().iloc[:, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone Date 2015-01-05 2015-01-05 289.0 2776.0 NaN 10030.0 2015-01-04 2015-01-04 288.0 2775.0 NaN 9780.0 2015-01-03 2015-01-03 287.0 2769.0 8166.0 9722.0 2015-01-02 2015-01-02 286.0 NaN 8157.0 NaN 2015-01-01 NaT NaN NaN NaN NaN
print(ebola.tail().iloc[:, :5])
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone Date 2014-03-26 2014-03-26 4.0 86.0 NaN NaN 2014-03-25 2014-03-25 3.0 86.0 NaN NaN 2014-03-24 2014-03-24 2.0 86.0 NaN NaN 2014-03-23 NaT NaN NaN NaN NaN 2014-03-22 2014-03-22 0.0 49.0 NaN NaN
다음은 last_valid_index, first_valid_index 메서드를 사용하여 각 나라의 에볼라 발병일을 구한 것입니다. 각각의 메서드는 유효한 값이 있는 첫 번째와 마지막 인덱스를 반환합니다. 다음을 입력하고 결과를 확인해 보자.
last_valid = ebola.apply(pd.Series.last_valid_index)
print(last_valid)
Date 2014-03-22 Day 2014-03-22 Cases_Guinea 2014-03-22 Cases_Liberia 2014-03-27 Cases_SierraLeone 2014-03-27 Cases_Nigeria 2014-07-23 Cases_Senegal 2014-08-31 Cases_UnitedStates 2014-10-01 Cases_Spain 2014-10-08 Cases_Mali 2014-10-22 Deaths_Guinea 2014-03-22 Deaths_Liberia 2014-03-27 Deaths_SierraLeone 2014-03-27 Deaths_Nigeria 2014-07-23 Deaths_Senegal 2014-09-07 Deaths_UnitedStates 2014-10-01 Deaths_Spain 2014-10-08 Deaths_Mali 2014-10-22 dtype: datetime64[ns]
first_valid = ebola.apply(pd.Series.first_valid_index)
print(first_valid)
Date 2015-01-05 Day 2015-01-05 Cases_Guinea 2015-01-05 Cases_Liberia 2015-01-03 Cases_SierraLeone 2015-01-05 Cases_Nigeria 2014-12-07 Cases_Senegal 2014-12-07 Cases_UnitedStates 2014-12-07 Cases_Spain 2014-12-07 Cases_Mali 2014-12-07 Deaths_Guinea 2015-01-05 Deaths_Liberia 2015-01-03 Deaths_SierraLeone 2015-01-05 Deaths_Nigeria 2014-12-07 Deaths_Senegal 2014-12-07 Deaths_UnitedStates 2014-12-07 Deaths_Spain 2014-12-07 Deaths_Mali 2014-12-07 dtype: datetime64[ns]
8.각 나라의 에볼라 발병일을 동일한 출발선으로 옮기려면 에볼라가 가장 처음 발병한 날(earliest_date)에서 각 나라의 에볼라 발병일을 뺀 만큼(shift_values)만 옮기면 됩니다.
earliest_date = ebola.index.min()
print(earliest_date)
2014-03-22 00:00:00
shift_values = last_valid - earliest_date
print(shift_values)
Date 0 days Day 0 days Cases_Guinea 0 days Cases_Liberia 5 days Cases_SierraLeone 5 days Cases_Nigeria 123 days Cases_Senegal 162 days Cases_UnitedStates 193 days Cases_Spain 200 days Cases_Mali 214 days Deaths_Guinea 0 days Deaths_Liberia 5 days Deaths_SierraLeone 5 days Deaths_Nigeria 123 days Deaths_Senegal 169 days Deaths_UnitedStates 193 days Deaths_Spain 200 days Deaths_Mali 214 days dtype: timedelta64[ns]
9.이제 각 나라의 에볼라 발병일을 옮기면 됩니다. 다음은 shift 메서드를 사용하여 모든 열의 값을 shift_values 값만큼 옮긴 것입니다. shift 메서드는 인잣값만큼 데이터를 밀어내는 메서드입니다.
ebola_dict = {}
for idx, col in enumerate(ebola):
d = shift_values[idx].days
shifted = ebola[col].shift(d)
ebola_dict[col] = shifted
10.ebola_dict에는 시간을 다시 설정한 데이터가 딕셔너리 형태로 저장되어 있습니다. 다음은 DataFrame 메서드를 사용하여 ebola_dict의 값을 데이터프레임으로 변환한 것입니다.
ebola_shift = pd.DataFrame(ebola_dict)
11.이제 에볼라의 최초 발병일(2014-03-22)을 기준으로 모든 열의 데이터가 옮겨졌습니다.
print(ebola_shift.tail())
Date Day Cases_Guinea Cases_Liberia Cases_SierraLeone \ Date 2014-03-26 2014-03-26 4.0 86.0 8.0 2.0 2014-03-25 2014-03-25 3.0 86.0 NaN NaN 2014-03-24 2014-03-24 2.0 86.0 7.0 NaN 2014-03-23 NaT NaN NaN 3.0 2.0 2014-03-22 2014-03-22 0.0 49.0 8.0 6.0 Cases_Nigeria Cases_Senegal Cases_UnitedStates Cases_Spain \ Date 2014-03-26 1.0 NaN 1.0 1.0 2014-03-25 NaN NaN NaN NaN 2014-03-24 NaN NaN NaN NaN 2014-03-23 NaN NaN NaN NaN 2014-03-22 0.0 1.0 1.0 1.0 Cases_Mali Deaths_Guinea Deaths_Liberia Deaths_SierraLeone \ Date 2014-03-26 NaN 62.0 4.0 2.0 2014-03-25 NaN 60.0 NaN NaN 2014-03-24 NaN 59.0 2.0 NaN 2014-03-23 NaN NaN 3.0 2.0 2014-03-22 1.0 29.0 6.0 5.0 Deaths_Nigeria Deaths_Senegal Deaths_UnitedStates Deaths_Spain \ Date 2014-03-26 1.0 NaN 0.0 1.0 2014-03-25 NaN NaN NaN NaN 2014-03-24 NaN NaN NaN NaN 2014-03-23 NaN NaN NaN NaN 2014-03-22 0.0 0.0 0.0 1.0 Deaths_Mali Date 2014-03-26 NaN 2014-03-25 NaN 2014-03-24 NaN 2014-03-23 NaN 2014-03-22 1.0
12.마지막으로 인덱스를 Day 열로 지정하고 그래프에 필요 없는 Date, Day 열은 삭제하면 그래프를 그리기 위한 데이터 프레임이 완성됩니다.
ebola_shift.index = ebola_shift['Day']
ebola_shift = ebola_shift.drop(['Date', 'Day'], axis=1)
print(ebola_shift.tail())
Cases_Guinea Cases_Liberia Cases_SierraLeone Cases_Nigeria \ Day 4.0 86.0 8.0 2.0 1.0 3.0 86.0 NaN NaN NaN 2.0 86.0 7.0 NaN NaN NaN NaN 3.0 2.0 NaN 0.0 49.0 8.0 6.0 0.0 Cases_Senegal Cases_UnitedStates Cases_Spain Cases_Mali \ Day 4.0 NaN 1.0 1.0 NaN 3.0 NaN NaN NaN NaN 2.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 0.0 1.0 1.0 1.0 1.0 Deaths_Guinea Deaths_Liberia Deaths_SierraLeone Deaths_Nigeria \ Day 4.0 62.0 4.0 2.0 1.0 3.0 60.0 NaN NaN NaN 2.0 59.0 2.0 NaN NaN NaN NaN 3.0 2.0 NaN 0.0 29.0 6.0 5.0 0.0 Deaths_Senegal Deaths_UnitedStates Deaths_Spain Deaths_Mali Day 4.0 NaN 0.0 1.0 NaN 3.0 NaN NaN NaN NaN 2.0 NaN NaN NaN NaN NaN NaN NaN NaN NaN 0.0 0.0 0.0 1.0 1.0
13.다음은 지금까지 만든 데이터프레임으로 다시 그린 그래프입니다.
fig, ax = plt.subplots()
ax = ebola_shift.iloc[:, :].plot(ax=ax)
ax.legend(fontsize=7, loc=2, borderaxespad=0.)
plt.show()
시계열데이터와 깊은 연관성이 있는 에볼라 데이터 및 주식 데이터를 주로 다루었습니다. 우리 주변의 상당수의 데이터는 시간과 깊은 연관성이 있는 경우가 많습니다. 시계열 데이터를 능숙하게 다루는 것은 데이터 분석가의 기본 소양이므로 익혀두기 바랍니다.
판다스, 그룹연산 (0) | 2021.03.31 |
---|---|
판다스, apply 메서드 활용 (0) | 2021.03.31 |
판다스, 문자열 처리하기 (0) | 2021.03.30 |
판다스, 깔끔한 데이터 (1) | 2021.03.28 |
판다스, 누락값 처리하기 (0) | 2021.03.27 |